【C语言】linux内核dev_direct_xmit

2024-03-01 11:20
文章标签 语言 linux 内核 dev direct xmit

本文主要是介绍【C语言】linux内核dev_direct_xmit,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、中文注释

这段代码是一个网络设备的直接数据包传输函数,在Linux内核网络子系统中用于处理数据包发送。函数签名 int dev_direct_xmit(struct sk_buff *skb, u16 queue_id) 表明这个函数用于发送指向 skb(socket buffer)结构的数据包,并使用指定的队列 queue_id。下面注释逐行解释这个函数的作用:

int dev_direct_xmit(struct sk_buff *skb, u16 queue_id)
{// 根据skb中存储的指向net_device结构的指针取得网络设备结构体struct net_device *dev = skb->dev;struct sk_buff *orig_skb = skb; // 保留原始的skb,用于后续对比struct netdev_queue *txq; // 指向网络设备发送队列的指针int ret = NETDEV_TX_BUSY; // 初始返回值设为忙bool again = false; // 用于标志是否需要再次尝试发送// 若网络设备没有激活或者网络连接不可用,则跳转到drop标签处理if (unlikely(!netif_running(dev) || !netif_carrier_ok(dev)))goto drop;// 检查和处理发送数据包,如果需要可以进行分片;// again变量会在必要时被置为true以再次尝试发送skb = validate_xmit_skb_list(skb, dev, &again);if (skb != orig_skb) // 如果skb被改变,说明有问题,需要释放资源goto drop;// 设置skb的队列映射到指定的queue_idskb_set_queue_mapping(skb, queue_id);// 根据skb获取对应的发送队列txq = skb_get_tx_queue(dev, skb);local_bh_disable(); // 禁止本地软中断,用于保护代码段不被中断// 锁定硬件发送队列HARD_TX_LOCK(dev, txq, smp_processor_id());// 如果发送队列没有冻结或者没有停止,则调用netdev_start_xmit来进行发送if (!netif_xmit_frozen_or_drv_stopped(txq))ret = netdev_start_xmit(skb, dev, txq, false);// 解锁硬件发送队列HARD_TX_UNLOCK(dev, txq);local_bh_enable(); // 启用本地软中断// 如果发送不完整,则释放skb资源if (!dev_xmit_complete(ret))kfree_skb(skb);return ret; // 返回发送结果drop: // 标签:用于处理发送失败情况// 对设备发送失败计数增加atomic_long_inc(&dev->tx_dropped);// 释放skb链表资源kfree_skb_list(skb);return NET_XMIT_DROP; // 返回发送丢弃结果
}
EXPORT_SYMBOL(dev_direct_xmit); // 导出符号,使得在模块中可以使用这个函数

这个函数的作用是尝试将数据包通过指定的网络设备发送出去。首先会对设备的运行状态进行检查,然后对skb列表进行可递验证,在保证硬件发送队列未冻结或未停止的条件下,尝试通过`netdev_start_xmit`发送数据包。发送完成之后,还需要释放或处理skb资源。如果在过程中检查到网络设备不处于工作状态或skb出现异常,则会释放skb并递增发送失败计数。

二、中文讲解

