看到很多书上或网上说内核模块的加载内核版本在2.6之后就必须使用这两个module_init和module_exit个宏,但是在内核3.6上发现不使用这两个宏而把初始化和去初始化函数命名为init_module()与cleanup_module()也是可以,就对内核模块的编译和加载产生很大的兴趣,下面来分析一下模块的编译和加载过程,为什么这两种方式都可以?这两个宏到底做了什么?以及模块在执行加载命令后发生了什么?
内核模块的编译
如果大家仔细观察的话在编译内核模块的会产生类似.mod.c的文件
[root@localhost modules_programming]# ls
hello-1.c hello-2.c Makefile
[root@localhost modules_programming]# make
make -C /lib/modules/3.6.10-4.fc18.i686/build M=/myfile/modules_programming modules
make[1]: Entering directory `/usr/src/kernels/3.6.10-4.fc18.i686'
CC [M] /myfile/modules_programming/hello-1.o
CC [M] /myfile/modules_programming/hello-2.o
Building modules, stage 2.
MODPOST 2 modules
CC /myfile/modules_programming/hello-1.mod.o
LD [M] /myfile/modules_programming/hello-1.ko
CC /myfile/modules_programming/hello-2.mod.o
LD [M] /myfile/modules_programming/hello-2.ko
make[1]: Leaving directory `/usr/src/kernels/3.6.10-4.fc18.i686'
在我们执行了make之后,首先顶层makefile遍历执行各个子文件夹得make文件,这个在linux内核模块编程一中有讲到过,在内核的linux-3.6.10/scripts/有一个Makefile.modpost,我们打开这个文件看到模块的编译分为四步
1、各自模块独立.o的编译,就是我们的hello-1.o,hello-2.o
2、根据1中产生的 <module>.o调用modpost产生对应的 <module>.mod.c并编译生 <module>.mod.o
然后把<module>.o与<module>.mod.o链接出<module>.ko
3、将在模块的ELF段中加入确定的信息,包括版本幻数和模块信息
4、这一步就仅用于外部模块的版本控制
这里列出hello-1.c、hello-2.c,区别就在于hello-2.c使用了module_init()与module_exit()两个宏,而hello-1.c没有
/* * hello-1.c - The simplest kernel module. */ #include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> /* Needed for KERN_INFO */ int init_module(void) { printk(KERN_ALERT "Hello world 1.\n"); /* * A non 0 return means init_module failed; module can't be loaded. */ return 0; } void cleanup_module(void) { printk(KERN_ALERT "Goodbye world 1.\n"); }
/* * hello-2.c - Demonstrating the module_init() and module_exit() macros. * This is preferred over using init_module() and cleanup_module(). */ #include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> /* Needed for KERN_INFO */ #include <linux/init.h> /* Needed for the macros */ static int __init hello_2_init(void) { printk(KERN_INFO "Hello, world 2\n"); return 0; } static void __exit hello_2_exit(void) { printk(KERN_INFO "Goodbye, world 2\n"); } module_init(hello_2_init); module_exit(hello_2_exit);
/* Each module must use one module_init(). */ #define module_init(initfn) \ static inline initcall_t __inittest(void) \ { return initfn; } \ int init_module(void) __attribute__((alias(#initfn))); /* This is only required if you want to be unloadable. */ #define module_exit(exitfn) \ static inline exitcall_t __exittest(void) \ { return exitfn; } \ void cleanup_module(void) __attribute__((alias(#exitfn)));
module_init(hello_2_init);就相当于
... int init_module(void) __attribute__((alias(#hello_2_init)));
而在编译过程中生成的hello-1.mod.c,hello-2.mod.c的内容是一样的
/* * hello-1.mod.c - The simplest kernel module. */ #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 char __module_depends[] __used __attribute__((section(".modinfo"))) = "depends=";
[root@localhost modules_programming]# readelf -S hello-1.mod.o
There are 19 section headers, starting at offset 0x6c64:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 000000 00 AX 0 0 4
[ 2] .data PROGBITS 00000000 000034 000000 00 WA 0 0 4
[ 3] .bss NOBITS 00000000 000034 000000 00 WA 0 0 4
[ 4] .modinfo PROGBITS 00000000 000034 000039 00 A 0 0 1
[ 5] .gnu.linkonce.thi PROGBITS 00000000 000080 000180 00 WA 0 0 32
[ 6] .rel.gnu.linkonce REL 00000000 0070e8 000010 08 17 5 4
[ 7] .debug_info PROGBITS 00000000 000200 003ba3 00 0 0 1
[ 8] .rel.debug_info REL 00000000 0070f8 001b38 08 17 7 4
[ 9] .debug_abbrev PROGBITS 00000000 003da3 0002a6 00 0 0 1
[10] .debug_aranges PROGBITS 00000000 004049 000018 00 0 0 1
[11] .rel.debug_arange REL 00000000 008c30 000008 08 17 10 4
[12] .debug_line PROGBITS 00000000 004061 00039a 00 0 0 1
[13] .debug_str PROGBITS 00000000 0043fb 002787 01 MS 0 0 1
[14] .comment PROGBITS 00000000 006b82 00002d 01 MS 0 0 1
[15] .note.GNU-stack PROGBITS 00000000 006baf 000000 00 0 0 1
[16] .shstrtab STRTAB 00000000 006baf 0000b4 00 0 0 1
[17] .symtab SYMTAB 00000000 006f5c 000130 10 18 16 4
[18] .strtab STRTAB 00000000 00708c 000059 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
最后hello-1.o与hello-1.mod.o链接的到hello-1.ko
通过nm产看编译出来的hell-1.ko,hello-2.ko内核模块的符号表,查看区别
[root@localhost modules_programming]# nm hello-1.ko
00000020 T cleanup_module
00000000 T init_module
U mcount
00000000 r __module_depends
00000009 r __mod_vermagic5
U printk
00000000 D __this_module
[root@localhost modules_programming]# nm hello-2.ko
00000000 T cleanup_module
00000000 t hello_2_exit
00000000 t hello_2_init
00000000 T init_module
00000000 r __module_depends
00000009 r __mod_vermagic5
U printk
00000000 D __this_module
hello-2.ko比hello-1.ko多了hello_2_init,hello_2_exit两个符号,但是他们是局部符号
况且init_module与cleanup_module是hello_2_init,hello_2_exit别名,所以就本质上来说hello-1.ko与hello-2.ko是一样
注:
局部符号,对于目标文件的外部不可见
全局符号,外部可见