创建内核模块时遇“vmlinux不可用”错误的解决问询
问题分析与解决思路
编译时BTF提示的含义
Skipping BTF generation for /home/dpb/Documents/CSE_420/p4/try2/mymodule2.ko due to unavailability of vmlinux 提示的核心含义是:
内核模块编译时会尝试生成**BTF(BPF类型格式)**信息——这是内核用于BPF调试、类型校验的标准格式,但系统中找不到带完整调试符号的内核镜像文件vmlinux,因此跳过BTF生成步骤。
这个提示本身不会直接导致模块无法加载,但你的同学未遇到该问题,是因为他们的系统安装了对应内核版本的调试包,或者内核编译时自带了vmlinux文件。
解决BTF提示的两种方法
- 安装内核调试包:
- Ubuntu/Debian系:执行
sudo apt install linux-image-$(uname -r)-dbg - RHEL/CentOS系:执行
sudo dnf install kernel-debuginfo-$(uname -r)
- Ubuntu/Debian系:执行
- 编译时禁用BTF生成:修改Makefile的
all目标,添加BTF=0参数:KERNEL_SOURCE := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) obj-m += mymodule2.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) BTF=0 modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
模块加载失败的核心问题
使用insmod加载时卡住,是因为代码存在空指针访问的致命错误:
- 全局变量
p_id未初始化,也没有提供外部传入参数的方式,加载时它的值是随机垃圾值,调用find_get_pid(p_id)几乎必然返回NULL - 直接对
NULL指针调用pid_task,会触发内核空指针异常,导致系统卡住甚至崩溃
代码修正方案
以下是修复后的完整代码(包含参数传入、空指针检查、资源释放、拼写修正):
#include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/pid_namespace.h> int p_id; struct pid *pid_struct; struct task_struct *task; /* Meta Information */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Devin 4 GNU/Linux"); MODULE_DESCRIPTION("A module to access task_struct via PID"); // 允许加载模块时传入目标PID module_param(p_id, int, 0644); MODULE_PARM_DESC(p_id, "The PID of the process to inspect"); /* This function is called when the module is loaded into the kernel */ static int __init ModuleInit(void) { // 检查传入的PID合法性 if (p_id <= 0) { printk(KERN_ERR "Invalid PID: %d, must be positive\n", p_id); return -EINVAL; } pid_struct = find_get_pid(p_id); if (!pid_struct) { printk(KERN_ERR "Process with PID %d not found\n", p_id); return -ESRCH; } task = pid_task(pid_struct, PIDTYPE_PID); if (!task) { printk(KERN_ERR "Failed to get task_struct for PID %d\n", p_id); put_pid(pid_struct); // 释放pid结构体,避免内存泄漏 return -EINVAL; } // 输出进程信息 printk(KERN_INFO "PID: %d\n", p_id); printk(KERN_INFO "Name: %s\n", task->comm); // 如需获取内存页信息,需先检查task->mm是否非空(内核线程无mm结构体) // if (task->mm) printk(KERN_INFO "Total Pages in KB: %lu\n", task->mm->total_vm * PAGE_SIZE / 1024); put_pid(pid_struct); // 释放pid结构体资源 return 0; } /* This function is called when the module is removed from the kernel */ static void __exit ModuleExit(void) { printk(KERN_INFO "Bye!\n"); } module_init(ModuleInit); module_exit(ModuleExit);
正确加载模块的方式
编译完成后,传入目标进程的PID(例如1234)加载模块:
sudo insmod mymodule2.ko p_id=1234
通过内核日志查看输出:
dmesg | tail -20
内容的提问来源于stack exchange,提问作者Devin Bowen
相关产品推荐
相关产品推荐

