LINUX协议栈详解 数据包接收


NAPI介绍

NAPI混合了中断和轮询,在高负载情况下可以有效减少CPU的负载,从而提高性能,从这一点可以认识到一种优化的方式就是降低CPU负载,例如DMZ,就是另一个例子,将更多的工作交给其他硬件来执行,而不是CPU,从而达到减轻CPU压力。

NAPI的原理:当内核正在处理接收数据包中断,它将继续轮询处理网卡上的数据包,而不是每次处理完成后就退出,省去了每次中断通知的时间。

NAPI的优势:

         减少CPU负载,因为更少的中断处理。

         更公平的处理每一个网卡设备。

 

Linux数据接收是通过软中断来实现的,下面是注册的两个软中断:

         open_softirq(NET_TX_SOFTIRQ,net_tx_action);

         open_softirq(NET_RX_SOFTIRQ,net_rx_action);

 

static void net_rx_action(structsoftirq_action *h)

         在这之前先看一个全局变量,softnet_data,这是一个per-CPU变量,定义如下:

DEFINE_PER_CPU_ALIGNED(structsoftnet_data, softnet_data);

                   poll_list所有需要遍历的网卡,后面将介绍如何产生的。

         在net_rx_action将会便利poll_list中的网卡,然后调用其poll函数来处理。

         这里忽略了细节,例如如何控制轮询遍历。

 

前面简单的说了是通过软中断来实现调用net_rx_action的,浅尝辄止和刨根究底是两种可以合并的学习方式,浅尝辄止可以快速的了解整件事情,但是刨根究底则可以学习到更多本质的东西,是不是有点类似于中断和轮询?

 

看e1000e,这是intel的网卡,pci架构的网卡看代码要从probe看起,因为这里才是真正发现硬件并进行处理的地方。

static int __devinit e1000_probe(structpci_dev *pdev,

                                      const struct pci_device_id *ent)

关注netif_napi_add(netdev, &adapter->napi, e1000_clean, 64);

void netif_napi_add(struct net_device *dev,struct napi_struct *napi,

                       int (*poll)(struct napi_struct *, int), intweight)

就是给napi结构体赋值,关注e1000_clean这个就是poll函数,我们上面提到的,后面还要继续深入分析。

 

接着就是看中断处理函数,中断处理函数一般命名都是intr,所以用sourceinsight搜索一下intr就出来了。

static irqreturn_t e1000_intr(int irq, void*data)

跳过前面的检查,看重要的部分:

if(napi_schedule_prep(&adapter->napi)) {

                   adapter->total_tx_bytes= 0;

                   adapter->total_tx_packets= 0;

                   adapter->total_rx_bytes= 0;

                   adapter->total_rx_packets= 0;

                   __napi_schedule(&adapter->napi);

}

__napi_schedule负责将napi加入到softnet_data的列表,现在我们就知道为什么在软中断处理函数net_rx_action中进行遍历了。

list_add_tail(&napi->poll_list,&sd->poll_list);

__raise_softirq_irqoff(NET_RX_SOFTIRQ);

从这里我们可以看到,只要是中断产生在同一个cpu,那么对应的数据包就是在一个cpu上的,且按照NAPI的设计,大多数数据包都应该是在同一个cpu上,因为产生一次中断就进行多次读取,如果没有读完的,是否可能在不同cpu上面呢?通过cpu绑定可以实现具体网卡到哪个cpu上处理。

http://www.ibm.com/developerworks/cn/linux/l-cn-linuxkernelint/

同一个会话呢?我们可以断定的是去包和回包在不同cpu上处理是非常有可能的,除非利用绑定cpu处理,但是绑定cpu处理是非常不灵活的,例如一个8核的机器只跑一对网卡。

 

有点乱?从上面的代码我们可以简单的说:除非硬件中断发生在同一个cpu上面,否则不可能在同一个cpu上进行处理数据,这就要求硬件支持,或者是利用cpu的亲和力。

更详细的讨论http://bbs.chinaunix.net/thread-1979496-1-1.html

 

E1000e的poll函数是e1000_clean,驱动本身搞的比较复杂了,先不考虑太多,最后调用了netif_receive_skb,用于接收数据进行处理,往后就是网络层的事情,当然或许是vlan和网桥,无所谓啦。这里我们可以得出结论,我们处理完数据包可以直接调用netif_receive_skb来提交给内核继续处理,如果是调用netif_rx则多一次软中断处理。

 

再讨论一下旧的api,新旧之间的区别在于旧的api是在中断中直接读取数据然后交给netif_rx,而新的依赖于poll函数,poll函数负责读取数据。

分析一下netif_rx函数。同样也依赖于softnet_data,只是将数据包挂到input_pkt_queue中,然后一样softirq来处理,也就是说和我们上面分析一样,那么它的poll函数是什么呢?

我们看net_dev_init初始化的时候给初始化为process_backlog,也同样是调用内部函数__netif_receive_skb。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值