【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系统中常用的打包和解压命令,包括tar和zip,使用tar命令可以创建和解压tar格式的归档文件,使用zip命令可以创建和解压zip格式的压缩文件,每... 目录Lijavascriptnux 打包和解压命令打包命令解压命令总结linux 打包和解压命令打

linux如何复制文件夹并重命名

《linux如何复制文件夹并重命名》在Linux系统中,复制文件夹并重命名可以通过使用“cp”和“mv”命令来实现,使用“cp-r”命令可以递归复制整个文件夹及其子文件夹和文件,而使用“mv”命令可以... 目录linux复制文件夹并重命名我们需要使用“cp”命令来复制文件夹我们还可以结合使用“mv”命令总

Linux使用cut进行文本提取的操作方法

《Linux使用cut进行文本提取的操作方法》Linux中的cut命令是一个命令行实用程序,用于从文件或标准输入中提取文本行的部分,本文给大家介绍了Linux使用cut进行文本提取的操作方法,文中有详... 目录简介基础语法常用选项范围选择示例用法-f:字段选择-d:分隔符-c:字符选择-b:字节选择--c

使用Go语言开发一个命令行文件管理工具

《使用Go语言开发一个命令行文件管理工具》这篇文章主要为大家详细介绍了如何使用Go语言开发一款命令行文件管理工具,支持批量重命名,删除,创建,移动文件,需要的小伙伴可以了解下... 目录一、工具功能一览二、核心代码解析1. 主程序结构2. 批量重命名3. 批量删除4. 创建文件/目录5. 批量移动三、如何安

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要