Preface
Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计。
Linux内核采用分层结构处理网络数据包。分层结构与网络协议的结构匹配,既能简化数据包处理流程,又便于扩展和维护。
一、内核网络结构在Linux内核中,对网络部分按照网络协议层、网络设备层、设备驱动功能层和网络媒介层的分层体系设计。
网络驱动功能层主要通过网络驱动程序实现。
在Linux内核,所有的网络设备都被抽象为一个接口处理,该接口提供了所有的网络操作。
net_device结构表示网络设备在内核中的情况,也就是网络设备接口。网络设备接口既包括软件虚拟的网络设备接口,如环路设备,也包括了网络硬件设备,如以太网卡。
Linux内核有一个dev_base的全局指针,指向一个设备链表,包括了系统内的所有网络设备。该设备链表每个节点是一个网络设备。
在net_device结构中提供了许多供系统访问和协议层调用的设备方法,包括初始化、打开关闭设备、数据包发送和接收等。
二、与网络有关的数据结构内核对网络数据包的处理都是基于sk_buff结构的,该结构是内核网络部分最重要的数据结构。
网络协议栈中各层协议都可以通过对该结构的操作实现本层协议数据的添加或者删除。使用sk_buff结构避免了网络协议栈各层来回复制数据导致的效率低下。
sk_buff结构可以分为两个部分,一部分是存储数据包缓存,在图中表示为PackertData,另一部分是由一组用于内核管理的指针组成。
sk_buff管理的指针最主要的是下面4个:
head指向数据缓冲(PackertData)的内核首地址;
data指向当前数据包的地址;
tail指向当前数据包的地址;
end 指向数据缓冲的内核尾部。
数据包的大小在内核网络协议栈的处理过程中会发生改变,因此data和tail指针也会不断变化,而head和tail指针是不会发生改变的。
对于一个TCP数据包为例,sk_buff还提供了几个指针直接指向各层协议头。mac指针指向数据的mac头;nh指针指向网络协议头,一般是IP协议头;h指向传输层协议头,在本例中是TCP协议头。
对各层设置指针的是方便了协议栈对数据包的处理。
三、net_device结构在linux中使用struct net_device结构体来描述每一个网络设备。同时这个用来刻画网络设备的struct net_device结构体包含的字段非常的多,以至于内核的开发者都觉得在现在的linux内核中,这个struct net_device是一个大的错误。在本篇文章中,只介绍struct net_device中的一些字段,其他的字段在以后使用的时候再说。
#define IFNAMSIZ 32 struct net_device { //用于存放网络设备的设备名称; char name[IFNAMSIZ]; //网络设备的别名; char *ifalias; //网络设备的接口索引值,独一无二的网络设备标识符; int ifindex; //这个字段用于构建网络设备名的哈希散列表,而struct net中的 //name_hlist就指向每个哈希散列表的链表头; struct hlist_node name_hlist; //用于构建网络设备的接口索引值哈希散列表,在struct net中的 //index_hlist用于指向接口索引值哈希散列表的链表头; struct hlist_node index_hlist; //用于将每一个网络设备加入到一个网络命名空间中的网络设备双链表中 struct list_head dev_list; //网络设备接口的标识符,其状态类型被定义在<linux/if.h>之中; unsigned int flags; //网络设备接口的标识符,但对用户空间不可见; unsigned short priv_flags; //接口硬件类型,在<if_arp.h>中定义了每一个接口硬件类型; unsigned short type; //网络设备接口的最大传输单元; unsigned mtu; //硬件接口头长度; unsigned short hard_header_len; //网络设备接口的MAC地址; unsigned char *dev_addr; //网络设备接口的单播模式 int uc_promisc; //网络设备接口的混杂模式; unsigned int promiscuity; //网络设备接口的全组播模式; unsigend int allmulti; //secondary unicast mac address struct netdev_hw_addr_list uc; //list of device hw address; struct netdev_hw_addr_list dev_addrs; //hw broadcast address; unsigned char broadcast[MAX_ADDR_LEN]; //multicast mac address; struct dev_addr_list *mac_list; //网络设备接口的数据包接收队列; struct netdev_queue rx_queue; //网络设备接口的数据包发送队列; struct netdev_queue *tx; //Number of TX queues allocated at alloc_netdev_mq() time unsigned int num_tx_queues; //Number of TX queues currently active in device; unsigned int real_num_tx_queues; //Max frame per queue allowned; unsigned long tx_queue_len; //网络设备接口的状态; unsigned long state; //网络设备接口的统计情况; struct net_device_state states; //用于执行网络设备所在的命名空间; struct net *nd_net; }
下面的这幅图用于展示linux内核如何利用struct net和struct net_device来构成一个网络命名空间:
详细免费视频资料关注 后台私信;资料;两个字可以免费视频领取 文档 各大厂面试题 资料内容包括:C/C ,Linux,golang,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,嵌入式 等
四、数据包接收流程
在Linux内核中,一个网络数据包从网卡接收到用户空间需要经过链路层、传输层和socket的处理,最终到达用户空
1、数据包到达网卡设备
2、收到数据后网卡根据设置的hash规则(多队列网卡),决定将数据放到哪个ring buffer,然后通过DMA将数据放到内存,也就是网卡的ring buffer中
3、设备产生硬件中断,根据网卡设置的中断绑核通知对应CPU处理数据报文
4、对应CPU执行网络设备注册的中断处理函数,响应硬中断,将该设备添加到CPU轮询设备队列中,关闭网络设备中断响应,并唤醒软中断处理
5、系统根据RPS和RFS的设置,确定软中断在哪个CPU执行,对应CPU的NAPI软中断处理例程从CPU的轮询队列中按额度从ring buffer中poll数据报文skb并递交上层协议栈处理,收完报文后,恢复网卡设备中断响应
6、协议栈处理报文
7、报文递交至应用程序socket的receive buffers中
详细免费视频资料关注 后台私信;资料;两个字可以免费视频领取 文档 各大厂面试题 资料内容包括:C/C ,Linux,golang,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,嵌入式 等
五、数据包发送流程以UDP数据包发送流程为例,在DM9000网卡上如何发送一个数据包。
当用户空间的应用程序通过 socket函数 sento()发送一个UDP数据后,会调用内核空间的 sock_writev()函数,然后通过 sock_sendmsg()函数处理。sock_sendmsg()函数调用 inet_sendmsg()函数处理,inet_sendmsg()函数会把要发送的数据交给传输层的 udp_sendmsg()函数处理。
udp_sendmsg()函数在数据前加入UDP头,然后把数据交给 ip_build_xmit()函数处理,该函数根据 socket提供的目的 IP和端口信息构造IP头,然后调用 output_maybe_reroute()函数处理。out_maybe_reroute()函数检查数据包是否需要经过路由,最后交给 ip_output()函数写入到发送队列,写入完成后由 ip_finish_output()函数处理后续工作。
链路层的 dev_queue_xmit()函数处理发送队列,调用 DM9000网卡的发送数据包函数 dm9000_xmit()发送数据包,发送完毕后,调用 dm9000_xmit_done函数处理发送结果。
总结:更多免费视频教程文档资料免费领取后台私信【资料】自行获取。,