数据结构-net_device 结构所在文件:include/linux/netdevice.h,成员结构相当复杂,这个结构也是SKB中的一个成员之一。 unsigned longfeatures; 接口支持的特性。这些标志由内核管理,其中有些由接口在初始化期间设置,用来声明接口的各种能力及特性。 #define NETIF_F_SG1/* Scatter/gather IO. */ #define NETIF_F_IP_CSUM2/* Can checksum only TCP/UDP over IPv4. */ #define NETIF_F_NO_CSUM4/* Does not require checksum. F.e. loopack. */ #define NETIF_F_HW_CSUM8/* Can checksum all the packets. */ #define NETIF_F_HIGHDMA32/* Can DMA to high memory. */ #define NETIF_F_FRAGLIST64/* Scatter/gather IO. */ #define NETIF_F_HW_VLAN_TX128/* Transmit VLAN hw acceleration */ #define NETIF_F_HW_VLAN_RX256/* Receive VLAN hw acceleration */ #define NETIF_F_HW_VLAN_FILTER512/* Receive filtering on VLAN */ #define NETIF_F_VLAN_CHALLENGED 1024/* Device cannot handle VLAN packets */ #define NETIF_F_GSO2048/* Enable software GSO. */ #define NETIF_F_LLTX4096/* LockLess TX */ unsigned intflags;/* interface flags (a la BSD)*/ ![]() unsigned shorttype;/* interface hardware type*/ unsigned shorthard_header_len;/* hardware hdr length*/ 硬件首部长度,以太网为14B struct net_device*master; 在启用了bonding网络负载均衡以后,指向bonding的虚拟设备。 unsigned charperm_addr[MAX_ADDR_LEN]; /* permanent hw address */ 硬件地址 void*ip_ptr;/* IPv4 specific data*/ IPv4相关设置存放在这个字段中,指针指向struct in_device。涉及到IP编址的部分内容,见下一个章节。 Int (*poll)(struct net_device *dev, int *quota); 网卡设备dev接收报文时候的轮询函数。 Int quota 读取数据包的配额,动态变化,每次从网络设备中读取数据包的时候,会从中减去本次读取的数据包数。当配额小于0时,结束轮询,这样即使当前网络设备上有大量的数据包输入,也能保证其他设备及时接收数据包。 网络设备的注册启用禁用 这部分内容并非协议栈的重点,这里不在展开来讲 IP编址 IP地址的组织 与IPv4相关的配置存放在in_device(IP配置快)结构中,IP地址、子网掩码、广播地址等信息放在in_ifaddr结构中。 ![]() struct in_device 所在文件:include/linux/inetdevice.h IP配置块。 网络设备层与IPv4相关的配置再放在in_device结构中,应用层可以通过ip或者ifconfig工具来修改这些配置。该结构的实例保存在net_device结构成员ip_ptr指针中。只有为设备设置第一个IP地址的时候,在函数inetdev_init()中才会分配并初始化in_device结构实例。 struct in_ifaddr*ifa_list;/* IP ifaddr chain*/ 指向in_ifaddr结构的链表。in_ifaddr结构中存储了网络设备的IP地址,因为一个网络设备可配置多个IP地址,因此需要链表来存储。 in_ifaddr结构 in_ifaddr结构:用于存储主机的IP地址、子网掩码、广播地址,这些配置属于主机,但是又配置到网络设备上,一个网络设备有多少个IP地址,就有多少了IP地址块。 管理函数 函数所在文件:net/ipv4/devinet.h inetdev_init() 为通过参数指定的网络设备分配IP配置块。 inet_select_addr() 再通过输出网络设备向目的地址发送报文的时候,如果没有指定源地址,会调用这个函数来根据给定的设备、目的地址和作用范围,获取给定作用范围的主IP地址作为源地址。 Dev:获取源地址的网络设备 Dst:目的地址 Scope:作用范围 RT_SCOPE_HOST RT_SCOPE_LINK RT_SCOPE_UNIVERSE 使用for_primary_ifa宏遍历到第一个符合条件的IP地址。inet_ifa_match函数用来判断两个IP是不是属于一个字网。优先选定在一个网段的源IP,否则的话随便选一个。 IP地址的设置 Ifconfig---->ioctl Ip address---->netlink Ioctl和netlink的相关知识不在本讲的范围内,请自行查阅相关资料。 第二部分 接口层 接口层的输入 1、背 景 系统如何得知数据包的到达并接收? 方案一:当数据包到达网络设备的时候,通常会触发硬件中断。系统在不支持软中断时,数据包的输入只能完全由硬件中断处理。缺点:频繁的硬件中断占用过多的CPU资源,使得用户进程处于饥渴状态。 方案二:数据包到达网络设备不会触发硬件中断,在这种情况下,只能通过定时器轮询网络设备的状态,发现有数据包到达时,才从网络设备中读取数据包并输入到协议栈。这种情况下,数据包的输入完全依赖于定时器触发的频率,如果频率过高,同样也消耗CPU资源,频率过低,数据包吞吐量过分低下。 2、接口层的初始化 net_dev_init():所在文件:net/core/dev.c 函数解释: ![]() 位置:include/linux/netdevice.h 描述了与网络软中断处理相关的报文输入和输出队列,每个CPU有一个单独的softnet_data实例,因此在操作该结构中的成员时不必加锁。 struct net_device *output_queue: 数据包输出软中断中输出数据包的网络设备队列。处于报文输出状态的网络设备添加到该队列上,在数据包输出软中断中,会遍历该队列,从网络设备的排队规则中获取数据包并输出。 struct sk_buff_head input_pkt_queue: 非NAPI的接口缓存队列。对于非NAPI的驱动,通常在硬中断中或通过轮询读取报文后,调用netif_rx()将接收到的报文传递到上层,即先将报文缓存到input_pkt_queue队列中,然后产生一个数据包输入软中断,由软中断例程将报文传递到上层。这在接口接收数据包的速率比协议栈和应用层快的时候非常有用。队列长度上限参见系统参数netdev_max_backlog。 struct list_head poll_list: 网络设备轮询队列。处于报文接受状态的网络设备链接到该队列上,在数据报输入软中断中,会遍历该队列,通过轮询方式接受报文。 ![]() struct sk_buff *completion_queue: 完成发送数据包的等待释放队列。需在适当的时机释放发送完成的数据,在发送报文软中断中会检测该队列。 NAPI NAPI是中断机制与轮询机制的混合体,能有效的提高网络处理速度。在网络负荷较重的时候,NAPI可以显著减少由于接受数据包而产生的硬中断数量,对于高速率、短长度的数据包的处理非常有效。 实现方法:当一批数据包中的第一个数据包到达网络设备的时候,会以硬中断的方式通知系统;在硬终端例程中(网卡设备的中断驱动程序),系统将该设备添加到CPU的设备轮询队列中,并关闭中断,同时激活数据包输入软中断;由软中断例程遍历轮询队列中的网络设备,从中读取数据包。这样,在内核从网络设备中接收报文的过程中,若有新的报文到来,NAPI也无需执行中断例程,只需要维护队列,就能读到新的报文。 ![]() 加红部分:禁止该网络设备上的中断,并将网络设备添加到网络设备轮询队列当中去。 软中断 Net_rx_action():文件位置:net/core/dev.c if (dev->quota <= 0 || dev->poll(dev, &budget)) 本次读取报文的配额已用完,或者经过一次轮询后还有报文没读完。 softnet_break: 对本次读取报文数量的总配额已经用完…….. 轮询处理 文件位置:e100.c E100_rx_clean()从网络设备中读取接收到的报文,并输入到上层协议中。 E100_tx_clean()释放已发送的报文(已经接收完毕)。 如果待输出和输入的报文都已经处理完成,则退出轮询模式,并从网络设备轮训队列中删除该网络设备。 ok ,就写到这里了,我想静静,下一节讲述接口层输入/出的处理等、(本文可作为读书笔记) ![]() |
|