网络子系统1

news2024/9/28 17:26:23

网络子系统

1 ISO/OSI和TCP/IP参考模型

国际标准化组织)设计了一种参考模型,定义了组成网络的各个层。该模型由7层组成,称为OSI(Open Systems Interconnection,开放系统互连)模型,如图所示。其中将ISO/OSI模型的一些层合并为新层。该模型只有4层,因此其结构更为简单。这种模型称为TCP/IP**参考模型**,IP表示Internet Protocol(网际协议),而TCP表示Transmission Control Protocol(传输控制协议)。


2 通过套接字通信

外部设备在Linux(和UNIX)中不过是普通的文件,通过正常的读写操作即可访问,对网卡而言,情况有点复杂,网卡的运作方式与普通的块设备和字符设备完全不同,使得经典的UNIX箴言“万物皆文件”不再完全适用。当然,内核必须提供一个尽可能通用的接口,供程序访问网络功能,套接字现在用于定义和建立网络连接,以便可以用操作inode的普通方法(特别是读写操作)来访问网络。从程序员的角度来看,创建套接字的最终结果是一个文件描述符,它不仅提供所有的标准函数,还包括几个增强的函数。用于实际数据交换的接口对所有的协议和地址族都是同样的。

2.1 创建套接字
 

在创建套接字时,必须指定所需要的地址和协议类型的组合。不仅要区分地址和协议族,还要区分基于流的通信和数据报的通信。

套接字是使用socket 库函数生成的,除了地址族和通信类型(流或数据报)之外,可使用第三个参数来选择协议。因为前两个参数已经唯一地定义了协议。将第三个参数指定为0,即通知函数使用适当的默认协议。在调用socket函数后,套接字地址的格式(或它属于哪个地址族)已经很清楚,但尚未给套接字分配本地地址。

bind 函数用于该目的,必须向该函数传递一个sockaddr_type 结构作为参数。该结构定义了本地地址。因为不同地址族的地址类型也不同,所以该结构对每个地址族都有一个不同的版本,以便满足各种不同的要求。type 指定了所需的地址类型。

struct sockaddr_in {
    sa_family_t  sin_family;        /* 地址族 */
    __be16       sin_port;          /* 端口号 */
    struct       in_addr sin_addr;  /* 因特网地址 */
    ...
}

3 网络实现的分层模型
 

相关的C语言代码划分为不同层次,各层次都有明确定义的任务,各个层次只能通过明确定义的接口与上下紧邻的层次通信。这种做法的好处在于,可以组合使用各种设备、传输机制和协议。例如,通常的以太网卡不仅可用于建立因特网(IP)连接,还可以在其上传输其他类型的协议,如Appletalk或IPX,而无须对网卡的设备驱动程序做任何类型的修改。

上图说明了内核对这个分层模型的实现。
分层模型不仅反映在网络子系统的设计上,而且也反映在数据传输的方式上(或更精确地说,对各层产生和传输的数据进行封装的方式)。通常,各层的数据都由首部和数据两部分组成(如下图所示)。

 各协议层的数据划分为首部和数据两部分,首部部分包含了与数据部分有关的元数据(目标地址、长度、传输协议类型等),数据部分包含有用数据(或净荷)。传输的基本单位是(以太网)帧,网卡以帧为单位发送数据。帧首部部分的主数据项是目标系统的硬件地址,这是数据传输的目的地,通过电缆传输数据时也需要该数据项。
高层协议的数据在封装到以太网帧时,将协议产生的首部和数据二元组封装到帧的数据部分。在因特网网络上,这是互联网络层数据。

 上图是在以太网帧中通过TCP/IP传输HTTP数据,清楚地说明了为容纳控制信息所牺牲的部分带宽。

4 网络命名空间

内核的许多部分包含在命名空间中。这可以建立系统的多个虚拟视图,并彼此分隔开来。每个实例看起来像是一台运行Linux的独立机器,但在一台物理机器上,可以同时运行许多这样的实例。在内核版本2.6.24开发期间,内核也开始对网络子系统采用命名空间。这对该子系统增加了一些额外的复杂性,因为该子系统的所有属性在此前的版本中都是“全局”的,而现在需要按命名空间来管理,例如,可用网卡的数量。对特定的网络设备来说,如果它在一个命名空间中可见,在另一个命名空间中就不一定是可见的。
照例需要一个中枢结构来跟踪所有可用的命名空间。其定义如下:

