深入理解Linux网络技术内幕——帧的接收与传输

帧的接收

NAPI与netif_rx(非NAPI)

Linux内核获取网络帧到达通知的方式有两中:中断和轮询。(中断值设备向内核发出中断,轮询指linux内核主动轮询设备)
在早起的linux内核中,网络帧主要以中断的方式通知linux内核帧的到达。这是非NAPI方式。
现在的操作系统中,linux内核使用NAPI方式, 获取帧到达的消息。NAPI混合使用了中断和轮询。
netif_rx(非NAPI):
每一个帧接收完毕时,设备向内核发送一个中断。(在低流量负载的情况下,这种方式对比轮询优势明显。但是由于中断处理需要一些额外的开销,在高流量负载下,由于要频繁处理中断,因此会造成不少的网络开销。)
NAPI:
在低流量负载下,使用轮询方式通知内核网络帧接收完毕。在高流量负载下使用轮询方式。
在NAPI机制下:
对于设备来说,当一个帧接收完毕,进入设备输入队列,这时驱动程序不会立即向内核发送中断,而是先判断内核是否还在处理设备输入队列中的帧,如果是,则先不发送中断,内核自然会处理设备输入队列中的帧。
对于内核来说,从接到一个中断开始处理设备输入队列中的网络帧,处理完一个网络帧时,内核不会先去做其他事,而是先轮询设备输入队列,看看还有没有其他帧要处理。如果有,则循环处理;如果设备输入队列中的帧都处理完了,就可以去做其他事,等待设备驱动程序下一次发来中断。

NAIP机制实现

我们知道,每个cpu都有这样的一个队列,它主要是用来存储incoming frame。由于他是每个cpu都有一个队列,因此在不同的cpu之间我们就不要任何锁来控制并发的处理这个帧队列。我们在操作系统层要取得帧数据,都是通过这个数据来读取。 
struct softnet_data
{
    struct Qdisc        *output_queue;      //qdisc是queueing discipline的简写,也就是排队规则,即qos.这里也就是输出帧的控制。
    struct sk_buff_head input_pkt_queue;    //当输入帧被驱动取得之前,就保存在这个队列里,(不适用与napi驱动,napi有自己的私有队列)
    struct list_head    poll_list;          //表示有输入帧待处理的设备链表。 
    struct sk_buff      *completion_queue;  //表示已经成功被传递出的帧的链表。

    struct napi_struct  backlog;            //用来兼容非napi的驱动。                                                                    
};
napi在结构体softnet_data中使用了struct list_head    poll_list;     和struct napi_struct  backlog;            //用来兼容非napi的驱动。 
struct napi_struct {
    /* The poll_list must only be managed by the entity which
     * changes the state of the NAPI_STATE_SCHED bit.  This means
     * whoever atomically sets that bit can add this napi_struct
     * to the per-cpu poll_list, and whoever clears that bit
     * can remove from the list right before clearing the bit.
     */
    struct list_head    poll_list; //设备列表(在入口队列有新帧等待处理的设备)

    unsigned long       state;
    int         weight;
    int         (*poll)(struct napi_struct *, int); //把缓冲区从设备的入口队列(NAPI私有队列,softnet_data->input_pkt_queue不同)退出
#ifdef CONFIG_NETPOLL
    spinlock_t      poll_lock;
    int         poll_owner;
#endif

    unsigned int        gro_count;

    struct net_device   *dev;
    struct list_head    dev_list;
    struct sk_buff      *gro_list;
    struct sk_buff      *skb;                                                                                                           
};
我们需要注意poll_list成员,它表示在输入队列中有帧需要处理的设备链表,这是用来支持轮询的。这个链表中的设备都处于关中断的状态。内核在适当时间进行轮询处理这些设备接收到的帧。






















郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。