Linux中的module_init与module_exit

1. module_init与module_exit宏

module_init与module_exit宏定义在include/linux/module.h文件中,部分代码如下:

#ifndef MODULE
/**
 * module_init() - driver initialization entry point
 * @x: function to be run at kernel boot time or module insertion
 *
 * module_init() will either be called during do_initcalls() (if
 * builtin) or at module insertion time (if a module).  There can only
 * be one per module.
 */
#define module_init(x)	__initcall(x);

/**
 * module_exit() - driver exit entry point
 * @x: function to be run when driver is removed
 *
 * module_exit() will wrap the driver clean-up code
 * with cleanup_module() when used with rmmod when
 * the driver is a module.  If the driver is statically
 * compiled into the kernel, module_exit() has no effect.
 * There can only be one per module.
 */
#define module_exit(x)	__exitcall(x);

#else /* MODULE */
...
...
#define module_init(initfn)					\
	static inline initcall_t __maybe_unused __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 __maybe_unused __exittest(void)		\
	{ return exitfn; }					\
	void cleanup_module(void) __attribute__((alias(#exitfn)));

#endif

宏定义MODULE在静态编译链接时不定义,在动态编译链接时定义。通过Makefile中obj+y(静态编译)或obj+m(动态编译)来区分。

1.1 静态编译

当采用静态编译时,module_init定义如下:

#define module_init(x)	__initcall(x);
#define module_exit(x)	__exitcall(x);

__initcall和__exitcall宏定义在include/linux/init.h中,如下:

#define __initcall(fn) device_initcall(fn)
#define __exitcall(fn)						\
	static exitcall_t __exitcall_##fn __exit_call = fn

module_exit宏到此展开完成,当我们调用module_exit(my_exit)时,结果如下:

static exitcall_t __exitcall_my_exitl = my_exit

接下来继续对device_initcall展开,device_initcall的宏定义如下:

#define device_initcall(fn)		__define_initcall(fn, 6)

继续对__define_initcall展开,如下:

#define __define_initcall(fn, id) \
	static initcall_t __initcall_name(fn, id) __used \
	__attribute__((__section__(".initcall" #id ".init"))) = fn;

__initcall_name展开如下:

#define __initcall_name(fn, id) 	__initcall_##fn##id

至此,module_init完全展开。
在静态编译过程中,如果使用module_init(my_init),结果如下:

static initcall_t __initcall_my_init_6 __used __attribute__((__section__(".initcall6.init"))) = my_init

该语句的作用:定义了一个initcall_t函数指针变量,指针指向自己定义的初始化函数。该指针变量在链接时会被放到section(.initcall6.init)。

而初始化函数被调用的过程可以参照以下链接:https://blog.csdn.net/jzjhome/article/details/72764931

1.2 动态编译

当采用动态编译时,module_init和module_exit定义如下:

#define module_init(initfn)					\
	static inline initcall_t __maybe_unused __inittest(void)		\
	{ return initfn; }					\
	int init_module(void) __attribute__((alias(#initfn)));
#define module_exit(exitfn)					\
	static inline exitcall_t __maybe_unused __exittest(void)		\
	{ return exitfn; }					\
	void cleanup_module(void) __attribute__((alias(#exitfn)));

这两个宏的主要作用就是给输入的函数取别名:将init_module定义为initf函数n的别名,即定义一个init_module变量,指向initfn;将cleanup_module定义为exitfn函数的别名,即定义一个cleanup_module变量,指向exitfn。

具体的编译和加载过程可以参照以下链接:https://blog.csdn.net/yueqian_scut/article/details/46694229

2. 其他init宏

除了module_init外,还有一些与init相关的宏定义:

/*
 * A "pure" initcall has no dependencies on anything else, and purely
 * initializes variables that couldn't be statically initialized.
 *
 * This only exists for built-in code, not for modules.
 * Keep main.c:initcall_level_names[] in sync.
 */
#define pure_initcall(fn)		__define_initcall(fn, 0)
#define core_initcall(fn)		__define_initcall(fn, 1)
#define core_initcall_sync(fn)		__define_initcall(fn, 1s)
#define postcore_initcall(fn)		__define_initcall(fn, 2)
#define postcore_initcall_sync(fn)	__define_initcall(fn, 2s)
#define arch_initcall(fn)		__define_initcall(fn, 3)
#define arch_initcall_sync(fn)		__define_initcall(fn, 3s)
#define subsys_initcall(fn)		__define_initcall(fn, 4)
#define subsys_initcall_sync(fn)	__define_initcall(fn, 4s)
#define fs_initcall(fn)			__define_initcall(fn, 5)
#define fs_initcall_sync(fn)		__define_initcall(fn, 5s)
#define rootfs_initcall(fn)		__define_initcall(fn, rootfs)
#define device_initcall(fn)		__define_initcall(fn, 6)
#define device_initcall_sync(fn)	__define_initcall(fn, 6s)
#define late_initcall(fn)		__define_initcall(fn, 7)
#define late_initcall_sync(fn)		__define_initcall(fn, 7s)

其过程基本类似,只是在链接的过程中会被放到不同的段中,从而导致调用的顺序不一样。调用顺序是从.initcall1.init段到.initcall7.init依次进行的,但在各个子段内,函数的调用顺序与链接顺序相关,所以是不确定的。

你可能感兴趣的:(Linux驱动开发)