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

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

整体流程如下

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

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

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

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

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

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

  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. /*调用qdisc_run启动网络设备数据包的传输*/
  24. static void net_tx_action(struct softirq_action *h)
  25. {
  26. while (head) {
  27. qdisc_run(q);
  28. }
  29. }

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

Linux内核数据包的发送传输的更多相关文章

  1. [转]Linux网络 - 数据包的发送过程

    转, 原文:https://segmentfault.com/a/1190000008926093 -------------------------------------------------- ...

  2. linux内核数据包转发流程(三)网卡帧接收分析

    [版权声明:转载请保留出处:blog.csdn.net/gentleliu.邮箱:shallnew*163.com] 每一个cpu都有队列来处理接收到的帧,都有其数据结构来处理入口和出口流量,因此,不 ...

  3. linux内核数据包转发流程(二):中断

    [版权声明:转载请保留出处:blog.csdn.net/gentleliu.邮箱:shallnew*163.com] 内核在处理2层数据包之前,必须先处理中断系统.设立中断系统,才有可能每秒处理成千的 ...

  4. linux内核数据包转发流程(一):网络设备驱动

    [版权声明:转载请保留出处:blog.csdn.net/gentleliu.邮箱:shallnew*163.com] 网卡驱动为每一个新的接口在一个全局的网络设备列表里插入一个数据结构.每一个接口由一 ...

  5. Linux内核源代码解析——用户发送数据包的起源之sendto

    本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/10162853 Jack:我想知道用户如何把数据发送到 ...

  6. Linux网络数据包的揭秘以及常见的调优方式总结

    https://mp.weixin.qq.com/s/boRWlx1R7TX0NLuI2sZBfQ 作为业务 SRE,我们所运维的业务,常常以 Linux+TCP/UDP daemon 的形式对外提供 ...

  7. virtio-netdev 数据包的发送

    在前面几文中已经大体介绍了virtio的重要组成,包含virtio net设备的创建,vring的创建,与virtio设备的交互方式,我们就从网络数据包的发送角度来看下virtio的详细使用流程. [ ...

  8. UDP发送的数据 以数据包形式发送

    UDP发送的数据 以数据包形式发送

  9. vlan linux内核数据流程

    转:http://blog.sina.com.cn/s/blog_62bbc49c0100fs0n.html 一.前言 前几天做协议划分vlan的时候看了一些linux内核,了解不深,整理了下vlan ...

随机推荐

  1. Java编程 的动态性,第 2部分: 引入反射--转载

    在“ Java编程的动态性,第1部分,”我为您介绍了Java编程类和类装入.该篇文章介绍了一些Java二进制类格式的相关信息.这个月我将阐述使用Java反射API来在运行时接入和使用一些相同信息的基础 ...

  2. 通过模拟器和ida搭建Android动态调试环境的问题

    这几天在学Android的native层逆向.在按照教程用ida搭建动态调试环境时,第一步是把android_server 放到手机里执行,但是在手机里可以,在genymotion模拟器上就提示 no ...

  3. [转] 关于SIGPIPE导致的程序退出

    PS: 如果服务器程序不忽略SIGPIPE,在某些时候TCP writer收到这个信号,会导致进程退出 The rule that applies is: When a process writes ...

  4. Java基础知识强化26:Object类之hashCode()方法、getClass()方法

    1. Object类的hashCode()方法,如下: public  int  hashCode():返回该对象的哈希码值,这个值和地址值有关,但是不是实际地址值(哈希码值是根据实际地址值转化过来的 ...

  5. thinkphp3.2.3 成功对接支付宝接口

    一.首先下载支付宝官方接口,下载地址: https://b.alipay.com/order/productDetail.htm?productId=2012111200373124&tabI ...

  6. python对象(腌制)

    python的内置对象类型主要有数字,字符串,列表,元祖,字典,集合等等,在python中,一切皆为对象 #腌制在python中如果我们有一些对象需要持久性存储,并且不丢失我们这个对象的类型与数据,我 ...

  7. 9、第九节课jquery选择器jq2,20151007

    1.表单选择器 2.not 里面不能加其他标签 $div p:not(not:disable)  错误的 $div p:not(:disable)  正确的 3.选择设置相应属性的标签项 $(&quo ...

  8. Android开发中在一个Activity中关闭另一个Activity

    比如有ActivityA, ActivityB,在ActivityB中关闭ActivityA 解决方案: 1. 在 ActivityA 里面设置一个静态的变量instance,初始化为this在 Ac ...

  9. XP系统取消开机硬件检查

    非正常关机后进行磁盘检查,主要用于检查磁盘错误等.非法关机后会丢失一些文件或产生一些文件错误,而硬盘自检恰恰就是来检查这些错误并对之进行必要的修复,此功能如果被关闭,不能进行必要的数据恢复,久而久之会 ...

  10. Convert String to Long

    问题: Given a string, write a routine that converts the string to a long, without using the built in f ...