【Linux API 分析】module_init与module_exit

img
个人主页:董哥聊技术
我是董哥,嵌入式领域新星创作者
创作理念:专注分享高质量嵌入式文章,让大家读有所得!
img

Linux版本:4.19

1、前言

module_initmodule_exit用于我们驱动的加载,卸载,是我们驱动初始化/退出的入口函数。

  • module_init:内核启动时或者动态插入模块时调用
  • module_exit:驱动移除时调用

下面主要分析一下这两个接口的底层实现。

2、调用层次分析

2.1 module_init

#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 */

/*
 * In most cases loadable modules do not need custom
 * initcall levels. There are still some valid cases where
 * a driver may be needed early if built in, and does not
 * matter when built as a loadable module. Like bus
 * snooping debug drivers.
 */
#define early_initcall(fn)		module_init(fn)
#define core_initcall(fn)		module_init(fn)
#define core_initcall_sync(fn)		module_init(fn)
#define postcore_initcall(fn)		module_init(fn)
#define postcore_initcall_sync(fn)	module_init(fn)
#define arch_initcall(fn)		module_init(fn)
#define subsys_initcall(fn)		module_init(fn)
#define subsys_initcall_sync(fn)	module_init(fn)
#define fs_initcall(fn)			module_init(fn)
#define fs_initcall_sync(fn)		module_init(fn)
#define rootfs_initcall(fn)		module_init(fn)
#define device_initcall(fn)		module_init(fn)
#define device_initcall_sync(fn)	module_init(fn)
#define late_initcall(fn)		module_init(fn)
#define late_initcall_sync(fn)		module_init(fn)

#define console_initcall(fn)		module_init(fn)
#define security_initcall(fn)		module_init(fn)

/* Each module must use one module_init(). */
#define module_init(initfn)					\
    static inline initcall_t __maybe_unused __inittest(void)		\
    { return initfn; }					\
    int init_module(void) __copy(initfn) __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) __copy(exitfn) __attribute__((alias(#exitfn)));

#endif

2.2 __initcall

#define __initcall(fn) device_initcall(fn)

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

2.3 device_initcall

#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)  
  
#define __initcall(fn) device_initcall(fn)  

2.4 ___define_initcall

#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
#define ___define_initcall(fn, id, __sec)			\
	__ADDRESSABLE(fn)					\
	asm(".section	\"" #__sec ".init\", \"a\"	\n"	\
	"__initcall_" #fn #id ":			\n"	\
	    ".long	" #fn " - .			\n"	\
	    ".previous					\n");
#else
#define ___define_initcall(fn, id, __sec) \
	static initcall_t __initcall_##fn##id __used \
		__attribute__((__section__(#__sec ".init"))) = fn;
#endif

2.5、module_init调用顺序汇总

module_init
    ---> __initcall
        ---> device_initcall
            ---> __define_initcall(include/linux/init.h)
                ---> ___define_initcall(/include/linux/init.h)

综上,我们调用顺序:module_init(fn)—> __initcall(fn) —> device_initcall(fn) —> __define_initcall(fn, 6)

3、源码分析

通过上面了解,我们最后调用的是___define_initcall的函数,下面我们主要分析该函数的意义。

了解之前呢,我们先来学习一下###的作用!

3.1 # 与 ## 的作用

符号 作用 举例
## “##”符号 可以是连接的意思 例如 _initcall##fn##id 为__initcall_fnid那么,fn = test_init,id = 6时, _initcall##fn##**id为 **__initcall_test_init6
# “#”符号 可以是字符串化的意思 例如 #id 为 “id”,id=6 时,#id 为“6”

3.2 __define_initcall

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

这里我们以module_init(test_init)为例,转换后的结果为:

static initcall_t __initcall_test_init6 __used __attribute__((__section__(".initcall6.init"))) = test_init

通过__attribute__(__section__)设置函数属性,将test_init放在字段.initcall6.init中。

该字段通过链接器链接起来,形成一个列表进行统一管理。

......
__initcall6_start = .; KEEP(*(.initcall6.init)) KEEP(*(.initcall6s.init)) 
......

还记得__define_initcall的定义吗?

#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)  
  
#define __initcall(fn) device_initcall(fn) 

不同的宏定义,被赋予了不同的调用等级,最后将不同的驱动初始化函数统一汇总到__initcallx_start字段统一管理,形成一个有序的列表。

这样,我们在内核中,按照顺序遍历这个列表,最后执行对应的模块初始化函数fn即可实现驱动的初始化。

这篇内容主要分析module_init的调用以及作用,后续再详细分析内核是如何调用初始化函数的。

img

点赞+关注,永远不迷路

img

猜你喜欢

转载自blog.csdn.net/dong__ge/article/details/129354194
今日推荐