module_init源码分析

10 篇文章 8 订阅
8 篇文章 4 订阅

目录

源码分析

调用流程


源码分析

        本章节我们一块来看一下module_init(x)这个函数,先分析一下它的源码,再梳理一下它的调用流程,参考代码:linux/include/linux/module.h。

/**
 * 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);

        注释:如果是常驻的driver,那么会在do_initcalls的时候调到module_init添加的函数。do_initcalls是如何调用过来的我们后面再讲,继续看__initcall的定义(linux/include/linux/init.h):

#define __initcall(fn) device_initcall(fn)

        此处继续看device_initcall(fn),还是在linux/include/linux/init.h中:

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

        此处继续看__define_initcall(fn, id),还是在linux/include/linux/init.h中:

#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)

        此处继续看__define_initcall(fn, early),还是在linux/include/linux/init.h中:

#define ___define_initcall(fn, id, __sec)			\
	__unique_initcall(fn, id, __sec, __initcall_id(fn))

        继续看___define_initcall(fn, id, .initcall##id),还是在linux/include/linux/init.h中:

#define __unique_initcall(fn, id, __sec, __iid)			\
	____define_initcall(fn,					\
		__initcall_stub(fn, __iid, id),			\
		__initcall_name(initcall, __iid, id),		\
		__initcall_section(__sec, __iid))

        在___define_initcall中,____define_initcall的实现:

#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
#define ____define_initcall(fn, __stub, __name, __sec)		\
	__define_initcall_stub(__stub, fn)			\
	asm(".section	\"" __sec "\", \"a\"		\n"	\
	    __stringify(__name) ":			\n"	\
	    ".long	" __stringify(__stub) " - .	\n"	\
	    ".previous					\n");	\
	static_assert(__same_type(initcall_t, &fn));
#else
#define ____define_initcall(fn, __unused, __name, __sec)	\
	static initcall_t __name __used 			\
		__attribute__((__section__(__sec))) = fn;
#endif

        从上述代码可知在定义了CONFIG_HAVE_ARCH_PREL32_RELOCATIONS的时候使用____define_initcall第一种实现方式,如果没有CONFIG_HAVE_ARCH_PREL32_RELOCATIONS定义,将采用第二种实现方式,由于未定义CONFIG_HAVE_ARCH_PREL32_RELOCATIONS我们暂时看第二种实现方式。

        GNU编译工具链支持用户自定义section,所以我们阅读Linux源码时,会发现大量使用如下一类用法:

__attribute__((__section__("section-name"))) 

        __attribute__用来指定变量或结构位域的特殊属性,其后的双括弧中的内容是属性说明,它的语法格式为:__attribute__ ((attribute-list))。它有位置的约束,通常放于声明的尾部且“ ;” 之前。

        这里的attribute-list为__section__(“.initcall6.init”)。通常,编译器将生成的代码存放在.text段中。但有时可能需要其他的段,或者需要将某些函数、变量存放在特殊的段中,section属性就是用来指定将一个函数、变量存放在特定的段中。

  所以这里的意思就是:定义一个名为 __initcall_XXX_init6 的函数指针变量,并初始化为 XXX_init(指向XXX_init);并且该函数指针变量存放于 .initcall6.init 代码段中。

        通过查看链接脚本( arch/$(ARCH)/kernel/vmlinux.lds.S)来了解 .initcall6.init 段。
  可以看到,.init段中包含 INIT_CALLS,它定义在include/asm-generic/vmlinux.lds.h。INIT_CALLS 展开后可得:

#define INIT_CALLS_LEVEL(level)						\
		VMLINUX_SYMBOL(__initcall##level##_start) = .;		\
		KEEP(*(.initcall##level##.init))			\
		KEEP(*(.initcall##level##s.init))			\
#define INIT_CALLS							\
		VMLINUX_SYMBOL(__initcall_start) = .;			\
		KEEP(*(.initcallearly.init))				\
		INIT_CALLS_LEVEL(0)					\
		INIT_CALLS_LEVEL(1)					\
		INIT_CALLS_LEVEL(2)					\
		INIT_CALLS_LEVEL(3)					\
		INIT_CALLS_LEVEL(4)					\
		INIT_CALLS_LEVEL(5)					\
		INIT_CALLS_LEVEL(rootfs)				\
		INIT_CALLS_LEVEL(6)					\
		INIT_CALLS_LEVEL(7)					\
		VMLINUX_SYMBOL(__initcall_end) = .;

        综上所述,module_init的源码实现可以简化为:

#define module_init(x)	__initcall(x);
|
--> #define __initcall(fn) device_initcall(fn)
    |
    --> #define device_initcall(fn)		__define_initcall(fn, 6)
        |
        --> #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
            |
            --> #define ___define_initcall(fn, id, __sec)			\
	                __unique_initcall(fn, id, __sec, __initcall_id(fn))
                |
                --> #define __unique_initcall(fn, id, __sec, __iid)			\
	                    ____define_initcall(fn,					\
		                    __initcall_stub(fn, __iid, id),			\
		                    __initcall_name(initcall, __iid, id),		\
		                    __initcall_section(__sec, __iid))                 

调用流程

        在#define module_init(x)    __initcall(x)的注释中说到do_initcalls调用module_init。实际上完整的调用流程如下:

asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
|
--> void __init __weak arch_call_rest_init(void)
    |
    --> noinline void __ref rest_init(void)
        |
        --> static int __ref kernel_init(void *unused)
            |
            --> static noinline void __init kernel_init_freeable(void)
                |
                --> static void __init do_basic_setup(void)
                    |
                    --> static void __init do_initcalls(void)

        此处就不熟练调用流程的源码了,后续在系统启动流程再详细讲解。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值