Linux网络内核数据帧的接收过程:数据链路层(概念篇)


处理链路层(L2)的函数是由中断事件驱动的。硬件会使用中断事件通知CPU,该帧已经可用了。接收中断事件的CPU会执行do_IRQ函数。IRQ编号引发正确的中断处理函数被启用。此处理函数通常是设备驱动程序在设备驱动程序初始化期间所注册的函数。IRQ函数处理函数会在中断模式下执行,即后续的中断事件都会暂时被关闭。

中断处理函数会执行一些立即性的任务,然后把其他任务安排到下半部函数中以便在稍后执行,明确地讲,中断处理函数会

1、 把帧拷贝到sk_buff数据结构中。

2、 对一些sk_buff参数做初始化,以便在稍后由上面的网络层使用(这是通过skb->protocol的字段来表示较高层协议处理函数)

3、 更新其他一些设备私用的参数。

4、 为NET_RX_SOFTIRQ软IRQ调度以准备执行,借此通知内核新帧的事。

由于设备发出中断事件的理由各有不同(新帧已接收、帧已成功传输等等),内核的代码会配合中断通知信息,使得设备驱动程序处理例程可以按类型处理中断事件

(一)、设备的开启和关闭

当net_device->status中的__LINK_STATE_START标识被设置时,设备就可被视为已开启。当设备打开时,这个标识通常会被设置,而当设备关闭时,就会被清除。

(二)、队列

讨论L2行为时,通常会提到帧接收和传  输时所需的队列。每个队列都有一个指针指向其相关联的设备,以及一个指针指向存储输入/输出缓冲区的skb_buff数据结构。只有少数专用的设备可以不需要队列就能工作,其中一例是环回设备,环回设备可以省掉队列,因为当你从环回设备传出一个封包时,该封包会立刻被递送出去,而无需排入队列的中间过程。再者,因为在环回设备上的传输不会失败,也没有必要让封包重新排入队列以尝试另一次传输。

(三)、NAPI与netif_rx

在Linux2.6版本中,引进了一套新的API以处理入口帧,由于找不到更好的名称,所以称之为NAPI(New API)。由于还有很多设备没有更新成NAPI,所以Linux驱动程序通知内核新帧的事有两种方式。

通过旧函数netif_rx

这种方法只通过中断期间来处理多帧。

通过NAPI机制

这种方法是通过中断与轮询混合使用来处理多帧的。

(1)NAPI简介

虽然有些NIC的设备驱动程序尚未改用NAIP,但是新型基础架构已经整合至内核,而且连旧式的netif_rx和内核其余部分之间的接口也都把NAPI考虑进来了。所以这里先简单介绍下NAPI的特性才来看netif_rx。

NAPI混合了中断事件和轮询,在高流量负载下其性能会比旧方法要好,因为可以大幅减少CPU的负载。

在旧模式中,设备驱动程序会为其所接收的每个帧都产生一个中断事件。在高流量负载下,花在处理中断事件的时间会造成资源相当程序的浪费。

NAPI背后的主要想法很简单:混合使用中断事件和轮询,而不使用纯粹的中断事件驱动模型。如果接收到新帧时,内核还没完成处理前几个帧的工作,驱动程序就没必要产生其他中断事件:让内核一直处理设备输入队列中的数据会比较简单一点,然后当该队列为空时再重新开启中断功能。如此一来,驱动程序就能获得中断事件和轮询的优点:

◇异步事件,如帧的接收,是由中断事件指出,如此一来,如果设备的入口队列是空的,内核就不用一直去检查了。

◇ 如果内核知道设备的入口队列中有数据存在,就没必要浪费时间去处理中断事件通知信息。用简单的轮询就够了。

(2)NAPI所用之net_device字段

为了处理驱动程序使用NAPI接口的设备,有四个新字段添加到此结构中,以供NET_RX_SOFTIRQ软IRQ使用。其它设备(非NAPI的设备)不会用到这些字段,但是它们可共享嵌入在softnet_data结构中作为backlog_dev字段的net_device结构的字段。(backlog_dev是积压设备,主要是为了处理非NAPI的驱动程序来满足NAPI的架构的一个对象)

poll

这个虚拟函数可用于把缓冲区从设备的输入队列中退出。此队列是使用NAPI设备的私有队列,而softnet_data->input_pkt_queu供其它设备使用

poll_list

这是设备列表,其中的设备就是在入口队列中有新帧等待被处理的设备。这些设备就是所谓的处于轮询状态。此列表的头为softnet_data->poll_list。此列表中的设备都处于中断功能关闭状态,而内核当前正在予以轮询。

由于Linux现在已经将NAPI的架构整合进了内核,并取代了老式的架构,所以当不支持NAPI的设备接受到来的新帧时,这个设备列表中的当前设备就是刚才说的积压设备——backlog_dev。积压设备有自己的方法来模拟NAPI的机制。这个之后会说明。

quota

weight

quota(配额)是一个整数,代表的是poll虚拟函数一次可以从队列退出的缓冲区的最大数目。其值的增加以weight为单位,用于在不同设备间施加某种公平性。配额越低, 表示潜在的延时愈低,因此让其他设备饿死的风险就愈低,另方面,低配额会增加设备间的切换量,因此整体的耗费会增加。

对配有非NAPI驱动程序的设备而言,weight的默认值为64。存储在net/core/dev.c顶端的weight_p变量。weight_p之值可通过/proc修改。

对配有NAPI程序的设备而言,默认值是由驱动程序所选。最常见的值是64,但是也有使用16和32的,其值可能过sysfs调整。

  • 1
  • 2
  • 下一页

相关内容