为什么这个内核模块在2.6.39上永久标记?

当我加载这个模块时:

#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> MODULE_LICENSE("Dual BSD/GPL"); static int hello_init(void) { printk("<1> Hello world!\n"); return 0; } static void hello_exit(void) { printk("<1> Bye, cruel world\n"); } module_init(hello_init); module_exit(hello_exit); 

(来自http://www.freesoftwaremagazine.com/articles/drivers_linux?page=0,2 )

该模块在lsmod被标记为[permanent] ,并且在2.6.39-02063904-generic(来自Ubuntu PPA )上不能被卸载。 但它在默认的2.6.38内核上工作正常。 (在Ubuntu 11.04 x86上)。

2.6.39有什么变化? 和我需要改变我的代码?

当我遇到这个问题时,我试图隔离一个更复杂的问题。

编辑:

从一个答案的build议,我编辑代码添加__init__exit (hello3.c):

 #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> MODULE_LICENSE("Dual BSD/GPL"); static int __init hello_init(void) { printk("<1> Hello world!\n"); return 0; } static void __exit hello_exit(void) { printk("<1> Bye, cruel world\n"); } module_init(hello_init); module_exit(hello_exit); 

构build输出:

 make -C /lib/modules/2.6.39-02063904-generic/build M=/home/douglas/kernelmod modules make[1]: Entering directory `/usr/src/linux-headers-2.6.39-02063904-generic' Building with KERNELRELEASE = 2.6.39-02063904-generic CC [M] /home/douglas/kernelmod/hello3.o Building modules, stage 2. Building with KERNELRELEASE = 2.6.39-02063904-generic MODPOST 8 modules CC /home/douglas/kernelmod/hello3.mod.o LD [M] /home/douglas/kernelmod/hello3.ko make[1]: Leaving directory `/usr/src/linux-headers-2.6.39-02063904-generic' 

EDIT2:

hello3.mod.c:

 #include <linux/module.h> #include <linux/vermagic.h> #include <linux/compiler.h> MODULE_INFO(vermagic, VERMAGIC_STRING); struct module __this_module __attribute__((section(".gnu.linkonce.this_module"))) = { .name = KBUILD_MODNAME, .init = init_module, #ifdef CONFIG_MODULE_UNLOAD .exit = cleanup_module, #endif .arch = MODULE_ARCH_INIT, }; static const struct modversion_info ____versions[] __used __attribute__((section("__versions"))) = { { 0xbe4b3e92, "module_layout" }, { 0xb4390f9a, "mcount" }, { 0x5e3b3ab4, "printk" }, }; static const char __module_depends[] __used __attribute__((section(".modinfo"))) = "depends="; MODULE_INFO(srcversion, "D2A869459874C22AB265981"); 

 # grep CONFIG_MODULE_UNLOAD /boot/config-2.6.39-02063904-generic CONFIG_MODULE_UNLOAD=y 

EDIT3:

更有趣的是,我自己编译的一个香草核心没有发生 – 加载和卸载模块都很好。

EDIT4:

我在虚拟机上安装了Oneiric beta 2版本,而3.0.0-11内核也没有任何问题。 所以它似乎仅限于Ubuntu Vanilla PPA内核。 解决这个问题不会太好玩。

所以,在与Canonical协商后,我知道问题是什么:

Ubuntu mainline构建是使用Hardy工具链构建的 ,11.04和11.10工具链对于构建out-of-tree内核模块是不兼容的。

“结构模块”布局取决于HAVE_JUMP_LABEL定义哪个依赖于CC_HAVE_ASM_GOTO定义,这取决于gcc-goto.sh脚本的结果,这取决于正在使用的gcc版本。 当出现不匹配时,模块出口回调(析构函数)的值为NULL,导致模块被标记为[permanent]。

据我从内核源头可以看出,如果一个模块具有初始化函数,但是没有退出函数,则该模块被标记为永久的。

在这种情况下,我不完全确定,但是可能需要分别用__init__exit标记init和exit函数。 (另外,请注意modpost发出的任何警告)

这个问题是由于gcc编译器生成错误的模块二进制格式,导致模块退出函数无法从内核获得适当的卸载信息。

你可以检查你的gcc versioni是否是4.4,如果是,改为使用4.6,问题就会修正:

gcc --version

如果是4.4版本,请删除/ usr / bin / gcc的符号链接,并将其重新链接到/usr/bin/gcc-4.6。 重新编译之后,模块删除应该工作。