数据报的接收过程详解---从网卡到L3层(非NAPI,即接收数据采用中断方式)

这篇博客详细介绍了在Linux内核2.4版本中,非NAPI(Non-Atomic Input/Output)环境下,数据报从网卡接收并传递到网络层的过程。首先,网卡接收数据包后通过中断通知内核,接着中断处理程序将数据包拷贝到sk_buff并更新状态,调用netif_rx将数据包送往上层。netif_rx主要任务是将数据包放入输入队列,并触发软中断NET_RX_SOFTIRQ。随后,net_rx_action作为软中断处理函数,处理数据包或调度设备的poll函数。最后,数据包经过process_backlog和netif_receive_skb处理,根据协议类型传递到相应协议层,如IP协议的ip_rcv函数。博客作者强调了netif_receive_skb在数据包流向中的关键作用,并探讨了可能的处理点,如嗅探器、分流器和桥接。文章适合对网络协议栈感兴趣的读者深入学习。
摘要由CSDN通过智能技术生成

 
     刚来实验室的时候主要看的就是数据报在协议栈的具体传输过程,当时有过记录,但是很凌乱,最近又回头看了看相关知识和内核源代码,算是理清了思路,特整理在此.本篇笔记写的是2.4中数据报的接收过程,从网卡到网络层的具体路线,2.4中大部分网卡采用的是中断的方式接收数据(好像是从2.5以后开始支持 NAPI的,不太确定),本篇笔记总结的是非NAPI,即采用中断接受数据的路线.ok,开始进入主题.

 

当网卡接收到一个数据报之后,产生一个中断通知内核,然后内核会调用相关的中断处理函数.一般,中断处理程序做如下工作:


1,把数据报拷贝到一个sk_buff中.
2,初始化sk_buff中的一些成员变量,为以后传输到上层用,特别是skb->protocol,标示了上层的具体协议,后面会调用相应的接收函数.
3,更新网卡的状态.
4,调用netif_rx将数据报送往上层(采用NAPI时,此处将调用netif_rx_schdule).

 

看netif_rx源代码之前首先要了解一下softnet_data结构.此结构是基于cpu的而不是device,即每个cpu对应一个softnet_data.
struct softnet_data
{           
        /*throttle用于拥塞控制,当拥塞时被设置,此后来的数据包都被丢弃*/
        int throttle;
        /*netif_rx返回的拥塞级别*/
        int cng_level;
        int avg_blog;
        /*input_pkt_queue是skb的队列,接收到的skb全都进入到此队列等待后续处理*/
        struct sk_buff_head input_pkt_queue;
        /*poll_list是一个双向链表,链表的成员是有接收数据等待处理的device*/
        struct list_head poll_list;
        /*net_device链表,成员为有数据报要发送的device*/
        struct net_device *output_queue;
        /*完成发送的数据包等待释放的队列*/
        struct sk_buff *completion_queue;
        /*注意,backlog_dev不是一个指针,而是一个net_device实体,代表了调用net_rx_action时的device*/
        struct net_device backlog_dev;
};

ok,了解了softnet_data结构体后接下来看netif_rx的源代码.

/*
*主要工作:
*1,初始化sk_buff的一些域值,比如数据报接收的时间截.
*2,把接收到的数据报入input_pkt_queue接收队列,并且通知内核然后触发相应的软中断,即NET_RX_SOFTIRQ.
* 2.1:当队列为空时,调用netif_rx_schedule,触发软中断.
* 2.2:当队列非空时,直接将数据报入队列,因为此时已经调用了软中断,所以无需再调用.
*3,更新相关状态信息.
*执行完后,流程来到net_rx_action
*/
int netif_rx(struct sk_buff *skb)
{
    int this_cpu = smp_processor_id();
    struct softnet_data *queue;
    unsigned long flags;
    //如果接收到的数据包时间截未设置,设置时间截
    if (skb->stamp.tv_sec == 0)
        do_gettimeofday(&skb->stamp);
    queue = &softnet_data[this_cpu];
    local_irq_save(flags); //disable irqs on local cpu
    netdev_rx_stat[this_cpu].total++;
    if (queue->input_pkt_queue.qlen = netdev_max_backlog) {
        if (queue->input_pkt_queue.qlen) {
            if (queue->throttle)
                goto drop;
enqueue:
            dev_hold(skb->dev); //即automic_inc(&(dev)->refcnt)累加设备引入计数器
            __skb_queue_tail(&queue->input_pkt_queue,skb); //把skb入input_pkt_queue队列,此处只是指针的指向,而不是数据报的拷贝,目的是节省时间.
            local_irq_restore(flags); //eable irqs on local cpu
#ifndef OFFLINE_SAMPLE
            get_sample_stats(this_cpu);
#endif

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值