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

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

整体流程如下

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

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

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

/*正常传输流程*/

/*高层协议dev_queue_xmit(skb)发送数据报文*/

static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t total_len)
{
` skb = sock_wmalloc(sk, total_len + dev->hard_header_len + 32, 0, GFP_KERNEL);
if (!skb) {
error = -ENOMEM;
goto end;
} //...... dev_queue_xmit(skb); end:
release_sock(sk);
return error;
} /*正常传输流程*/
int dev_queue_xmit(struct sk_buff *skb)
{
struct net_device *dev = skb->dev; /*得到网络设备*/
struct Qdisc *q; /*选取net_device中的_tx队列*/
txq = dev_pick_tx(dev, skb);
{
txq = &dev->_tx[index]
} /*取得Qdisc*/
q = rcu_dereference(txq->qdisc);
{
q = dev->_tx[index]->qdisc;
} if (q->enqueue)
{
rc = __dev_xmit_skb(skb, q, dev, txq);
{
rc = qdisc_enqueue_root(skb, q);
{
struct Qdisc *sch = q; /*将数据报文至于设备发送队列中, 一般采用FIFO算法进行发送,标记数据包发送准备完毕*/
qdisc_enqueue(skb, sch) ;
{
sch->enqueue(skb, sch);
}
} /*触发对发送准备完毕的数据包的进一步处理*/
qdisc_run(q);
}
goto out;
} /*如果没有定义队列的管理方法,即q->enqueue == NULL,直接调用dev_hard_start_xmit发送数据包
* 一般是逻辑网络设备,如环回口或者隧道口
*/
if (dev->flags & IFF_UP) { dev_hard_start_xmit(skb, dev, txq)
{
const struct net_device_ops *ops = dev->netdev_ops;
int rc;
rc = ops->ndo_start_xmit(skb, dev); /*硬件发送数据包*/
}
} out_kfree_skb:
kfree_skb(skb);
return rc;
out:
rcu_read_unlock_bh();
return rc;
} static inline void qdisc_run(struct Qdisc *q)
{
/*反复调用qdisc_restart直到返回空值(队列中不在含有数据包)
*或者网络不在接受任何数据包为止*/
__qdisc_run(q);
{
while (qdisc_restart(q))
{
if (need_resched() || jiffies != start_time) {
__netif_schedule(q);
break;
}
}
}
} /*
* 从设备队列中获取下一个数据包并将其发出,
* 一般而言只有一个队列并按照FIFO原则运作
* 可以通过Qdisc对它们赋予某种策略
*/
static inline int qdisc_restart(struct Qdisc *q)
{
struct netdev_queue *txq;
struct net_device *dev;
spinlock_t *root_lock;
struct sk_buff *skb; /*请求下一个数据包*/
skb = dequeue_skb(q);
{
skb = q->dequeue(q);
} /*没有stop的情况下,发送数据包*/
sch_direct_xmit(skb, q, dev, txq, root_lock);
{
/*试图获取txq->_xmit_loc的锁,并记录CPU到txq->xmit_lock_owner中*/
HARD_TX_LOCK(dev, txq, smp_processor_id());
{
__netif_tx_lock(txq, cpu);
{
spin_lock(&txq->_xmit_lock);
txq->xmit_lock_owner = cpu;
}
} if (!netif_tx_queue_stopped(txq) && !netif_tx_queue_frozen(txq))
ret = dev_hard_start_xmit(skb, dev, txq);
{
const struct net_device_ops *ops = dev->netdev_ops;
int rc;
rc = ops->ndo_start_xmit(skb, dev); /*硬件发送数据包*/
} HARD_TX_UNLOCK(dev, txq); switch (ret) {
case NETDEV_TX_OK:
/* Driver sent out skb successfully */
ret = qdisc_qlen(q);
break; case NETDEV_TX_LOCKED:
/* Driver try lock failed */
ret = handle_dev_cpu_collision(skb, txq, q);
{
if (unlikely(dev_queue->xmit_lock_owner == smp_processor_id())) {
/*同一个CPU被锁住了,说明存在处理器死循环,该网络适配器的数据包发送因为某种原因失败了,
* 并做出了重新传输一个数据包的尝试,这里只做简单丢弃并立即从qdisc_run返回以完成第一个传输进程*/
kfree_skb(skb);
printk(KERN_WARNING "Dead loop on netdevice %s, fix it urgently!\n", dev_queue->dev->name);
} else {
/*其他CPU正在发送其他的数据包,使用requeue()进行重新调度,
*在netif_schedule()中激活NET_TX_SOFTIRQ以便再次触发发送*/
ret = dev_requeue_skb(skb, q);
{
__netif_schedule(q);
{
__netif_reschedule(q)
{
raise_softirq_irqoff(NET_TX_SOFTIRQ);
}
}
}
}
}
break;
}
}
}

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

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

/*NETIF_TX_SOFTIRQ上的传输,通过__netif_schedule触发
* 使用的场景是
* 一:数据报文发送失败
* 二:在QOS中进行流量整形,按照一定的时间定时启动定时器,进行数据定速率发送
*对__netif_schedule的调用在下一次CPU进行调度的时候被触发
*/
void __netif_schedule(struct Qdisc *q)
{
__netif_reschedule(q);
{
raise_softirq_irqoff(NET_TX_SOFTIRQ);
}
} /*
* 设定NET_TX_SOFTIRQ 对应的处理句柄是net_tx_action
*/
static int __init net_dev_init(void)
{
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
} /*调用qdisc_run启动网络设备数据包的传输*/
static void net_tx_action(struct softirq_action *h)
{
while (head) {
qdisc_run(q);
}
}

通过上面的分析,我们可以清晰的了解到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.lang.ref 包--转

    概述 Java.lang.ref 是 Java 类库中比较特殊的一个包,它提供了与 Java 垃圾回收器密切相关的引用类.这些引用类对象可以指向其它对象,但它们不同于一般的引用,因为它们的存在并不防碍 ...

  2. [转] When exactly does the virtual table pointer (in C++) gets set for an object?

    PS: http://stackoverflow.com/questions/7934540/when-exactly-does-the-virtual-table-pointer-in-c-gets ...

  3. 补间动画 Interpolator 简介 示例

    简介 补间动画的原理: 每次绘制视图时View所在的[ViewGroup]中的drawChild函数获取该View的Animation的值,然后调用canvas.concat (transformTo ...

  4. OD: ActiveX Vulnerabilities

    通过一个精心构造的页面 exploit 第三方软件中的 ActiveX 已经成为一种惯用攻击手段,众多知名软件公司都曾被发现其注册的 ActiveX 中存在严重的缓冲区溢出漏洞,一个被广泛使用的第三方 ...

  5. Android开发环境搭建简介

    Android的开发工具,可以使用Eclipse,Idea,Android Studio,其中Eclipse是开源中国大部分使用的IDE,Idea是号称最好用的开发工具,有很多用处,Android S ...

  6. swift 重载 泛式 inout的使用

    swift 重载 泛式 inout的使用 函数 func 关键字 -> 表示返回值信息等等 那我们接下来利用函数做几件事情 -a 比较两个数字的大小 -b 比较两个字符串 -c 既能比较字符串, ...

  7. Java中如何判断当前环境是大端字节顺序还是小端字节顺序

    Java非字节类型的基本类型,除了布尔型都是由组合在一起的几个字节组成的.这些数据类 型及其大小总结在表 2-1 中. 表:基本数据类型及其大小 数据类型 大小(以字节表示) Byte 1 Char ...

  8. 将图片转换为Base64

    string Imagefilename   硬盘路径 protected string ImgToBase64String(string Imagefilename) { try { Bitmap ...

  9. Class类相关

    Class类是java.lang包中的类,该类的实例可以帮助程序创建其他类的实例或者取得其他类的对象的内部信息 使用class类获得一个类相关的class类(注意得到的是class类,不是相关的类) ...

  10. [转]C++堆和栈的区别

    一.预备知识―程序的内存分配 一个由c/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)― 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈. ...