- 论坛徽章:
- 0
|
这里queue->backlog_dev并不是一个实际上存在的网络设备,netif_rx_schedule()只是利用了其中的poll函数而已。
一、问题的背景
这个问题是《深入理解Linux网络技术内幕》中第三部分第九、第十章提到的“在终中断期间处理多帧”的方法netif_rx。
内核代码2.6.12。
二、初始化
net/core/dev.c
3275 static int __init net_dev_init(void)
3303 for (i = 0; i < NR_CPUS; i++) {
3304 struct softnet_data *queue;
3305
3306 queue = &per_cpu(softnet_data, i);
3307 skb_queue_head_init(&queue->input_pkt_queue);
3308 queue->throttle = 0;
3309 queue->cng_level = 0;
3310 queue->avg_blog = 10; /* arbitrary non-zero */
3311 queue->completion_queue = NULL;
3312 INIT_LIST_HEAD(&queue->poll_list);
3313 set_bit(__LINK_STATE_START, &queue->backlog_dev.state);
3314 queue->backlog_dev.weight = weight_p;
3315 queue->backlog_dev.poll = process_backlog;
3316 atomic_set(&queue->backlog_dev.refcnt, 1);
3317 }
这里的意思是有几个CPU,就分配几个softnet_data结构,而每个softnet_data结构都包含一个net_device结构。
所以说这里的backlog_dev并不是实际上存在的网络设备,这里最重要的是初始化backlog_dev的状态:__LINK_STATE_START,
以及poll:process_backlog。
三、netif_rx处理过程
第一次因为接收包发生中断后,关闭中断,进入中断处理函数,此时连续处理接收到的包(这也是在中断期间处理多帧的本质),通过netif_rx()函数将新接收的包链入softdata->input_pkt_data队列上。当没有新包时,中断处理结束,开中断。
当net_rx_action()运行时,调用dev->poll()函数处理接收的包,而因为在net_dev_init()中已经将dev->poll()初始化成process_backlog函数,故转入process_backlog()函数,再调用netif_receive_skb()根据包的类型送入上一层的协议栈。
四、一点题外话——为什么NAPI更好一些?
粗略分起来,一共有四种接收数据包的方法:
1 轮询
2 中断
3 在中断期间处理多帧netif_rx
4 NAPI
其中轮询和中断就是一般操作系统或计算机原理中介绍的方法,他们的优缺点这里就不再重复。
但是网络设备有其自己的特点,它接收数据包的频率有时很高,有时有比较低,这样有必要将中断和轮询的方法结合起来,
当接收数据包频率很高时,主要采用轮询方法,这样可以避免过多的中断影响系统的性能;
当接收数据包频率很低时,主要采用中断方法,这样可以避免轮询消耗太多CPU时间。
可以说是各取其长,各避其短,这是netif_rx和NAPI这两种方法之所以比单纯使用轮询或中断好的原因。
但是为什么NAPI会比netif_rx更好一些呢?
关键在关中断持续的时间长短上。
注意这里的前提是Linux中中断处理函数是非抢占的,不可重入的,也就是说在处理一个中断时,会将本地CPU的所有中断全部关掉。
使用netif_rx方法时,连续处理多个包的操作(操作主要是链入input_pkt_queue队列)是在中断处理函数中进行的,在这期间CPU的中断是关闭的。
而使用NAPI时,连续处理多个包的操作也是在中断处理函数中进行的,但是此时进行的操作很简单,就是将该网络设备加入softnet_data->poll_list中,
并且即使对多个包也只需进行一次。而具体的接收包的操作是在net_rx_action()-->netif_receive_skb()中进行的,此时的执行上下文是软中断,而CPU的中断此时是开启的。所以说NAPI关闭CPU中断持续的时间比netif_rx方法要少一些。
如有不对的地方,请大家指正。 |
评分
-
查看全部评分
|