Linux内核数据包的发送传输

本文主要讲解了Linux内核数据包的传输流程,使用的内核的版本是2.6.32.27


为了方便理解,本文采用整体流程图加伪代码的方式

从内核高层面上梳理了二层数据包发送传输的流程,

希望可以对大家有所帮助。

阅读本文章假设大家对C语言有了一定的了解


整体流程如下



数据包的传输可以分为两种:

一种是正常的传输流程,即一般网卡的发送流程用于一般的;另一种是基于软中断的发送流程,这种发送流程用于CPU冲突时候的重新调度和QOS的流量整形


正常的传输流程伪代码如下:

[cpp]  view plain copy
  1. /*正常传输流程*/  
  2.   
  3. /*高层协议dev_queue_xmit(skb)发送数据报文*/  
  4.   
  5. static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t total_len)  
  6. {  
  7. `   skb = sock_wmalloc(sk, total_len + dev->hard_header_len + 32, 0, GFP_KERNEL);  
  8.     if (!skb) {  
  9.         error = -ENOMEM;  
  10.         goto end;  
  11.     }  
  12.       
  13.     //......  
  14.   
  15.     dev_queue_xmit(skb);  
  16.   
  17. end:  
  18.     release_sock(sk);  
  19.     return error;  
  20. }  
  21.   
  22.   
  23.   
  24. /*正常传输流程*/  
  25. int dev_queue_xmit(struct sk_buff *skb)  
  26. {  
  27.     struct net_device *dev = skb->dev; /*得到网络设备*/  
  28.     struct Qdisc *q;  
  29.   
  30.     /*选取net_device中的_tx队列*/  
  31.     txq = dev_pick_tx(dev, skb);  
  32.     {  
  33.         txq = &dev->_tx[index]  
  34.     }  
  35.       
  36.     /*取得Qdisc*/  
  37.     q = rcu_dereference(txq->qdisc);  
  38.     {  
  39.         q = dev->_tx[index]->qdisc;  
  40.     }  
  41.   
  42.     if (q->enqueue)   
  43.     {  
  44.         rc = __dev_xmit_skb(skb, q, dev, txq);  
  45.         {  
  46.             rc = qdisc_enqueue_root(skb, q);  
  47.                 {  
  48.                 struct Qdisc *sch = q;  
  49.                   
  50.                 /*将数据报文至于设备发送队列中, 一般采用FIFO算法进行发送,标记数据包发送准备完毕*/  
  51.                 qdisc_enqueue(skb, sch) ;  
  52.                 {                     
  53.                     sch->enqueue(skb, sch);    
  54.                 }  
  55.             }  
  56.               
  57.             /*触发对发送准备完毕的数据包的进一步处理*/  
  58.             qdisc_run(q);             
  59.         }  
  60.         goto out;  
  61.     }  
  62.   
  63.     /*如果没有定义队列的管理方法,即q->enqueue == NULL,直接调用dev_hard_start_xmit发送数据包 
  64.      * 一般是逻辑网络设备,如环回口或者隧道口 
  65.      */  
  66.     if (dev->flags & IFF_UP) {  
  67.           
  68.         dev_hard_start_xmit(skb, dev, txq)  
  69.         {  
  70.             const struct net_device_ops *ops = dev->netdev_ops;  
  71.             int rc;  
  72.             rc = ops->ndo_start_xmit(skb, dev); /*硬件发送数据包*/            
  73.         }  
  74.     }  
  75.       
  76. out_kfree_skb:  
  77.     kfree_skb(skb);  
  78.     return rc;  
  79. out:  
  80.     rcu_read_unlock_bh();  
  81.     return rc;  
  82. }  
  83.   
  84.   
  85. static inline void qdisc_run(struct Qdisc *q)  
  86. {  
  87.     /*反复调用qdisc_restart直到返回空值(队列中不在含有数据包) 
  88.      *或者网络不在接受任何数据包为止*/  
  89.     __qdisc_run(q);  
  90.     {  
  91.         while (qdisc_restart(q))   
  92.         {  
  93.             if (need_resched() || jiffies != start_time) {  
  94.                 __netif_schedule(q);  
  95.                 break;  
  96.             }  
  97.         }  
  98.     }  
  99. }  
  100.   
  101. /* 
  102.  * 从设备队列中获取下一个数据包并将其发出, 
  103.  * 一般而言只有一个队列并按照FIFO原则运作 
  104.  * 可以通过Qdisc对它们赋予某种策略 
  105.  */  
  106. static inline int qdisc_restart(struct Qdisc *q)  
  107. {  
  108.     struct netdev_queue *txq;  
  109.     struct net_device *dev;  
  110.     spinlock_t *root_lock;  
  111.     struct sk_buff *skb;  
  112.       
  113.     /*请求下一个数据包*/  
  114.     skb = dequeue_skb(q);  
  115.     {  
  116.         skb = q->dequeue(q);  
  117.     }  
  118.           
  119.     /*没有stop的情况下,发送数据包*/  
  120.     sch_direct_xmit(skb, q, dev, txq, root_lock);  
  121.     {  
  122.         /*试图获取txq->_xmit_loc的锁,并记录CPU到txq->xmit_lock_owner中*/  
  123.         HARD_TX_LOCK(dev, txq, smp_processor_id());  
  124.         {  
  125.             __netif_tx_lock(txq, cpu);  
  126.             {  
  127.                 spin_lock(&txq->_xmit_lock);  
  128.                 txq->xmit_lock_owner = cpu;  
  129.             }  
  130.         }  
  131.           
  132.         if (!netif_tx_queue_stopped(txq) &&  !netif_tx_queue_frozen(txq))  
  133.             ret = dev_hard_start_xmit(skb, dev, txq);         
  134.             {  
  135.                 const struct net_device_ops *ops = dev->netdev_ops;  
  136.                 int rc;  
  137.                 rc = ops->ndo_start_xmit(skb, dev); /*硬件发送数据包*/                
  138.             }  
  139.           
  140.         HARD_TX_UNLOCK(dev, txq);  
  141.   
  142.         switch (ret) {  
  143.             case NETDEV_TX_OK:  
  144.                 /* Driver sent out skb successfully */  
  145.                 ret = qdisc_qlen(q);  
  146.                 break;  
  147.           
  148.             case NETDEV_TX_LOCKED:  
  149.                 /* Driver try lock failed */  
  150.                 ret = handle_dev_cpu_collision(skb, txq, q);  
  151.                 {  
  152.                     if (unlikely(dev_queue->xmit_lock_owner == smp_processor_id())) {  
  153.                         /*同一个CPU被锁住了,说明存在处理器死循环,该网络适配器的数据包发送因为某种原因失败了, 
  154.                          * 并做出了重新传输一个数据包的尝试,这里只做简单丢弃并立即从qdisc_run返回以完成第一个传输进程*/  
  155.                          kfree_skb(skb);  
  156.                          printk(KERN_WARNING "Dead loop on netdevice %s, fix it urgently!\n", dev_queue->dev->name);  
  157.                     } else {  
  158.                         /*其他CPU正在发送其他的数据包,使用requeue()进行重新调度,     
  159.                          *在netif_schedule()中激活NET_TX_SOFTIRQ以便再次触发发送*/  
  160.                         ret = dev_requeue_skb(skb, q);  
  161.                         {  
  162.                             __netif_schedule(q);  
  163.                             {  
  164.                                 __netif_reschedule(q)  
  165.                                 {  
  166.                                     raise_softirq_irqoff(NET_TX_SOFTIRQ);  
  167.                                 }  
  168.                             }                             
  169.                         }  
  170.                     }                     
  171.                 }  
  172.                 break;            
  173.         }                     
  174.     }  
  175. }  


基于软中断的发送流程伪代码如下,要理解这部分内容,需要大家对中断下半部的软中断机制和作用做一些了解,以后的博客文章中会进行讲解

如果需要QOS的流量整形,那么内核启动定时器,定时调用__netif_schedule()来触发软中断,达到定时定流量的作用

[cpp]  view plain copy
  1. /*NETIF_TX_SOFTIRQ上的传输,通过__netif_schedule触发 
  2.  * 使用的场景是 
  3.  * 一:数据报文发送失败 
  4.  * 二:在QOS中进行流量整形,按照一定的时间定时启动定时器,进行数据定速率发送 
  5.  *对__netif_schedule的调用在下一次CPU进行调度的时候被触发 
  6.  */  
  7. void __netif_schedule(struct Qdisc *q)  
  8. {  
  9.     __netif_reschedule(q);  
  10.     {  
  11.         raise_softirq_irqoff(NET_TX_SOFTIRQ);  
  12.     }  
  13. }  
  14.   
  15. /*  
  16.  * 设定NET_TX_SOFTIRQ 对应的处理句柄是net_tx_action 
  17.  */  
  18. static int __init net_dev_init(void)  
  19. {  
  20.     open_softirq(NET_TX_SOFTIRQ, net_tx_action);  
  21. }  
  22.   
  23.   
  24. /*调用qdisc_run启动网络设备数据包的传输*/  
  25. static void net_tx_action(struct softirq_action *h)  
  26. {  
  27.     while (head) {  
  28.         qdisc_run(q);  
  29.     }  
  30. }  


通过上面的分析,
我们可以清晰的了解到Linux上对数据报文的一般发送传输的流程。
希望大家批评指正

original  link:http://blog.csdn.net/eric_liufeng/article/details/10252857



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值