linux内核模块编译链接与加载剖析一
来源:互联网 发布:淘宝客服工作室照片 编辑:程序博客网 时间:2024/05/01 09:32
看到很多书上或网上说内核模块的加载内核版本在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);
在linux-3.6.10/include/linux/init.h 有对module_init()与module_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)));
这里通过__attribute__((alias(#hello_2_init)))给hello_2_init换了一个名字init_module
而在编译过程中生成的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=";
<module>.mod.c中定义一个module结构类型的变量struct module __this_module这个变量是放在ELF文件的段名为.gnu.linkonce.this_module的段中,通过readelf工具也可以看到相关的段
[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是一样
注:
局部符号,对于目标文件的外部不可见
全局符号,外部可见
- linux内核模块编译链接与加载剖析一
- linux内核模块编译链接与加载剖析二
- Linux内核模块编译与加载
- Linux内核编译,内核模块编译加载
- Linux 可加载内核模块剖析
- Linux 可加载内核模块剖析
- Linux 可加载内核模块剖析
- Linux 可加载内核模块剖析
- Linux 可加载内核模块剖析
- Linux 可加载内核模块剖析
- Linux 可加载内核模块剖析
- Linux 可加载内核模块剖析
- Linux 可加载内核模块剖析
- Linux 可加载内核模块剖析
- Linux 可加载内核模块剖析
- Linux 可加载内核模块剖析
- Linux 可加载内核模块剖析
- OK6410 linux 内核模块加载--LED内核模块编译加载
- STL中的排序算法 sort stable_sort patition_sort等
- 数论基础
- IOS开发百度地图API-用点生成路线,导航,气泡响应
- IPv6地址详解
- 中文linux安装oracle界面乱码解决方案
- linux内核模块编译链接与加载剖析一
- fs
- TP 触摸芯片通道数
- 历年百度校园招聘笔试题
- 黑马程序员<CSS样式总结>
- apache如何将顶级域名跳转到www子域名中?
- 张小龙:如何把产品做简单
- DB2《SQL Error: SQLCODE=-805, SQLSTATE=51002 解决方法》
- ORA-12154: TNS: 无法解析指定的连接标识符