//include/net/net_namespace.h
struct net {
    atomic_t count; /* 用于判断何时释放网络命名空间 */
...
    struct list_head list; /* 网络命名空间的链表 */
...
    struct proc_dir_entry *proc_net;
    struct proc_dir_entry *proc_net_stat;
    struct proc_dir_entry *proc_net_root;
    struct net_device *loopback_dev; /* 环回接口设备 */
    struct list_head dev_base_head;
    struct hlist_head *dev_name_head;
    struct hlist_head *dev_index_head;
};

count 是一个标准的使用计数器,在使用特定的net 实例前后,需要分别调用辅助函数get_net 和put_net 。在count 降低到0时,将释放该命名空间,并将其从系统中删除。所有可用的命名空间都保存在一个双链表上,表头是net_namespace_list 。list 用作链表元素。copy_net_ns函数向该链表添加一个新的命名空间。在用create_new_namespace 创建一组新的命名空间时,会自动调用该函数。每个命名空间都可以有一个不同的环回设备,而loopback_dev指向履行该职责的(虚拟)网络设备。网络设备由struct net_device 表示。与特定命名空间关联的所有设备都保存在一个双链表上,表头为dev_base_head 。各个设备还通过另外两个双链表维护:一个将设备名用作散列键(dev_name_head ),另一个将接口索引用作散列键(dev_index_head )。


5 套接字缓冲区

在内核分析(收到的)网络分组时,底层协议的数据将传递到更高的层。发送数据时顺序相反,各种协议产生的数据(首部和净荷)依次向更低的层传递,直至最终发送。这些操作的速度对网络子系统的性能有决定性的影响,因此内核使用了一种特殊的结构,称为套接字缓冲区 (socket buffer),定义如下:

struct sk_buff {
/* 这两个成员必须在最前面*/
    struct sk_buff *next;
    struct sk_buff *prev;
    struct sock *sk;
    ktime_t tstamp;
    struct net_device *dev;
    struct dst_entry *dst;
    char cb[48];
    unsigned int len,
    data_len;
    __u16 mac_len,
    hdr_len;
    union {
        __wsum csum;
         struct {
            __u16 csum_start;
            __u16 csum_offset;
           };
    };
    __u32 priority;
    __u8 local_df:1,
    cloned:1,
    ip_summed:2,
    nohdr:1,
    nfctinfo:3;
    __u8 pkt_type:3,
    fclone:2,
    ipvs_property:1;
    nf_trace:1;__be16 protocol;
...
    void (*destructor)(struct sk_buff *skb);
...
    int iif;
...
    sk_buff_data_t transport_header;
    sk_buff_data_t network_header;
    sk_buff_data_t mac_header;
/* 这些成员必须在末尾,详见alloc_skb()*/
    sk_buff_data_t tail;
    sk_buff_data_t end;
    unsigned char *head,
    *data;
    unsigned int truesize;
    atomic_t users;
};


 套接字缓冲区用于在网络实现的各个层次之间交换数据,而无须来回复制分组数据,对性能的提高很可观。因为在产生和分析分组时,在各个协议层次上都需要处理该结构。

5.1 使用套接字缓冲区管理数据

如图所示, 套接字缓冲区通过其中包含的各种指针与一个内存区域相关联,网络分组的数据就位于该区域中。这使得内核可以将套接字缓冲区用于所有协议类型。套接字缓冲区在各个协议层之间传递。

在一个新分组产生时,TCP层首先在用户空间中分配内存来容纳该分组数据(首部和净荷)。分配的空间大于数据实际需要的长度,因此较低的协议层可以进一步增加首部。分配一个套接字缓冲区,使得head 和end 分别指向上述内存区的起始和结束地址,而TCP数据位于data 和tail 之间。
在套接字缓冲区传递到互联网络层时,必须增加一个新层。只需要向已经分配但尚未占用的那部分内存空间写入数据即可,除了data 之外所有的指针都不变,data 现在指向IP首部的起始处。下面的各层会重复同样的操作,直至分组完成,即将通过网络发送。


5.2 管理套接字缓冲区数据

