为什么这个内核模块在 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< /a>)。但它在默认的 2.6.38 内核上运行良好。 (均在 Ubuntu 11.04 x86 上)。
2.6.39 中发生了什么变化?我的代码需要更改什么?
当我遇到这个问题时,我试图隔离一个更复杂的问题。
编辑:
根据答案的建议,我编辑了代码以添加 __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);
构建输出:
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 内核。解决这个问题不会有太大乐趣。
When I load this module:
#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);
(From http://www.freesoftwaremagazine.com/articles/drivers_linux?page=0,2 )
The module get marked as [permanent]
in lsmod
and can't be unloaded, on 2.6.39-02063904-generic (from the Ubuntu PPA). But it works fine on the default 2.6.38 kernel. (Both on Ubuntu 11.04 x86).
What has changed in 2.6.39? and what do I need to change in my code?
I was trying to isolate a more complicated problem when I ran into this issue.
EDIT:
Following a suggestion from an answer I edited the code to add __init
and __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);
The build output:
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");
Also
# grep CONFIG_MODULE_UNLOAD /boot/config-2.6.39-02063904-generic
CONFIG_MODULE_UNLOAD=y
EDIT3:
More interestingly it doesn't happen with a vanilla kernel I've compiled myself - that
loads and unloads modules fine.
EDIT4:
I installed the Oneiric beta 2 build on a VM, and that 3.0.0-11 kernel doesn't have any problem either. So it appears to be limited to the Ubuntu Vanilla PPA kernels. That won't be much fun to resolve.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
因此,在咨询 Canonical 后,我知道问题是什么:
Ubuntu 主线版本 是使用Hardy 工具链以及 11.04 和 11.10 工具链对于构建树外内核模块不兼容。
So, after consultation with Canonical, I know what the problem is:
Ubuntu mainline builds are built with the Hardy tool chain, and the 11.04 and 11.10 tool chains are incompatible for building out-of-tree kernel modules.
“struct module”布局取决于 HAVE_JUMP_LABEL 定义,而 HAVE_JUMP_LABEL 定义取决于 CC_HAVE_ASM_GOTO 定义,而 CC_HAVE_ASM_GOTO 定义取决于 gcc-goto.sh 脚本结果,而 gcc-goto.sh 脚本结果取决于所使用的 gcc 版本。当不匹配时,模块退出回调(析构函数)会获取 NULL 值,这会导致模块被标记为 [permanent]。
"struct module" layout depends on HAVE_JUMP_LABEL define which depends on CC_HAVE_ASM_GOTO define which depends on the gcc-goto.sh script result which depends on gcc version being used. When there is a mismatch, the module exit callback (destructor) gets value of NULL which results in module being marked as [permanent].
据我从内核源代码得知,如果一个模块具有 init 函数但缺少 exit 函数,则该模块将被标记为永久。
在这种情况下我并不完全确定,但您可能需要分别用 __init 和 __exit 标记您的 init 和 exit 函数。 (另外,请注意 modpost 发出的任何警告)
As far as I can tell from the kernel sources, a module is marked permanent if it has an init function, but lacks an exit function.
I'm not entirely sure in this case, but you may want to mark your init and exit functions with
__init
and__exit
respectively. (also, pay attention to any warnings emitted by modpost)此问题是由于旧版 gcc 编译器生成错误的模块二进制格式导致模块退出函数无法从内核获取正确的卸载信息。
您可以检查您的 gcc 版本是否为 4.4,如果是,则更改为使用 4.6,问题将得到解决:
gcc --version
如果是4.4版本,删除/usr/bin/gcc的符号链接并将其重新链接到/usr/bin/gcc-4.6。重新编译后,模块删除应该可以工作。
This issue is due to elder gcc compiler generate wrong module binary format and cause the module exit function can not get proper unload inforamtion from kernel.
You may check if your gcc versioni is 4.4, if that, changing to use 4.6 and problem will fixed:
gcc --version
if 4.4 version, remove the symbol link of /usr/bin/gcc and relink it to /usr/bin/gcc-4.6. The module remove should works after you recompile it.