Linux 学习笔记1 --- kernel初始化以及module_init(x)解析

粗略的看了下,kernel大致的初始化流程为:

setup.c   kernel/\arch\parisc\kernel    start_parisc     // init arm 

main .c   kernel/init/          start_kernel   // init 

main .c   kernel/init/           rest_init 

main .c   kernel/init/     kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

      

                    wait untill kernel_thread is ready

main .c                       kernel_init

main .c                      do_basic_setup

main .c                      do_initcalls

main .c                      do_one_initcall         

在do_initcalls函数中,会有这样一个调用:

for (fn = __early_initcall_end; fn < __initcall_end; fn++)

do_one_initcall(*fn);

那么 __early_initcall_end和  __initcall_end是怎么来的呢?还得从 module_init(x)这个宏说起。

每个模块最后都会写上这个宏,来指明自己的初始化函数,是怎么样生效的呢?

打开 init.h ,看到如下定义:

 

 

#define __init __section(.init.text) __cold notrace    //  __init开头的函数均会被放置到init.txt,并且只会被调用一次

#define device_initcall(fn) __define_initcall("6",fn,6)  //  device的level为6

#define __define_initcall(level,fn)   static initcall_t __initcall_##fn __attribute_used__   __attribute__((__section__(".initcall" level ".init"))) = fn

#define module_init(x) __initcall(x);

#define __initcall(fn) device_initcall(fn)

由上面的宏定义关系不难得出module_init的真正表达式:

module_init(x)  = static initcall_t __initcall_x6 __attribute_used__   __attribute__((__section__(".initcall" 6".init"))) = x

即定义了initcall_t类型的变量 _initcall_x 保存了X的地址,并 将其存放在 .initcall6.init,运行时由连接器vmlinux.lds 装入指定内存。

或许有些读者想到了,__early_initcall_end和  __initcall_end 存放的就是指定的内存地址,实现方法见vmlinux.lds.h:

#define INIT_CALLS \

VMLINUX_SYMBOL(__initcall_start) = .; \

INITCALLS \

VMLINUX_SYMBOL(__initcall_end) = .;

其含义时,是让__initcall_start指向代码节.initcall.init的节首,而__initcall_end指向.initcall.init的节尾.

而INITCALLS的首地址在上面被,__early_initcall_end保存,故在上述do_initcalls的循环中,vmlinux将可执行的init文件都装入指定的内存地址,按照初始化的优先级,依次调用各自模块的init函数,从而初始化设备。

 

 

 

 

你可能感兴趣的:(thread,linux,Module,null,basic)