套接字缓冲区结构不仅包含上述指针,还包括用于处理相关的数据和管理套接字缓冲区自身的其他成员。
使用了一个表头来实现套接字缓冲区的等待队列。其结构定义如下:
 

//<skbuff.h>
struct sk_buff_head {
/* 这两个成员必须在最前面*/
    struct sk_buff *next;
    struct sk_buff *prev;
    __u32 qlen;
    spinlock_t lock;
};

len 指定了等待队列的长度,即队列中成员的数目。sk_buff_head 和sk_buff 的next 和prev 用于创建一个循环双链表,套接字缓冲区的list 成员指回到表头,如下图所示。

 通过双链表管理套接字缓冲区,分组通常放置在等待队列中,例如分组等待处理时,或需要重新组合已经分析过的分组时。


6 网络访问层

网络实现的第一层,即网络访问层。该层主要负责在计算机之间传输信息,与网卡的设备驱动程序直接协作。
 

6.1 网络设备的表示

在内核中,每个网络设备都表示为net_device 结构的一个实例。在分配并填充该结构的一个实例之后,必须用net/core/dev.c 中的register_netdev 函数将其注册到内核。该函数完成一些初始化任务,并将该设备注册到通用设备机制内。
在详细讨论struct net_device 的内容之前,先阐述一下内核如何跟踪可用的网络设备,以及如何查找特定的网络设备。照例,这些设备不是全局的,而是按命名空间进行管理的。回想一下,每个命名空间(net 实例)中有如下3个机制可用。

  • 所有的网络设备都保存在一个单链表中,表头为dev_base 。
  • 按设备名散列。辅助函数dev_get_by_name(struct net * net, const char * name) 根据设备名在该散列表上查找网络设备。
  • 按接口索引散列。辅助函数dev_get_by_index(struct net * net, int ifindex) 根据给定的接口索引查找net_device 实例。

在内核中,每个网卡都有唯一索引号,在注册时动态分配保存在ifindex 成员中。我们知道内核提供了dev_get_by_name 和dev_get_by_index 函数,用于根据网卡的名称或索引号来查找其net_device 实例。
net_device结构我就不展示出来了,一些结构成员定义了与网络层和网络访问层相关的设备属性

  • mtu (maximum transfer unit,最大传输单位 )指定一个传输帧的最大长度。网络层的协议必须遵守该值的限制,可能需要将分组拆分为更小的单位。
  • type 保存设备的硬件类型,它使用的是<if_arp.h> 中定义的常数。例如,ARPHRD_ETHER 和ARPHDR_IEEE802 分别表示10兆以太网和802.2以太网,ARPHRD_APPLETLK 表示AppleTalk,而ARPHRD_LOOPBACK 表示环回设备。
  • dev_addr 存储设备的硬件地址(如以太网卡的MAC地址),而addr_len 指定该地址的长度。broadcast 是用于向附接的所有站点发送消息的广播地址。
  • ip_ptr 、ip6_ptr 、atalk_ptr 等指针指向特定于协议的数据,通用代码不会操作这些数据。

net_device 结构的大多数成员都是函数指针,执行与网卡相关的典型任务。尽管不同适配器的实现各有不同,但调用的语法(和执行的任务)总是相同的。因而这些成员表示了与下一个协议层次的抽象接口。这些接口使得内核能够用同一组接口函数来访问所有的网卡,而网卡的驱动程序负责实现细节。

6.2 注册网络设备

每个网络设备都按照如下过程注册。

  1. alloc_netdev 分配一个新的struct net_device 实例,一个特定于协议的函数用典型值填充该结构。对于以太网设备,该函数是ether_setup 。其他的协议(这里不详细介绍)会使用形如XXX_setup 的函数,其中XXX 可以是fddi (fiber distributed data interface,光纤分布式数据接口)、tr (token ring,令牌环网)、ltalk (指Apple LocalTalk)、hippi (high-performance parallel interface,高性能并行接口)或fc (fiber channel,光纤通道)。
  2. 在struct net_device 填充完毕后,需要用register_netdev 或register_netdevice 注册。这两个函数的区别在于,register_netdev 可处理用作接口名称的格式串(有限)。在net_device->dev 中给出的名称可以包含格式说明符%d 。在设备注册时,内核会选择一个唯一的数字来代替%d 。例如,以太网设备可以指定eth%d ,而内核随后会创建设备eth0 、eth1 ……

 上图为register_netdevice 的代码流程图