dev_direct_xmit 函数是 Linux 网络子系统中负责将 socket buffer(或称作 sk_buff,是内核中表示网络数据包的结构)直接发送到指定的网络队列的函数。在讲解函数的具体实现前,需要了解 sk_buff 和其他一些网络相关的结构体。
以下是该函数的详解:
1. 初始化变量:函数接收两个参数,`skb`(要发送的数据包)以及 queue_id(要发送到的队列 ID)。此外,它定义了一些局部变量如网络设备(`dev`),原始 skb(`orig_skb`),网络队列(`txq`),函数返回值(`ret`,初始值设为 NETDEV_TX_BUSY 标志忙碌)以及一个标志变量 again。
2. 检查网络设备状态:通过 unlikely 宏,该函数检测网络设备 (dev) 是否正在运行(`netif_running`)以及网络链路(网络载波)是否正常(`netif_carrier_ok`)。如果任何条件不满足,就跳转到标签 drop。
3. 验证和处理数据包:通过调用 validate_xmit_skb_list 函数验证 skb 是否可发送,并处理可能的问题。如果处理后的 skb 不等于原始 skb,表示数据包在处理中被更改或发生错误,也要跳转到 drop。
4. 设置队列映射和获取队列:通过 skb_set_queue_mapping 设置 skb 的队列映射到指定的 queue_id,然后通过 skb_get_tx_queue 获取相应 txq(网络设备中的发送队列)。
5. 禁用本地底半处理(softirq):`local_bh_disable` 函数调用用于防止在处理发送过程中被其他底半部打断,如网络软件中断处理程序。
6. 锁定发送队列:使用 HARD_TX_LOCK 宏锁定发送队列以保证在多核或多线程的情况下发送的同步性。
7. 发送数据包:先检查发送队列没有被冻结(`netif_xmit_frozen_or_drv_stopped`),接着调用 netdev_start_xmit 函数来开始发送数据包。如果发送成功或等待发送,`ret` 会被设置为相应的状态。
8. 解锁发送队列和启用本地底半处理:完成发送操作后,使用 HARD_TX_UNLOCK 宏解锁发送队列,然后调用 local_bh_enable 重新启用本地底半处理。
9. 处理发送返回状态:如果 dev_xmit_complete 函数显示发送未完成(通常是因为发生了错误),则使用 kfree_skb 释放 skb。
10. 处理失败和释放资源:如果发生需要丢弃数据包的情况(`drop` 标签下执行的代码),函数通过原子操作(`atomic_long_inc`)增加网络设备统计中丢弃的数据包计数,然后释放 skb 列表,并返回 NET_XMIT_DROP 表示数据包已丢弃。
11. 导出符号:最后,`EXPORT_SYMBOL` 宏允许其他模块使用 dev_direct_xmit 函数。

dev_direct_xmit 函数通常用于绕过标准的发送流程,直接将数据包(`sk_buff`)加入到指定网络设备的发送队列。这是一个比较低层的函数,直接与网络设备驱动交互。它经常用于性能敏感或需要细粒度控制发送过程的场景。函数会做适当的状态检查,如检查网络设备是否启动并且链路是否正常,验证和预处理数据包,设置队列映射,获取发送队列,并尝试发送数据包。在发送过程中,`dev_direct_xmit` 会处理排队、设备锁定和统计更新等事项。

在 dev_direct_xmit 函数中,发送的数据,即 skb (socket buffer),通常是已经经过封装的 IP 数据包。`skb` 结构是内核中用来存储网络数据包的通用数据结构,它可以包含各种层次的网络协议数据,包括链路层(例如以太网帧)、网络层(如 IP)、传输层(如 TCP/UDP)以及上层的应用数据。
在数据发送流程中,当一个 IP 数据包准备被发送出去时,它会首先被封装成一个 sk_buff,并含有完整的协议信息,例如:
- IP 头(如源地址、目标地址、协议类型等)
- 可能的传输层头(如 TCP 或 UDP 头部)
- 应用层负载(真正的数据内容)
在数据包通过 dev_direct_xmit 函数发送之前,它可能已经通过了多个网络层次的处理,包括路由决策、网络地址转换(NAT)、排队规则等。如果是以太网设备,`skb` 也很可能已包含了以太网帧头部,这样网络设备驱动就可以将其直接放送到硬件发送队列以交由网络接口卡(NIC)发送出去。
因此,通过 dev_direct_xmit 发送的 skb 一般情况下是一个完整的已封装好的数据包,包括 IP 层及可能的其他层次。然而,确切的协议层次和封装细节取决于 skb 是如何在发送路径上被构造和处理的。

这篇关于【C语言】linux内核dev_direct_xmit的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/762294

相关文章

