(3)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_schedule。NAPI设备所给予的性能会好很多。(因为NAPI中的poll函数是直接从设备中取数据,而非NAPI的是用积压设备来替代真实设备,实际上的数据依然是从内核中取走的)