内核模块可以使用两种方式加入进内核:
1.使用insmod等命令动态加载到内核(obj-m);
2.作为内核的一部分静态编译进内核(obj-y);
在linux/init.h文件中
typedef int (*initcall_t)(void);
typedef void (*exitcall_t)(void);
#ifndef MODULE
//静态方式
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
//最终展开为:
//static initcall_t __initcall_test_init6 __used __attribute__((__section__(".initcall"6".init"))) = test_init
//即将初始化函数test_init的地址放在了.initcall6.init的section,内核在启动的时候按级别顺序调用__initcall_start处的函数
//start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls()
#define device_initcall(fn) __define_initcall("6",fn,6)
#define __initcall(fn) device_initcall(fn)
#define module_init(x) __initcall(x);
#define __exitcall(fn) \
static exitcall_t __exitcall_##fn __exit_call = fn
//module_exit在静态编译的时候没有意义,因为静态编译的驱动无法卸载
#define module_exit(x) __exitcall(x);
#else
//动态方式
#define module_init(initfn) \
static inline initcall_t __inittest(void) \ //检查初始化函数的格式是否满足initcall_t类型,当函数格式不匹配时,编译会warnning
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
//为init_module函数定义一个别名,即初始化函数的名字。
//insmod时候,在系统内部会调用sys_init_module()去找到init_module函数的入口地址
//用objdump -t xxx.ko 命令可以看出test_init函数名的地址与init_module函数的地址相同。
//module_exit函数的作用也是检查函数格式和定义别名。
#define module_exit(exitfn) \
static inline exitcall_t __exittest(void) \
{ return exitfn; } \
void cleanup_module(void) __attribute__((alias(#exitfn)));
linux/arch/arm/kernel/vmlinux.lds
.init.data : {
*(.init.data) *(.meminit.data) *(.init.rodata) . = ALIGN(8); __start_ftrace_events = .; *(_ftrace_events) __stop_ftrace_events = .; *(.meminit.rodata) . = ALIGN(3 2); __dtb_start = .; *(.dtb.init.rodata) __dtb_end = .;
. = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .;
__initcall_start = .; *(.initcallearly.init) __initcall0_start = .; *(.initcall0.init) *(.initcall0s.init) __initcall1_start = .; *(.initcall1.init) *(.initcall1s .init) __initcall2_start = .; *(.initcall2.init) *(.initcall2s.init) __initcall3_start = .; *(.initcall3.init) *(.initcall3s.init) __initcall4_start = .; *(.initcal l4.init) *(.initcall4s.init) __initcall5_start = .; *(.initcall5.init) *(.initcall5s.init) __initcallrootfs_start = .; *(.initcallrootfs.init) *(.initcallrootfss.in it) __initcall6_start = .; *(.initcall6.init) *(.initcall6s.init) __initcall7_start = .; *(.initcall7.init) *(.initcall7s.init) __initcall_end = .;
__con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .;
__security_initcall_start = .; *(.security_initcall.init) __security_initcall_end = .;
. = ALIGN(4); __initramfs_start = .; *(.init.ramfs) . = ALIGN(8); *(.init.ramfs.info)
}
示例代码:
#include <linux/module.h> int test_init(void) { printk("test test init\n"); return 0; } void test_exit(void) { printk("test test exit\n"); } module_init(test_init); module_exit(test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("frank"); MODULE_VERSION("1.0");