【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 rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

GO语言短变量声明的实现示例

《GO语言短变量声明的实现示例》在Go语言中,短变量声明是一种简洁的变量声明方式,使用:=运算符,可以自动推断变量类型,下面就来具体介绍一下如何使用,感兴趣的可以了解一下... 目录基本语法功能特点与var的区别适用场景注意事项基本语法variableName := value功能特点1、自动类型推

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

Linux搭建ftp服务器的步骤

《Linux搭建ftp服务器的步骤》本文给大家分享Linux搭建ftp服务器的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录ftp搭建1:下载vsftpd工具2:下载客户端工具3:进入配置文件目录vsftpd.conf配置文件4:

Linux实现查看某一端口是否开放

《Linux实现查看某一端口是否开放》文章介绍了三种检查端口6379是否开放的方法:通过lsof查看进程占用,用netstat区分TCP/UDP监听状态,以及用telnet测试远程连接可达性... 目录1、使用lsof 命令来查看端口是否开放2、使用netstat 命令来查看端口是否开放3、使用telnet

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优

Go语言连接MySQL数据库执行基本的增删改查

《Go语言连接MySQL数据库执行基本的增删改查》在后端开发中,MySQL是最常用的关系型数据库之一,本文主要为大家详细介绍了如何使用Go连接MySQL数据库并执行基本的增删改查吧... 目录Go语言连接mysql数据库准备工作安装 MySQL 驱动代码实现运行结果注意事项Go语言执行基本的增删改查准备工作