打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Linux内核二层数据包接收流程

本文主要讲解了Linux内核二层数据包接收流程,使用的内核的版本是2.6.32.27

为了方便理解,本文采用整体流程图加伪代码的方式从内核高层面上梳理了二层数据包接收的流程,希望可以对大家有所帮助。阅读本文章假设大家对C语言有了一定的了解

整体流程如下:



数据报文接收流程伪代码分析如下

  1. /*在基于中断收发报文的网卡设备驱动中, 
  2.  * 当有数据报文进来的时候,使用net_interrupt()进行中断触发 
  3.   *如 isa-skeleton设备驱动中*/  
  4.   
  5. static int __init netcard_probe1(struct net_device *dev, int ioaddr)  
  6. {  
  7.     /*注册net_interrupt为中断处理历程*/  
  8.     int irqval = request_irq(dev->irq, &net_interrupt, 0, cardname, dev);  
  9.     if (irqval) {  
  10.         printk("%s: unable to get IRQ %d (irqval=%d).\n",  
  11.                dev->name, dev->irq, irqval);  
  12.         goto out;  
  13.     }  
  14.       
  15.     //......   
  16.     return err;  
  17. }  
  18.   
  19.   
  20. static irqreturn_t net_interrupt(int irq, void *dev_id)  
  21. {  
  22.     //......   
  23.     if (status & RX_INTR) {  
  24.         /* Got a packet(s). */  
  25.         /*使用NET_RX实现进行发送数据报文*/  
  26.         net_rx(dev);  
  27.     }  
  28. #if TX_RING   
  29.     if (status & TX_INTR) {  
  30.         /* Transmit complete. */  
  31.         net_tx(dev);  
  32.         np->stats.tx_packets++;  
  33.         netif_wake_queue(dev);  
  34.     }  
  35. #endif   
  36.       
  37.     return IRQ_RETVAL(handled);  
  38. }  
  39.   
  40.   
  41.   
  42. /* We have a good packet(s), get it/them out of the buffers. */  
  43. static void  
  44. net_rx(struct net_device *dev)  
  45. {  
  46.   
  47.     /*使用dev_alloc_skb来分配skb,并把数据报文复制到skb中*/  
  48.     skb = dev_alloc_skb(pkt_len);  
  49.     if (skb == NULL) {  
  50.         //......   
  51.     }  
  52.     skb->dev = dev;  
  53.       
  54.     /* 'skb->data' points to the start of sk_buff data area. */  
  55.     memcpy(skb_put(skb,pkt_len), (void*)dev->rmem_start, pkt_len);  
  56.     /* or */  
  57.     insw(ioaddr, skb->data, (pkt_len + 1) >> 1);  
  58.       
  59.     /*调用netif_rx将数据报文交给上层处理*/     
  60.     netif_rx(skb);  
  61.   
  62.     return;  
  63. }  
  64.   
  65.   
  66.   
  67.   
  68. DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, };  
  69.   
  70.   
  71.   
  72. /*完成中断处理过程*/  
  73. int netif_rx(struct sk_buff *skb)  
  74. {  
  75.     struct softnet_data *queue;  
  76.     unsigned long flags;  
  77.   
  78.     /*取得当前时间存储在skb->tstamp中*/  
  79.     if (!skb->tstamp.tv64)  
  80.         net_timestamp(skb);  
  81.   
  82.     /* 
  83.      * The code is rearranged so that the path is the most 
  84.      * short when CPU is congested, but is still operating. 
  85.      */  
  86.     local_irq_save(flags);  
  87.     /*取得当前CPU的softnet_data,*/  
  88.     queue = &__get_cpu_var(softnet_data);  
  89.   
  90.     if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {  
  91.         if (queue->input_pkt_queue.qlen) {  
  92. enqueue:  
  93.             /*将SKB放入到softnet_data[CPU].input_pkt_queue中 
  94.              *一旦数据包出于该对列,中断就处理完成了*/  
  95.             __skb_queue_tail(&queue->input_pkt_queue, skb);  
  96.             local_irq_restore(flags);  
  97.             return NET_RX_SUCCESS;  
  98.         }  
  99.   
  100.         /*如果queue->input_pkt_queue.qlen中已经有上次的数据包, 
  101.          *发起NET_RX_SOFTIRQ软中断,由软中断的处理函数net_rx_action进行发送*/  
  102.         napi_schedule(&queue->backlog);  
  103.         {  
  104.             __napi_schedule(n)  
  105.             {  
  106.                 list_add_tail(&n->poll_list, &__get_cpu_var(softnet_data).poll_list);  
  107.                 __raise_softirq_irqoff(NET_RX_SOFTIRQ);  
  108.             }  
  109.         }  
  110.         goto enqueue;  
  111.     }  
  112.   
  113.     __get_cpu_var(netdev_rx_stat).dropped++;  
  114.     local_irq_restore(flags);  
  115.   
  116.     kfree_skb(skb);  
  117.     return NET_RX_DROP;  
  118. }  
  119.   
  120. /*注册软中断NET_RX_SOFTIRQ的处理函数为net_rx_action*/  
  121. static int __init net_dev_init(void)  
  122. {  
  123.     open_softirq(NET_RX_SOFTIRQ, net_rx_action);  
  124. }  
  125.   
  126.   
  127.   
  128.   
  129. /*必须要有NAPI的POLL么?没有NAPI的POLL回调怎么送往协议栈*/  
  130. static void net_rx_action(struct softirq_action *h)  
  131. {  
  132.     struct list_head *list = &__get_cpu_var(softnet_data).poll_list;  
  133.           
  134.     while (!list_empty(list)) {  
  135.         struct napi_struct *n;  
  136.   
  137.         n = list_entry(list->next, struct napi_struct, poll_list);  
  138.   
  139.         /*调用每款驱动对NAPI注册的POLL函数,如pcnet32_poll 
  140.           *在POLL函数的RX部分里面,会调用netif_receive_skb将 
  141.           *数据包交给协议栈处理*/  
  142.         work = n->poll(n, weight);  
  143.   
  144.         WARN_ON_ONCE(work > weight);  
  145.   
  146.         budget -= work;  
  147.   
  148.         local_irq_disable();  
  149.   
  150.         /* Drivers must not modify the NAPI state if they 
  151.          * consume the entire weight.  In such cases this code 
  152.          * still "owns" the NAPI instance and therefore can 
  153.          * move the instance around on the list at-will. 
  154.          */  
  155.         if (unlikely(work == weight)) {  
  156.             if (unlikely(napi_disable_pending(n))) {  
  157.                 local_irq_enable();  
  158.                 napi_complete(n);  
  159.                 local_irq_disable();  
  160.             } else  
  161.                 list_move_tail(&n->poll_list, list);  
  162.         }  
  163.   
  164.         netpoll_poll_unlock(have);  
  165.     }  
  166. out:  
  167.     local_irq_enable();  
  168.   
  169. #ifdef CONFIG_NET_DMA   
  170.     /* 
  171.      * There may not be any more sk_buffs coming right now, so push 
  172.      * any pending DMA copies to hardware 
  173.      */  
  174.     dma_issue_pending_all();  
  175. #endif   
  176.   
  177.     return;  
  178.   
  179. softnet_break:  
  180.     __get_cpu_var(netdev_rx_stat).time_squeeze++;  
  181.     __raise_softirq_irqoff(NET_RX_SOFTIRQ);  
  182.     goto out;  
  183. }  
  184.   
  185.   
  186.   
  187. /*在RX部分里,会调用*/  
  188. static int pcnet32_poll(struct napi_struct *napi, int budget)  
  189. {  
  190.     /*RX部分*/  
  191.     work_done = pcnet32_rx(dev, budget);  
  192.     {  
  193.         pcnet32_rx_entry()  
  194.         {  
  195.             netif_receive_skb(skb);  
  196.         }  
  197.     }  
  198.   
  199.     /*TX部分*/  
  200.     pcnet32_tx(dev);  
  201.       
  202.     return work_done;  
  203. }  
  204.   
  205.   
  206. int netif_receive_skb(struct sk_buff *skb)  
  207. {  
  208.     struct packet_type *ptype, *pt_prev;  
  209.     struct net_device *orig_dev;  
  210.   
  211.   
  212.     pt_prev = NULL;  
  213.       
  214.     /*看看ptype_all中有没有相应的协议进行相应的协议处理,一般这里没有注册的协议,但是可以加入我们的分析钩子函数*/  
  215.     list_for_each_entry_rcu(ptype, &ptype_all, list) {  
  216.         if (ptype->dev == null_or_orig || ptype->dev == skb->dev ||  ptype->dev == orig_dev) {  
  217.             if (pt_prev)  
  218.                 /*协议分发函数*/  
  219.                 ret = deliver_skb(skb, pt_prev, orig_dev);  
  220.             pt_prev = ptype;  
  221.         }  
  222.     }  
  223.   
  224.     /*处理网桥配置的数据报文*/  
  225.     skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);  
  226.     if (!skb)  
  227.         goto out;  
  228.   
  229.     skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);  
  230.     if (!skb)  
  231.         goto out;  
  232.   
  233.     /*对ptype_base表中的协议进行遍历,如果找到对应的协议,送往对应的协议栈进行处理*/  
  234.     type = skb->protocol;  
  235.     list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {  
  236.         if (ptype->type == type &&  (ptype->dev == null_or_orig || ptype->dev == skb->dev ||  ptype->dev == orig_dev)) {  
  237.             if (pt_prev)  
  238.                 /*协议分发函数*/  
  239.                 ret = deliver_skb(skb, pt_prev, orig_dev);  
  240.             pt_prev = ptype;  
  241.         }  
  242.     }  
  243.   
  244.     if (pt_prev) {  
  245.         ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);  
  246.     }   
  247.     else  
  248.      {  
  249.         kfree_skb(skb);  
  250.         ret = NET_RX_DROP;  
  251.     }  
  252.   
  253. out:  
  254.     rcu_read_unlock();  
  255.     return ret;  
  256. }  
  257.   
  258. /*调用相应协议的func进行处理*/  
  259. static inline int deliver_skb(struct sk_buff *skb, struct packet_type *pt_prev, struct net_device *orig_dev)  
  260. {  
  261.     return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);  
  262. }  
  263.   
  264.   
  265. /*在af_inet.c文件中对IPV4的处理注册为ip_rcv,所以IPV4对应的FUNC为ip_rcv*/  
  266. static struct packet_type ip_packet_type __read_mostly = {  
  267.     .type = cpu_to_be16(ETH_P_IP),  
  268.     .func = ip_rcv,  
  269. };  
  270.   
  271.   
  272. /* 
  273.  *  Main IP Receive routine. 
  274.  */  
  275. int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)  
  276. {  
  277.     //......   
  278. }  

