Linux内核数据包的发送传输
本文主要讲解了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内核数据包的发送传输的更多相关文章
- [转]Linux网络 - 数据包的发送过程
转, 原文:https://segmentfault.com/a/1190000008926093 -------------------------------------------------- ...
- linux内核数据包转发流程(三)网卡帧接收分析
[版权声明:转载请保留出处:blog.csdn.net/gentleliu.邮箱:shallnew*163.com] 每一个cpu都有队列来处理接收到的帧,都有其数据结构来处理入口和出口流量,因此,不 ...
- linux内核数据包转发流程(二):中断
[版权声明:转载请保留出处:blog.csdn.net/gentleliu.邮箱:shallnew*163.com] 内核在处理2层数据包之前,必须先处理中断系统.设立中断系统,才有可能每秒处理成千的 ...
- linux内核数据包转发流程(一):网络设备驱动
[版权声明:转载请保留出处:blog.csdn.net/gentleliu.邮箱:shallnew*163.com] 网卡驱动为每一个新的接口在一个全局的网络设备列表里插入一个数据结构.每一个接口由一 ...
- Linux内核源代码解析——用户发送数据包的起源之sendto
本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/10162853 Jack:我想知道用户如何把数据发送到 ...
- Linux网络数据包的揭秘以及常见的调优方式总结
https://mp.weixin.qq.com/s/boRWlx1R7TX0NLuI2sZBfQ 作为业务 SRE,我们所运维的业务,常常以 Linux+TCP/UDP daemon 的形式对外提供 ...
- virtio-netdev 数据包的发送
在前面几文中已经大体介绍了virtio的重要组成,包含virtio net设备的创建,vring的创建,与virtio设备的交互方式,我们就从网络数据包的发送角度来看下virtio的详细使用流程. [ ...
- UDP发送的数据 以数据包形式发送
UDP发送的数据 以数据包形式发送
- vlan linux内核数据流程
转:http://blog.sina.com.cn/s/blog_62bbc49c0100fs0n.html 一.前言 前几天做协议划分vlan的时候看了一些linux内核,了解不深,整理了下vlan ...
随机推荐
- Tomcat jdbc pool配置
Tomcat jdbc pool是apache在tomcat7版本中启用的新连接池,用它来解决以往DBCP无法解决的一些问题. Tomcat jdbc pool的优点: (1) tomcat j ...
- 开发中可能会用到的几个 jQuery 小提示和技巧 (转)
转自:http://www.cnblogs.com/lhb25/p/useful-jquery-tips-and-tricks.html 今天,我们将分享一些很有用的技巧和窍门给 jQuery 开发人 ...
- [转] GDB attach
转:http://blog.csdn.net/wangeen/article/details/14230171 attach是GDB一种重要的debug模式,在MPI程序debug中发挥重要的作用. ...
- MySQL(13):Select-order by
1. 按照字段值进行排序: 语法: order by 字段 升序|降序(asc|desc) 允许多字段排序,指的是,先按照第一个字段排序,如果说,不能区分,才使用第二个字段,以此类推. ...
- Because the people who are crazy enough to think they can change the world, are the ones who do.
Here's to the crazy ones. The misfits. The rebels. The troublemakers. The round pegs in the square h ...
- OD: DEP & Ret2Libc
Data Execution Prevention,数据执行保护,专门用来弥补计算机对数据和代码混淆这一天然缺陷. DEP 的原理是将数据所在的内存页(默认的堆.各种堆栈页.内存池页)标记为不可执行, ...
- asp.net微信开发第十篇----使用百度编辑器编辑图文消息,上传图片、微信视频
经过几天的资料收集,终于完成了该编辑器的图片上传,视频插入功能,视频插入功能主要借用了该编辑器的插入iframe功能,如原始插件图: 修改后的插件图如下(其中我隐藏掉了一些不需要使用的插件功能): 配 ...
- 【转载】Java StringBuffer与StringBuider
本文转载微学苑java基础教程,原文地址链接:http://www.weixueyuan.net/view/6318.html String 的值是不可变的,每次对String的操作都会生成新的Str ...
- PHP XML Parser
安装 XML Parser 函数是 PHP 核心的组成部分.无需安装即可使用这些函数. PHP XML Parser 函数 PHP:指示支持该函数的最早的 PHP 版本. 函数 描述 PHP utf8 ...
- vimtutor-summary