赞
踩
本文对代码的详细实现过程不做过多的讲解,重点让读者熟悉数据的接收过程,如需进一步熟悉源码,可根据下面的链接做进一步学习:
1、网卡驱动源码分析
2、网卡结构和基础知识详解
从TCP/IP网络分层模型中可以清楚当数据帧从网卡(物理层)接收到客户端(应用层)收到数据包的整个过程。
通过网卡进行网络数据接收一般要经历下面两个过程:
1、接收数据前的准备工作
1)网络子系统的初始化;
2)协议栈的注册;
3)网卡驱动的初始化;
4)启动网卡;
2、接收和传输网络数据:
1、网络子系统的初始化:
linux系统在加载内核到内存中后会进行大量的初始化工作,其中初始化各个内核子系统都是通过调用subsys_initcall函数来实现,具体初始化网络子系统主要有以下三个方面:
1、申请一个softnet_data数据结构,在这个数据结构里的poll_list是等待驱动程序将其poll函数注册进来;
2、将每一种软中断都注册一个处理函数;
3、将软中断和对应的中断处理函数都放在softirq_vec数组中。
具体流程如下图:
2、协议栈的注册:
内核实现了网络层的ip协议,也实现了传输层的tcp协议和udp协议。 这些协议对应的实现函数分别是ip_rcv(),tcp_v4_rcv()和upd_rcv()。和我们平时写代码的方式不一样的是,内核是通过注册的方式来实现的,这些函数主要是将从物理层ring_buffer写到skb_buffer中的数据做进一步的解析处理并传至上一层;
3、网卡驱动的初始化:
每一个驱动程序(不仅仅只是网卡驱动)会使用 module_init 向内核注册一个初始化函数,当驱动被加载时,内核会调用这个函数去完成驱动的注册、网卡设备的初始化和网卡驱动和设备的匹配等过程。
4、启动网卡:
当完成前面的注册和初始化过程后,网卡就可以通过命令ifconfig eth0 up进行打开,网卡的打开过程就是通过调用驱动注册的open函数去实现的,igb_open函数执行过程中,还会启用msix机制和NAPI机制,其中msix机制可以具体理解为中断管理机制,通过该函数调用最终的中断处理函数,而NAPI机制是与最开始的软中断调用的poll函数相关的,具体流程如下图:
整体过程梳理:
首先网卡接收网络数据帧并通过DMA保存在ring_buffer中,此时网卡通过硬中断通知cpu接收到数据,cpu会生成一个软中断,然后通过创建一个ksoftirqd内核线程处理软中断,然后通过NAPI机制调用驱动注册的poll函数,poll函数会将ring_buffer中的数据保存在skb_buffer中然后调用netif_receive_skb()将数据发送到内核协议栈进行处理,也就会调用最开始协议栈注册的函数。
具体流程如下图:
下面具体解析一个硬中断和ksoftirqd内核线程处理软中断:
硬中断:
首先当数据帧从网线到达网卡上的时候,第一站是网卡的接收队列。网卡在分配给自己的RingBuffer中寻找可用的内存位置,找到后DMA引擎会把数据DMA到网卡之前关联的内存里,这个时候CPU都是无感的。当DMA操作完成以后,网卡会像CPU发起一个硬中断,通知CPU有数据到达,具体流程如下图:
ksoftirqd内核线程处理软中断:
网络子系统初始化部分, 我们看到我们为NET_RX_SOFTIRQ注册了处理函数net_rx_action,函数就是在这里被调用执行的。
注意:
硬中断中设置软中断标记,和ksoftirq的判断是否有软中断到达,都是基于smp_processor_id()的。这意味着只要硬中断在哪个CPU上被响应,那么软中断也是在这个CPU上处理的。所以说,如果你发现你的Linux软中断CPU消耗都集中在一个核上的话,做法是要把调整硬中断的CPU亲和性,来将硬中断打散到不通的CPU核上去。
最后讲述协议栈的处理过程:
在ksoftirqd内核线程处理软中断最终调用的netif_receive_skb函数会根据包的协议,假如是upd包,会将包依次送到ip_rcv(),upd_rcv()协议处理函数中进行处理,最终将数据包传至传输层,函数处理流程如下:
接收数据前准备工作:
1、创建ksoftirqd线程,为它设置好它自己的线程函数,后面就指望着它来处理软中断;
2、协议栈注册,linux要实现许多协议,比如arp,icmp,ip,udp,tcp,每一个协议都会将自己的处理函数注册一下,方便包来了迅速找到对应的处理函数;
3、网卡驱动初始化,每个驱动都有一个初始化函数,内核会让驱动也初始化一下。在这个初始化过程中,把自己的DMA准备好,把NAPI的poll函数地址告诉内核;
4、启动网卡,分配RX,TX队列,注册中断对应的处理函数;
以上是内核准备收包之前的重要工作,当上面都ready之后,就可以打开硬中断,等待数据包的到来了。
数据接收:
1、网卡将数据帧DMA到内存的RingBuffer中,然后向CPU发起中断通知;
2、CPU响应中断请求,调用网卡启动时注册的中断处理函数;
3、中断处理函数发起软中断请求;
4、内核线程ksoftirqd线程发现有软中断请求到来,先关闭硬中断;
5、ksoftirqd线程开始调用驱动的poll函数收包;
5、poll函数将受到的包送到协议栈注册的ip_rcv函数中;
6、ip_rcv函数再讲包送到upd_rcv函数中(对于tcp包就送到tcp_rcv)。
读者有兴趣可以通过下面的链接对网卡其他的知识进行学习:
1、网卡驱动源码分析
2、网卡结构和基础知识详解
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。