kernel module编程(二):一些古老的记忆

本文回顾了一个基于Linux内核模块的大规模高速数据包过滤项目。项目旨在从网络流量中高效过滤并分析数据包,涉及多CPU负载均衡、内核模块设计等技术挑战。文章还分享了性能测试中的不公平竞争经历及专利归属问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  本文档和技术没有直接关联,只是一些回忆。

   记录内核模块变成是因为最近在搞那个可恶的GMA500的驱动,翻到了《Linux Device Drivers》。回想起来我倒真有一个设计kernel 模块的项目,也算大的。我处理器中内核模块,也就是高速从网卡过滤数据包的方式,上层软件从内核模块中获取过滤(包括端口范围、IP地址范围、关键字、协 议特征等等)的包进行协议分析,除了记录分析外还有一些其他的处理,和kernel module没有关系。整个关键就是过滤的效率。

   这是我第一个接触Linux kernel module的项目,而且对性能的要求很高。对于SMP Linux,内核kernel模块如何实现CPU均衡,麻烦死了,而且效果不太好,因为每一个网卡的接收都指定使用某一个CPU,缺省是CPU0,这部分 资源耗费不是我们的代码可以解决。百兆流量没有问题,千兆就很惨,为了这个问题提供花费了很长时间(按月计算)。

   我还记得为了保证同时过滤,同时向上层分析软件提供数据,采用了两个队列轮回的方式,其中还有些一些"巧妙"的处理,我只记得在上面使了点小聪明,用来 特别的方式。可是过了一段时间我看回那段程序,我只记得目的,但是如何处理,已经看不懂了。看来人是越来越笨的,没有将处理方法加注,只说明了处理目的。 这个印象很深,所以注释或者笔记是必须的。

   实际上那时,根本就没学过内核模块编程,也没有人可以找,我在很长一段时间内都认为程序、程序语言都只是工具,工具不需要明白原理,只要会用就行,一开 始时间也紧,网上共产主义的没有现在繁荣,而且向来也就不求甚解,只求何用。直接在一个开源程序中看懂的,只能说看懂了怎么做。现在翻开 Linux Device Drivers》.,才发现,这个见过,原来是这么一回事……惭愧!

   千兆高速分析的性能测试被人整过,这事我记得。实际上也是我们单位悲哀,典型的窝里斗。测试由数据部的同事进行,事后才知道一开始就定调不让通过。原因 不详,估计和利益有关系。但是关系这个东东,你可以让不通过的变成通过,但是不能让通过的变成不通过。不过让你不过就不过,办法总是有的。哼哼,那个家 伙,我记得……不,已经不记得是谁了。实际上数据包的分析和流量并没有直接关系,只是和每秒的数据包的数目有关,处理的单位是数据包,不是一个一个比特。 一般来讲每秒数据包数据越多,带宽越大。我们自己测试的时候好像是按照平均包大小为100-200byte来计算,具体是多少忘了,因为实际网络上有很多 1500的包,BT嘛。但是他们测试给出的包除了目标包之外,都是短包,不记得具体是多少,好像是40或者32,即是smartbit发送的缺省短包,因 为现象就不对,问他们怎么配,死后不说,后来才知道的。测试环境的每秒packet实在太大了,和实际值差太多,短一半就消耗至少多一倍,少了几 倍,CPU就多几倍。而网卡接收这部分只能通过一个CPU来处理,内核模块里面到可以轮选,但是切换CPU也是消耗的,所以最后简单处理是选择和接收不同 的CPU,尽可能优化程序。但是接收的问题不是我能解决。就这样被“自己人”玩了一把。

  当开发人员去接触单位政治是一种悲哀。不知道领导上面的关系如何,但是下面同事的关系还是可以的,当时不敢和我说,实际上最后还是知道的。相对一声叹息,还能如何……

  这个项目测试问题,领导后来去解决,具体的不清楚,但是这种测试很无耻,没有底线。

   这个项目的最后一个故事是后来另一个同事抽取其中的某些部分去报了专利,今年年初批下来了。具体报了哪一部分不记得报。记得批下来之后,看了电子扫描文 档,哇,前面挂的领导的名字那个长啊,挂第一位,不,应该说挂1、2、3位的可能都不知道这个项目具体的开发者是哪个小人物,而且我相信,挂在1、2、 3、4、5位的他们根本不知道这个专利说将的技术具体是什么,说白了,他们啥都不清楚。我终于在倒数第二发现了我名字,倒数第一是这个项目的另一个开发者 及申报者。不错了,虽然排在第7、第8,至少让我们漏了把脸,我们要感谢领导们宽广的胸怀,在这个学术腐败争名夺利的地方,居然还能让开发人员排上,虽然 有些勉强,虽然考在最后,上去嘛总比不上的强。

 

相关链接: 我的与kernel module有关的文章

