kernel的initcall函数

 

先来看看这些initcall函数的声明:

/* include/linux/init.h */

/* initcalls are now grouped by functionality into separate

 * subsections. Ordering inside the subsections is determined

 * by link order.

 * For backwards compatibility, initcall() puts the call in

 * the device init subsection.

 */

 

#define __define_initcall(level,fn) /

       static initcall_t __initcall_##fn __attribute_used__ /

       __attribute__((__section__(".initcall" level ".init"))) = fn

 

#define core_initcall(fn)        __define_initcall("1",fn)

#define postcore_initcall(fn)        __define_initcall("2",fn)

#define arch_initcall(fn)        __define_initcall("3",fn)

#define subsys_initcall(fn)            __define_initcall("4",fn)

#define fs_initcall(fn)                     __define_initcall("5",fn)

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

#define late_initcall(fn)         __define_initcall("7",fn)

 

#define __initcall(fn) device_initcall(fn)

 

#define __exitcall(fn) /

       static exitcall_t __exitcall_##fn __exit_call = fn

 

#define console_initcall(fn) /

       static initcall_t __initcall_##fn /

       __attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn

 

#define security_initcall(fn) /

       static initcall_t __initcall_##fn /

       __attribute_used__ __attribute__((__section__(".security_initcall.init"))) = fn

 

#define module_init(x)   __initcall(x);    ß从这里知道module_init的等级为6,相对靠后

#define module_exit(x)  __exitcall(x);

 

 

可以发现这些*_initcall(fn)最终都是通过__define_initcall(level,fn)宏定义生成的。

__define_initcall宏定义如下:

#define __define_initcall(level,fn) /

       static initcall_t __initcall_##fn __attribute_used__ /

       __attribute__((__section__(".initcall" level ".init"))) = fn

 

这句话的意思为定义一个initcall_t型的初始化函数,函数存放在.initcall”level”.init section内。.initcall”level”.init section定义在vmlinux.lds内。

/* arch/arm/kernel/vmlinux.lds */

……

  __initcall_start = .;

   *(.initcall1.init)

   *(.initcall2.init)

   *(.initcall3.init)

   *(.initcall4.init)

   *(.initcall5.init)

   *(.initcall6.init)

   *(.initcall7.init)

  __initcall_end = .;

       ……

正好包括了上面init.h里定义的从core_initcalllate_initcall7level等级的.initcall”level”.init section. 因此通过不同的*_initcall声明的函数指针最终都会存放不同level等级的.initcall”level”.init section内。这些不同levelsectionlevel等级高低依次存放。

 

下面我们再来看看,内核是什么时候调用存储在.initcall”level”.init section内的函数的。

 

内核是通过do_initcalls函数循环调用执行initcall.init section内的函数的,流程如下:

start_kernel -> rest_init -> kernel_thread -> init -> do_basic_setup -> do_initcalls

 

 

这里要分析两个函数: kernel_threaddo_initcalls,这两个函数都定义在init/main.c

1)    kernel_thread

1.static void noinline rest_init(void)

2.    __releases(kernel_lock)

3.{

4.    system_state = SYSTEM_BOOTING_SCHEDULER_OK;

5.

6.    kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);

7.    numa_default_policy();

8.    unlock_kernel();

9.

10.  /*

11.  * The boot idle thread must execute schedule()

12.  * at least one to get things moving:

13.  */

14.  __preempt_enable_no_resched();

15.  schedule();

16.  preempt_disable();

17.

18.  /* Call into cpu_idle with preempt disabled */

19.  cpu_idle();

20.}

6行通过kernel_thread创建一个内核线程执行init函数。(其实这里创建的即Linux的1号进程(init进程), linux中所有其他进程的父进程,有兴趣的可以自己查资料)

 

2)    do_initcalls

1.static void __init do_initcalls(void)

2.{

3.    initcall_t *call;

4.    int count = preempt_count();

5.

6.    for (call = __initcall_start; call < __initcall_end; call++) {

7.           ……

8.           result = (*call)();

9.           ……

10.  }

11.}

其中, initcall_t类型如下:

typedef int (*initcall_t)(void);

 

__initcall_start__initcall_end定义在vmlinux.lds内,表示initcall section的起始和结束地址。

/* arch/arm/kernel/vmlinux.lds */

……

  __initcall_start = .;

   *(.initcall1.init)

   *(.initcall2.init)

   *(.initcall3.init)

   *(.initcall4.init)

   *(.initcall5.init)

   *(.initcall6.init)

   *(.initcall7.init)

  __initcall_end = .;

       ……

因此,上面6-10行代码的作用为按initcall level等级的顺序,依次循环调用预先存储在initcall section内的所有各个级别的初始化函数。这样,kernelinitcall函数的原理我们就搞清楚了。

 

最后要注意的是rest_init是在start_kernel函数内最后部分才被调用执行的,rest_init前包含了kernel一系列的初始化工作。另外,这些不同level等级的initcall.init section本身有一定的执行顺序,因此如果你的驱动依赖于特定的执行顺序的话需要考虑到这一点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
initcall_from_entry函数Linux内核初始化过程中的一部分,其作用是将定义为__initcall的函数指针添加到初始化调用链表中。这个函数被定义在init/main.c中。 具体来说,当内核启动时,会执行一系列的初始化函数,例如设备驱动初始化、内存管理初始化等。这些初始化函数被定义为__initcall,表示它们只在内核启动时被调用一次,并且在初始化完成后会被释放掉。这些函数指针被存储在一个初始化调用链表中。initcall_from_entry函数就是将这些函数指针添加到初始化调用链表中的。 initcall_from_entry函数的参数是一个函数指针,指向一个定义为__initcall的初始化函数函数的实现比较简单,它会将传入的函数指针添加到初始化调用链表中。具体的实现过程可以参考以下代码: ```c static void __init initcall_from_entry(initcall_entry_t *entry) { initcall_t fn = (initcall_t) entry->function; int level = initcall_level(entry); if (level != -1) { list_add_tail(&entry->list, &initcall_levels[level]); printk(KERN_DEBUG "Registered initcall %s with priority %d\n", entry->name, level); } } ``` 在函数中,首先将传入的函数指针转换成initcall_t类型,并通过initcall_level函数获取该函数的优先级level。如果level不等于-1,表示该函数是一个有效的初始化函数,将其添加到对应优先级的初始化调用链表中。最后打印一条日志信息,表示该函数已经被注册。 总的来说,initcall_from_entry函数Linux内核初始化过程中的一部分,负责将定义为__initcall的初始化函数添加到初始化调用链表中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值