网卡驱动:
1.初始化DMA中断函数
2.驱动提供 int (*poll)(struct napi_struct *, int)回调函数用于接收数据帧
3.驱动分配结构A,A中包括一个struct napi_struct结构体B
4.驱动调用netif_napi_add()函数,此函数关键:
4.1 B->poll = poll (poll为驱动接收数据帧回调函数),即2中的函数
4.2 B->weight = weight 接收权重,默认由驱动设置为64,表示一个poll可以最多接收64个帧
4.3 list_add(&napi->dev_list, &dev->napi_list); 将B->dev_list加入此网卡设备的napi_list链表
将驱动提供的帧接收回调函数加入struct net_device结构体(每个网络设备都会使用struct net_device来描述)的dev->napi_list链表中。
4.4 set_bit(NAPI_STATE_SCHED, &napi->state); 设置napi的状态
NAPI收包:
1.驱动打开NAPI功能,默认处于未工作状态(没有收包)
2.数据包到达,网卡通过DMA写到内存
3.网卡触发一个硬件中断,中断处理函数做的事情:
napi_schedule(&B)
__napi_schedule(n)
____napi_schedule(this_cpu_ptr(&softnet_data), n);
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
3.1 将B->poll_list加入到softnet_net->poll_list中
3.2 使能NET_RX_SOFTIRQ(其实就是将软中断表示位置为1,在进程退出内核态前夕时,会去检测此标志,一般在entry.S汇编中,所以软中断优先级高于应用程序,可以理解为抢占应用程序)。
net_rx_action
此函数为软中断调用的收包函数
1.将softnet_net->poll_list中的所有元素,添加到临时链表list
2.遍历list链表,调用驱动中注册的poll函数收包
2.1 收包需满足weight不能超过配置的64,时间不能超过2ms
2.2 如果收包低于64,并且时间不超过2ms就没有包的,将list加入到struct softnet_data->list队尾
2.3 如果收包大于等于64或者超过2ms,则关闭NAPI,等下一次硬件中断触发。
2.4 收割的数据完数据(接收的包大于等于weight时),调用napi_complete()把数据上协议栈。
注意,在本次没有接收完成时,还会设置软中断标志位,为什么呢?虽然本次没有接收完所有的数据包,但是释放了此次软中断抢占的进程,下一次软中断可能就不会在本次的进程上下文中了。可以降低进程的延迟。