鸿 网 互 联 www.68idc.cn

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

来源:互联网 作者:佚名 时间:2015-06-03 08:51
处理链路层(L )的函数是由中断事件驱动的 硬件会使用中断事件通知CPU 该帧已经可用了 接收中断事件的CPU会执行do_IRQ函数 IRQ编号引发正确的中断处理函数被启用 此处理函数通常是设备驱动程序在设备驱动程序初始化期间所注册的函数 IRQ函数处理函数会在中

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

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

   把帧拷贝到sk_buff数据结构中

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

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

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

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

  (一)设备的开启和关闭

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

  (二)队列

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

  (三)NAPI与netif_rx

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

  通过旧函数netif_rx

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

  通过NAPI机制

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

  ()NAPI简介

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

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

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

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

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

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

  ()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的默认值为存储在net/core/devc顶端的weight_p变量weight_p之值可通过/proc修改

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

  ()net_rx_action软中断处理函数和NAPI

  如下图所示是每次内核轮询进来的网络流量时所发生的事在此图中可以看到poll_list列表其内的设备处于轮询状态也可以看到poll虚拟函数以及软中断函数net_rx_action之间的关系

  

  我们已经知道net_rx_action是与NET_RX_SOFTIRQ标识相关联的函数

  假设在一段低活动量期间之后有些设备开始接收帧并在硬件中断中触发了对应于NIC的中断服务例程并且为NET_RX_SOFTIRQ软IRQ调度以准备执行最终这些行为触发了net_rx_action的软中断net_rx_action会浏览列表中处于轮询状态的设备然后为每个设备都调用相关联的poll虚拟函数以处理入口队列中的帧

  之前说过该列表中的设备会按照循环方式被查阅而且每次其poll方法启用时能处理的帧数目都有最大值存在如果在其时间片内无法使队列清空就得等到下一个时间片继续下去也就是说net_rx_action软中断处理函数会持续为入口队列中有数据的设备调用其设备驱动程序所提供的poll方法真到入口队列为空到那时就不再轮询了而设备驱动程序就可重新开启该设备的中断事件通知功能值得强调的是中断功能关闭只针对那些在poll_list中的设备也就是只用于那些使用NAPI而不共享backlog_dev的设备

  net_rx_action会限制其执行时间当其用完限制的执行时间或处理过一定数量的帧后就会自行重新调度准备执行这样是为了强制net_rx_action能与其他内核任务彼此公平运行同是每个设备也公限制其poll方法每次启用时所能处理的帧数目才能与其它设备之间彼此公平运行当设备无法清空其入口队列时就得等到下一次调用其poll方法的时候

  

  从设备驱动程序的角度看NAPI和非NAPI之间只有两点差异首先NAPI驱动程序必须提供一个poll方法其次为帧调度所调用的函数有别非NAPI调用netif_rx而NAPI驱动程序调用__netif_rx_schedule内核提供一个名为netif_rx_schedule的包裹函数检查以确保该设备正在运行而且该IRQ还调度然后才调用__netif_rx_schedule这些检查由以netif_rx_schedule_prep进行的

  如上图所未这两种驱动程序都会把输入设备排入轮询列表为NET_RX_SOFTIRQ软中断调度以及准备执行最后再由net_rx_action予以处理即使这两种驱动程序最后都会调用__netif_rx_scheduleNAPI设备所给予的性能会好很多(因为NAPI中的poll函数是直接从设备中取数据而非NAPI的是用积压设备来替代真实设备实际上的数据依然是从内核中取走的)

  本篇文件参考《Linux网络技术内幕》 下载见

网友评论
<