从分析的伪代码可以看出,数据包接受的时候,可以基于2中方式触发:1 收发中断 2 NAPI的轮询机制

这里没有分析驱动代码对硬件的操作,这部分代码在设备驱动程序中,本文举例了2款网卡代码 pcnet32 和 isa-skeleton,当硬件接受完毕之后就进入dev层面进行内核的总体调度,这也是上面伪代码分析的重点。当软中断被触发后,内核会回调每款驱动注册的poll函数钩子,进而进行首发处理,

在POLL的RX阶段中,会对报文进行分类送往不同的协议进行处理,这里举例ipv4的处理入口ip_rcv(),但是没有深入进去,后面的文章中将进行细致讲解。最后在POLL的TX阶段里面,对已经处理好的发送队列中的数据进行发送,在该阶段中会将数据报文映射到PCI DMA的发送ring中,并且调用netif_wake_queue(dev),来通知高层调用device注册的 ndo_hard_start_xmit函数进行硬件发送,后面发送的处理流程请参考我的上一篇博客

<<Linux内核数据包的发送传输>>(http://blog.csdn.net/eric_liufeng/article/details/10252857)



本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
数据报的接收过程详解---从网卡到L3层(非NAPI,即接收数据采用中断方式) - 技术文...
Android socket创建、绑定流程分析(二)
TCP/IP学习(28)
Linux内核napi机制分析
NAPI 技术在 Linux 网络驱动上的应用和完善
服务器不丢包背后的兵法:Redis在万亿级日访问量下的中断优化
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服