Linux内核网络协议栈笔记4 接收网络数据包详细过.doc
文本预览下载声明
Linux内核网络协议栈笔记4 接收网络数据包详细过
Linux内核网络协议栈笔记4:接收网络数据包详细过程2010-08-10 15:41网络数据接收过程,从数据包到达网卡的物理接口开始,然后由网卡的驱动程序交给网络协议栈,最后经过协议栈的一层层处理之后交给应用程序。大致上是这样的过程,但实际上有更多的细节。本文中主要介绍第一个和第二个步骤。
我们本文中依然以一个Realtek 8139网卡为例(驱动程序为/drivers/net/8139too.c)。请注意在内核代码中receive都是用rx简写的。
(1)注册与激活软中断
在生成net_device对象及初始化的函数rtl8139_init_one中已经初始化dev-open方法为rtl8139_open函数(在本系列文章2:初始化中的net_device对象中已经介绍,点这里查看)。在rtl8139_open函数(这个函数在网卡启动时被调用)中注册了一个中断函数rtl8139_interrupt:
retval=request_irq(devirq,rtl8139_interrupt,SA_SHIRQ,devname,dev);所以只要当网卡开启后(状态为up),当网络数据包到达时,都会产生一个硬件中断(这不同于后面的软中断)。这个硬件中断由内核调用中断处理程序rtl8139_interrupt函数处理。这个函数比较重要,网卡发送或者接收数据时内核都会调用这个函数处理中断,而中断的类型是根据网卡状态寄存器的不同而确定的。本文中仅涉及接收数据的中断,因此只给出了接收的代码:
staticirqreturn_t rtl8139_interrupt(intirq,void*dev_instance,structpt_regs*regs)
{
if(statusRxAckBits){
if(netif_rx_schedule_prep(dev))
__netif_rx_schedule(dev);
}
}主要函数为__netif_rx_schedule(函数名意为:network interface receive schedule,即网络接口接收调度),因为当网卡接收到数据包之后,马上告知CPU在合适的时间去启动调度程序,轮询(poll)网卡。
请注意:Linux接收网络数据实际上有两种方式。
(a)中断。每个数据包到达都会产生一个中断,然后由内核调用中断处理程序处理。
(b)NAPI(New API)。Linux内核2.6版本之后加入的新机制,核心方法是:不采用中断的方式读取数据,而代之以首先采用中断唤醒数据接收的服务程序,然后以POLL的方法来轮询数据。
因此本文中只介绍NAPI的接收方式。我们不再详细介绍这种机制,网上可找到比较多的资料,可以参考IBM的技术文章:NAPI技术在Linux网络驱动上的应用和完善。__netif_rx_schedule函数的定义如下:
staticinlinevoid__netif_rx_schedule(structnet_device*dev)
{
local_irq_save(flags);//disable interrupt
//Add interface to tail of rx poll list list_add_tail(devpoll_list,__get_cpu_var(softnet_data).poll_list);
//activate network rx softirq __raise_softirq_irqoff(NET_RX_SOFTIRQ);
local_irq_restore(flags);
}这个函数最核心的就是三步:
(a)local_irq_save:禁用中断
(b)list_add_tail:将设备添加到softnet_data的poll_list中。
(c)激活一个软中断NET_RX_SOFTIRQ。
==
说到这里我们必须介绍一个关键数据结构softnet_data,每个CPU都拥有一个这样的网络数据队列(所以函数中使用了__get_cpu_var函数取得),定义如下:
structsoftnet_data
{
int throttle;/*为1表示当前队列的数据包被禁止*/
int cng_level;/*表示当前处理器的数据包处理拥塞程度*/
int avg_blog;/*某个处理器的平均拥塞度*/
structsk_buff_head input_pkt_queue;/*接收缓冲区的sk_buff队列*/
structlist_head poll_list;/*POLL设备队列头*/
structnet_device output_queue;/*网络设备发送队列的队列头*/
显示全部