内核模块中访问sysctl条目:获取未导出的tcp_pacing_ss_ratio
从内核模块访问未导出sysctl变量的最简方法
咱们直接说核心思路:既然tcp_pacing_ss_ratio已经对应了一个可通过用户空间sysctl命令修改的条目,那最稳妥也最简洁的方式,就是利用内核自带的sysctl查找接口定位到这个条目,直接访问它指向的变量内存,完全不用去碰符号导出的路子——毕竟sysctl本身就是内核提供的标准化访问路径。
下面是具体的实现步骤和代码示例:
关键实现逻辑
Linux内核提供了sysctl_find_table等函数来遍历sysctl树,找到目标条目后,就能通过它的data指针直接读写对应的变量。这里以常见的5.x系列内核为例:
代码示例
#include <linux/sysctl.h> #include <linux/net.h> #include <linux/tcp.h> #include <linux/string.h> static struct ctl_table *pacing_ss_ratio_entry; static int __init my_module_init(void) { // 先定位到TCP相关的sysctl根表 struct ctl_table *tcp_sysctl_table = sysctl_find_table("net/ipv4/tcp"); if (!tcp_sysctl_table) { pr_err("Failed to locate TCP sysctl table\n"); return -EINVAL; } // 遍历TCP表,找到目标条目 for (struct ctl_table *entry = tcp_sysctl_table; entry->procname; entry++) { if (strcmp(entry->procname, "tcp_pacing_ss_ratio") == 0) { pacing_ss_ratio_entry = entry; break; } } if (!pacing_ss_ratio_entry) { pr_err("Failed to find tcp_pacing_ss_ratio entry\n"); return -EINVAL; } // 读取当前值 int current_val = *(int *)pacing_ss_ratio_entry->data; pr_info("Current tcp_pacing_ss_ratio: %d\n", current_val); // 修改值(记得加锁避免竞态) if (pacing_ss_ratio_entry->lock) { write_lock(pacing_ss_ratio_entry->lock); *(int *)pacing_ss_ratio_entry->data = 150; // 示例修改为150 write_unlock(pacing_ss_ratio_entry->lock); pr_info("Updated tcp_pacing_ss_ratio to 150\n"); } return 0; } static void __exit my_module_exit(void) { pr_info("Module exited\n"); } module_init(my_module_init); module_exit(my_module_exit); MODULE_LICENSE("GPL");
重要注意事项
- 内核版本兼容性:老版本内核(比如3.x)的sysctl结构可能有差异,比如
sysctl_find_table的参数格式、结构字段名称可能不同,需要针对性调整。 - 锁的必要性:修改变量时必须使用条目自带的
lock字段加锁,防止并发访问引发竞态问题。 - 许可证要求:由于模块直接访问了内核内部结构,必须声明为GPL许可证,否则会触发内核taint标记,甚至无法加载。
- 不推荐的替代方案:比如通过
kallsyms查找变量内存地址,这种方法极度依赖内核版本,稳定性极差,完全不建议使用。
内容的提问来源于stack exchange,提问作者FormerNcp
相关产品推荐
相关产品推荐