<think>好的,用户问的是如何确保在Linux驱动模块的module_exit函数中释放所有资源。我需要详细回答这个问题,确保覆盖用户可能关心的各个方面。 首先,用户可能是一个正在学习Linux驱动开发的开发者,可能在编写模块时遇到了资源泄漏的问题,或者在卸载模块时系统出现异常。他们想知道具体的方法来确保所有资源都被正确释放,避免内存泄漏或其他资源泄漏问题。 接下来,我需要考虑用户的需求。他们可能希望了解具体的步骤和最佳实践,包括如何跟踪资源分配、常见的资源类型、释放顺序的重要性,以及调试工具的使用。此外,他们可能对代码结构、错误处理机制和版本兼容性也有疑问。 我应该分步骤来回答,确保结构清晰。第一步可能是建立资源清单,记录所有分配的资源。第步是按顺序释放资源,比如遵循LIFO原则。第三步是检查每个释放函数的返回值,确保没有错误。第四步是使用调试工具检测泄漏。第五步是处理异常情况,比如在初始化失败时提前释放资源。第六步是代码审查和测试,包括静态分析和动态测试。第七步是内核版本兼容性,确保不同内核版本的API变化不会影响资源释放。最后,给出示例代码和常见错误,帮助用户理解具体实现。 需要注意的是,用户可能对某些术语或API函数不熟悉,比如kfree、iounmap等,因此需要解释清楚。同时,强调资源释放的顺序,比如先释放子资源再释放父资源,避免悬空指针。此外,调试工具如kmemleak和KASAN的使用方法也需要简要介绍,方便用户实际操作。 可能用户没有考虑到的问题包括初始化部分失败时的资源释放,或者在并发情况下资源释放的竞争条件。因此,在回答中需要提到这些情况,并给出解决方案,比如使用goto语句集中释放资源,或者确保同步机制的正确使用。 最后,总结关键点,帮助用户记忆和实践。同时,给出参考资料或推荐书籍,如《Linux Device Drivers》,增强答案的可信度和实用性。</think>在Linux驱动开发中,确保`module_exit`中完整释放所有资源是避免内存泄漏和系统不稳定的关键。以下是系统性解决方案和最佳实践: --- ### 一、资源释放四层检查体系 #### 1. **建立资源清单(资源审计)** - **显式记录所有分配操作** 在代码中添加注释标记: ```c /* [ALLOC] 在probe函数中分配 */ dev->buffer = kmalloc(BUF_SIZE, GFP_KERNEL); /* [REG] 在init函数中注册 */ ret = register_chrdev(MAJOR_NUM, "demo_dev", &fops); ``` #### 2. **逆向释放原则(LIFO顺序)** ```mermaid graph TD A[释放中断号] --> B[注销设备] B --> C[解除内存映射] C --> D[释放DMA缓冲区] D --> E[销毁工作队列] ``` - **典型错误顺序示例** 若先`iounmap`再释放DMA缓冲区,可能造成硬件访问野指针 #### 3. **释放函数对照表** | 分配函数 | 对应释放函数 | 注意事项 | |-------------------------|-----------------------|------------------------------| | `kmalloc/kzalloc` | `kfree` | 需检查指针非NULL | | `vmalloc` | `vfree` | 不可在中断上下文使用 | | `request_irq` | `free_irq` | 需确保中断处理已完成 | | `ioremap` | `iounmap` | 必须成对出现 | | `dma_alloc_coherent` | `dma_free_coherent` | 需保持size参数一致 | | `cdev_add` | `cdev_del` | 需先移除再注销设备号 | --- ### 、防御性编程技巧 #### 1. **双重释放防护** ```c if (dev->buffer) { kfree(dev->buffer); dev->buffer = NULL; // 置空防止重复释放 } ``` #### 2. **错误处理集中化(goto模式)** ```c static int __init demo_init(void) { if (alloc1_fail) goto err_alloc1; if (register_fail) goto err_register; return 0; err_register: free_alloc1(); err_alloc1: return -ENOMEM; } ``` #### 3. **引用计数器验证** ```c // 在结构体中添加计数器 struct my_dev { atomic_t ref_count; /* 其他成员 */ }; // 释放前检查 if (atomic_read(&dev->ref_count) != 0) { printk(KERN_ERR "Device still in use!\n"); return; } ``` --- ### 三、调试与验证手段 #### 1. **内存泄漏检测工具** - **kmemleak使用流程** ```bash # 挂载debugfs mount -t debugfs nodev /sys/kernel/debug # 扫描泄漏 echo scan > /sys/kernel/debug/kmemleak # 查看报告 cat /sys/kernel/debug/kmemleak ``` #### 2. **压力测试方法** ```bash # 循环加载卸载模块 for i in {1..1000}; do insmod demo.ko && rmmod demo done # 监控dmesg输出 dmesg | grep "freed" ``` #### 3. **资源状态监控** ```bash # 检查/proc信息 cat /proc/interrupts | grep demo cat /proc/iomem | grep demo ls /sys/devices/virtual/ | grep demo ``` --- ### 四、典型错误案例分析 #### 案例1:未注销次设备号 - **现象**:`rmmod`后`cat /proc/devices`仍显示设备号 - **修复**:在`module_exit`中添加`unregister_chrdev_region()` #### 案例2:中断未释放 - **现象**:模块卸载后产生IRQ错误日志 - **关键代码**: ```c free_irq(dev->irq_num, dev); // 必须与request_irq参数完全一致 ``` #### 案例3:sysfs残留 - **现象**:`sys/class/`中仍存在设备目录 - **解决方案**: ```c device_destroy(class, devno); // 需与class_create配对使用 class_destroy(class); ``` --- ### 五、自动化检查方案 #### 1. **静态代码分析** ```bash # 使用Sparse工具检查 make C=2 CHECK="sparse" # 使用Coccinelle模式匹配 coccicheck --mode=resource ``` #### 2. **动态追踪技术** ```bash # 使用ftrace跟踪释放函数 echo function > /sys/kernel/debug/tracing/current_tracer echo kfree > /sys/kernel/debug/tracing/set_ftrace_filter echo 1 > /sys/kernel/debug/tracing/tracing_on ``` --- **最终验证清单**: 1. 所有`kmalloc`都有对应的`kfree` 2. 每个`ioremap`都执行了`iounmap` 3. 注册的设备号/中断号已注销 4. 工作队列/tasklet已销毁 5. sysfs节点已移除 6. 引用计数器归零验证 7. 压力测试通过1000次加载/卸载循环 通过系统化的资源管理和多重验证手段,可确保驱动模块的资源释放完整性,建议结合`slub_debug`和`KASAN`等工具进行深度检测。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值