6.3 接收分组

所有现代的设备驱动程序都使用中断来通知内核(或系统)有分组到达。网络驱动程序对特定于设备的中断设置了一个处理例程,因此每当该中断被引发时(即分组到达),内核都调用该处理程序,将数据从网卡传输到物理内存,或通知内核在一定时间后进行处理。
几乎所有的网卡都支持DMA模式,能够自行将数据传输到物理内存。而不需经过操作系统,不需要中断。
传统方法:

下图给出了在一个分组到达网络适配器之后,该分组穿过内核到达网络层函数的路径。

  1. net_interrupt 是由设备驱动程序设置的中断处理程序。它将确定该中断是否真的是由接收到的分组引发的(也存在其他的可能性,例如,报告错误或确认某些适配器执行的传输任务)。如果确实如此,则控制将转移到net_rx 。
  2. net_rx 函数也是特定于网卡的,首先创建一个新的套接字缓冲区。分组的内容接下来从网卡传输到缓冲区(也就是进入了物理内存),然后使用内核源代码中针对各种传输类型的库函数来分析首部数据。这项分析将确定分组数据所使用的网络层协议,例如IP协议。
  3. 与上述两个方法不同,netif_rx 函数不是特定于网络驱动程序的,该函数位于net/core/dev.c 。调用该函数,标志着控制由特定于网卡的代码转移到了网络层的通用接口部分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/730716.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android耗电优化需要注意的要点

作者&#xff1a;小墙程序员 在应用开发中&#xff0c;耗电是我们需要关注的重点。但是&#xff0c;开始进行耗电优化时&#xff0c;我们常常感到无从下手。这篇文章将介绍耗电优化的相关要点&#xff0c;让我们优化时有一个方向。 传感器 大多数Android设备都内置传感器&…

【停用词】NLP中的停用词怎么获取?我整理了6种方法

文章目录 一、停用词介绍二、停用词应用场景2.1 提取高频词2.2 词云图 三、停用词获取方法3.1 自定义停用词3.2 用wordcloud调取停用词3.3 用nltk调取停用词3.3.1 nltk中文停用词3.3.2 nltk英文停用词 3.4 用sklearn调取停用词3.5 用gensim调取停用词3.6 用spacy调取停用词 一、…

select语句与CSP模型~Go进阶

select语句 select 是 Go 中的一个控制结构。select 语句类似于 switch 语句&#xff0c;但是select会随机执行一个可运行的case。如果没有case可运行&#xff0c;它将阻塞&#xff0c;直到有case可运行。 语法结构 select语句的语法结构和switch语句很相似&#xff0c;也有…

【UGUI学习笔记】Rect Transform

文章目录 Rect Transform锚点和轴心轴心点锚点 Rect Transform 锚点和轴心 在上图中&#xff0c;中心位置的蓝色同心圆代表了轴心点&#xff0c;而四角花瓣的图形代表了实体的锚点。 Attribute含义轴心点 Pivot默认实体的几何中心处&#xff0c;轴心代表了对物体在空间上的坐…

安装git工具

下载安装地址&#xff1a; Git - Downloading Package (git-scm.com) 命令安装&#xff1a;创建文件夹下载路径 启动powershell 输入命令&#xff1a;winget install --id Git.Git -e --source winget 等待下载安装

集合面试题--ArrayList数组

介绍数组 数组&#xff08;Array&#xff09;是一种用连续的内存空间存储相同数据类型数据的线性数据结构。数组&#xff08;Array&#xff09;是一种用连续的内存空间存储相同数据类型数据的线性数据结构。 因为int占4个字节&#xff0c;所以也可以理解为占四块内存 数组如何…

600多个人工智能AI工具汇总,资源、教程和讲解免费提供(第二讲)

这里是600多个人工智能AI工具汇总第二讲&#xff0c;每天介绍5个&#xff0c;文章最后会告诉大家获取方式的。现在请大家同我一起进入AIGC的世界。 第一个&#xff1a;Leonardo.Ai&#xff0c;用来创造力、革新为你的创意项目生成质量的资产AI-driven速度和style-consistency。…

代码安全审计