Linux内核定时器使用及说明

《Linux内核定时器使用及说明》文章详细介绍了Linux内核定时器的特性、核心数据结构、时间相关转换函数以及操作API,通过示例展示了如何编写和使用定时器,包括按键消抖的应用... 目录1.linux内核定时器特征2.Linux内核定时器核心数据结构3.Linux内核时间相关转换函数4.Linux内核定时

Linux镜像文件制作方式

《Linux镜像文件制作方式》本文介绍了Linux镜像文件制作的过程,包括确定磁盘空间布局、制作空白镜像文件、分区与格式化、复制引导分区和其他分区... 目录1.确定磁盘空间布局2.制作空白镜像文件3.分区与格式化1) 分区2) 格式化4.复制引导分区5.复制其它分区1) 挂载2) 复制bootfs分区3)

C语言逗号运算符和逗号表达式的使用小结

《C语言逗号运算符和逗号表达式的使用小结》本文详细介绍了C语言中的逗号运算符和逗号表达式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习... 在C语言中逗号“,”也是一种运算符,称为逗号运算符。 其功能是把两个表达式连接其一般形式为:表达

Go语言实现桥接模式

《Go语言实现桥接模式》桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立地变化,本文就来介绍一下了Go语言实现桥接模式,感兴趣的可以了解一下... 目录简介核心概念为什么使用桥接模式?应用场景案例分析步骤一:定义实现接口步骤二:创建具体实现类步骤三:定义抽象类步骤四:创建扩展抽象类步

GO语言实现串口简单通讯

《GO语言实现串口简单通讯》本文分享了使用Go语言进行串口通讯的实践过程,详细介绍了串口配置、数据发送与接收的代码实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 目录背景串口通讯代码代码块分解解析完整代码运行结果背景最近再学习 go 语言,在某宝用5块钱买了个

Linux服务器数据盘移除并重新挂载的全过程

《Linux服务器数据盘移除并重新挂载的全过程》:本文主要介绍在Linux服务器上移除并重新挂载数据盘的整个过程,分为三大步:卸载文件系统、分离磁盘和重新挂载,每一步都有详细的步骤和注意事项,确保... 目录引言第一步:卸载文件系统第二步:分离磁盘第三步:重新挂载引言在 linux 服务器上移除并重新挂p

Linux下屏幕亮度的调节方式

《Linux下屏幕亮度的调节方式》文章介绍了Linux下屏幕亮度调节的几种方法,包括图形界面、手动调节(使用ACPI内核模块)和外接显示屏调节,以及自动调节软件(CaliseRedshift和Reds... 目录1 概述2 手动调节http://www.chinasem.cn2.1 手动屏幕调节2.2 外接显

Linux(centos7)虚拟机没有IP问题及解决方案

《Linux(centos7)虚拟机没有IP问题及解决方案》文章介绍了在CentOS7中配置虚拟机网络并使用Xshell连接虚拟机的步骤,首先,检查并配置网卡ens33的ONBOOT属性为yes,然后... 目录输入查看ZFhrxIP命令:ip addr查看,没有虚拟机IP修改ens33配置文件重启网络Xh

linux实现对.jar文件的配置文件进行修改

《linux实现对.jar文件的配置文件进行修改》文章讲述了如何使用Linux系统修改.jar文件的配置文件,包括进入文件夹、编辑文件、保存并退出编辑器,以及重新启动项目... 目录linux对.jar文件的配置文件进行修改第一步第二步 第三步第四步总结linux对.jar文件的配置文件进行修改第一步进

GO语言zap日志库理解和使用方法示例

《GO语言zap日志库理解和使用方法示例》Zap是一个高性能、结构化日志库,专为Go语言设计,它由Uber开源,并且在Go社区中非常受欢迎,:本文主要介绍GO语言zap日志库理解和使用方法的相关资... 目录1. zap日志库介绍2.安装zap库3.配置日志记录器3.1 Logger3.2 Sugared