什么是代码安全审计 代码安全审计是指有开发和安全经验的人员,通过阅读开发文档和源代码,以自动化分析工具或者人工分析为手段,对应用程序进行深入分析,高效全面的发现系统代码的编码缺陷以及开发人员不安全的编程习惯,并指导开发人员进行修复,保障应用系统的安全运行。 …

Spring Boot 中的任务调度器是什么,如何使用?

Spring Boot 中的任务调度器是什么&#xff0c;如何使用&#xff1f; 介绍 在开发企业级应用程序时&#xff0c;经常需要执行定时任务或周期性任务。这些任务可以包括清理临时文件&#xff0c;备份数据库&#xff0c;发送电子邮件等等。Spring Boot 提供了一个内置的任务调度…

基于STM32的homeassistant(采用FreeRTOS操作系统)【第一章:设备配网、连接服务器、断网重连、断服务器重接】

第一章开发环境&#xff1a; 主控STM32F103C8T6WIFI模块ESP01S开发语言C开发编译器 KEIL 组网方式WIFI服务器协议MQTT 本章要点&#xff1a; ESP01S的AT指令配网以及服务器连接STM32与ESP01S的usart协议通信断网重连以及断服务器重连STM32向服务器端口发送对应指令 抽象理解…

硬件电路设计--运算放大器(二)选型

文章目录 前言一、运算放大器的工艺决定Vos和Ib二、TI放大器的命名规律三、选型总结 前言 一、运算放大器的工艺决定Vos和Ib 运放的设计工艺对其各种指标有非常重要的影响 常常有三种基本工艺&#xff1a; Bipolar: 低输入阻抗&#xff0c;Ib1-100nA:Vos10-100uV,低至0.1uV/o…

惊艳!全网首份“架构师成长笔记”GitHub狂澜9000星

其实架构师是需要一个相对而言对架构师友善的环境。第一&#xff0c;架构师到底需要什么&#xff1f;一个架构师要成长&#xff0c;首先他需要信任&#xff0c;第二他需要授权&#xff0c;第三他需要时间&#xff0c;第四他需要资源&#xff0c;少一样都很难开展工作。如果一个…

NIO-Selector 网络编程

目录 一、阻塞 & 非阻塞 1、阻塞 2、非阻塞 二、selector 1、连接和读取 2、处理客户端断开 3、处理消息的边界 4、ByteBuffer大小分配 三、多线程优化 四、NIO vs BIO 1、stream vs channnel 2、IO模型 阻塞IO 非阻塞IO 多路复用 异步IO模型 一、阻塞 &am…

使用OpenCV检测两张图片的关键点并计算关键点的描述子

#include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/highgui/highgui.hpp>

每日一博 - 探索代码世界的地图 code iris

文章目录 地址特性安装 code iris如何使用 地址 https://plugins.jetbrains.com/plugin/7324-code-iris 特性 This plugin visualizes the modules, packages and classes of your project. It’s like a UML based “Google Maps” for your Source Code. Code Iris does…

zabbix代理服务器,高可用,监控java,windows,SNMP

zabbix 一&#xff1a;代理服务器1.设置 zabbix 的下载源&#xff0c;安装 zabbix-proxy2.部署数据库&#xff0c;要求 MySQL 5.7 或 Mariadb 10.5 及以上版本2.1.初始化数据库2.2.创建数据库并指定字符集2.3.创建 zabbix 数据库用户并授权 3.导入数据库信息4.修改 zabbix-prox…

前端项目请求天地图地址报错跨域;报错418

原因是因为转义字符&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 在请求回来的地址中 给他转化一下再次请求就OK了&#xff01;

oracle 使从表中随机取出一行记录数据

select * from (select rownum no, a.* from a where status_code AVAILABLE and id_type MEM and archive_flag N and rownum<1000000 ) where no >1000000-1 for update 随机取一条&#xff0c;锁住记录&#xff0c;操作完archive_flag Y不会再取。 四种解决…

【动态规划算法】第八题:931.下降路径最小和

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法\&#x1f384; 如 果 你…

PqQt实现对数据库的添加,删除,修改(完整过程演示)

在PyQt中设置的如下的窗口&#xff1a; 其中的图标是通过新建Resource File加入的 images里面的图片可以在这里面取&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1gOgBpW7s-ZWn_5aRoaYLkQ 提取码&#xff1a;jyjy 我们把这个文件取名为res.qrc 资源文件的使用可以…