Linux内核中的init段、initcall


一、Linux内核初始化段介绍

1、Linux内核初始化段一览表


段名标记方法功能描述
.init.text__init初始化代码
.init.data__initdata初始化数据
.init.setupearly_param/__setup/__setup_param启动参数
.con_initcallconsole_initcall控制台初始化
.initcallearly.initearly_initcall早期initcall函数
.initcall0.initpure_initcall第0级
.initcall1.init/.initcall1s.initcore_initcall/core_initcall_sync第1级
.initcall2.init/.initcall2s.initpostcore_initcall/postcore_initcall_sync第2级
.initcall3.init/.initcall3s.initarch_initcall/arch_initcall_sync第3级
.initcall4.init/.initcall4s.initsubsys_initcall/subsys_initcall_sync第4级
.initcall5.init/.initcall5s.initfs_initcall/fs_initcall_sync第5级
.initcallrootfs.initrootfs_initcallRootfs级
.initcall6.init/.initcall6s.initdevice_initcall/device_initcall_sync第6级
.initcall7.init/.initcall7s.initlate_initcall/late_initcall_sync第7级

2、初始化段大致有四种:


第一类是初始化代码,也就是用__init标记的函数代码,放置在.init.text段;
第二类是初始化数据,也就是用__initdata标记的全局变量,放置在.init.data段;
第三类是启动参数,也就是用early_param()、__setup()、__setup_param()定义的参数变量,放置在.init.setup段;
第四类是用各种*_initcall()定义的函数,称为initcall函数,共有10种,有一种是console_initcall(),其余9种分为9个级别放置在各种.initcall*.init段中。

3、重点介绍第四类初始化段

3.1、console_initcall

console_initcall修饰的函数直接由console_init函数调用;console_init()由内核启动C语言入口start_kernel()直接调用

3.2、early_initcall和剩余9种initcall

第1~7级的initcall函数分为两个子级别:一个不带s,用*_initcall()来定义函数;另一个带s,用*_initcall_sync()来定义函数。在同一级别里,不带s的比带s的子级别先执行。
在9个级别里,最早被执行的是早期initcall函数,然后是第0级initcall,第1级initcall函数,以此类推。在Linux-2.6.20版本,引入了一个特殊的Rootfs级别的initcall,介于fs_initcall和device_initcall之间,其主要用于建立初始化内存盘(initrd/initramfs)中的根文件系统。

除了这9个级别,还有一种用module_init()定义的initcall函数,它跟这9级函数有一个对应关系:如果module_init()定义的函数被直接编译在内核里面,那么它等价于device_initcall();如果编译成模块,则跟其他initcall定义的函数编译成模块的情况相同(如下说明)。
如果*_initcall()修饰的函数被编译成模块,在内核启动完成后,由1号进程间接调用。


二、initcall函数的执行时机


1、early_initcall和其余9种initcall函数在1号进程的启动代码中执行,其执行关系如下:


kernel_init()
kernel_init_freeable();
do_pre_smp_initcalls(); //处理early_initcall()定义的initcall函数
do_basic_setup();
do_initcalls(); //执行其他的8个级别的initcall函数

如上所示:
early_initcall : do_pre_smp_initcalls();
其他initcall : do_basic_setup—>do_initcalls();

2、内核中对initcall的宏定义

内核源码根目录 include/linux/init.h

第195行
196 #define pure_initcall(fn) __define_initcall(fn, 0)
197
198 #define core_initcall(fn) __define_initcall(fn, 1)
199 #define core_initcall_sync(fn) __define_initcall(fn, 1s)
200 #define postcore_initcall(fn) __define_initcall(fn, 2)
201 #define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
202 #define arch_initcall(fn) __define_initcall(fn, 3)
203 #define arch_initcall_sync(fn) __define_initcall(fn, 3s)
204 #define subsys_initcall(fn) __define_initcall(fn, 4)
205 #define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
206 #define fs_initcall(fn) __define_initcall(fn, 5)
207 #define fs_initcall_sync(fn) __define_initcall(fn, 5s)
208 #define rootfs_initcall(fn) __define_initcall(fn, rootfs)
209 #define device_initcall(fn) __define_initcall(fn, 6)
210 #define device_initcall_sync(fn) __define_initcall(fn, 6s)
211 #define late_initcall(fn) __define_initcall(fn, 7)
212 #define late_initcall_sync(fn) __define_initcall(fn, 7s)
213
214 #define __initcall(fn) device_initcall(fn)
215
216 #define __exitcall(fn)
217 static exitcall_t _exitcall##fn __exit_call = fn
218

* 驱动模块的module_init实际是调用的device_initcall去修饰这个函数

3、内核链接脚本记录了各种init代码的存放位置


arch/mips/kernel/vmlinux.lds

60行
. = ALIGN(16384); /* Init code and data */
__init_begin = .;
. = ALIGN(16384); .init.text : AT(ADDR(.init.text) - 0) { _sinittext = .; *(.init.text) _einittext = .; }
.init.data : AT(ADDR(.init.data) - 0) { *(.init.data) *(.init.rodata) . = ALIGN(8); __start_ftrace_events = .; *(_ftrace_events) __stop_ftrace_events = .; . = ALIGN(8); __start_syscalls_metadata = .; *(__syscalls_metadata) __stop_syscalls_metadata = .; . = ALIGN(8); __iommu_of_table = .; *(__iommu_of_table) *(__iommu_of_table_end) . = ALIGN(32); __dtb_start = .; *(.dtb.init.rodata) __dtb_end = .; . = ALIGN(8); __irqchip_begin = .; *(__irqchip_of_table) *(__irqchip_of_end) . = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .; __initcall_start = .; *(.initcallearly.init) __initcall0_start = .; *(.initcall0.init) *(.initcall0s.init) __initcall1_start = .; *(.initcall1.init) *(.initcall1s.init) __initcall2_start = .; *(.initcall2.init) *(.initcall2s.init) __initcall3_start = .; *(.initcall3.init) *(.initcall3s.init) __initcall4_start = .; *(.initcall4.init) *(.initcall4s.init) __initcall5_start = .; *(.initcall5.init) *(.initcall5s.init) __initcallrootfs_start = .; *(.initcallrootfs.init) *(.initcallrootfss.init) __initcall6_start = .; *(.initcall6.init) *(.initcall6s.init) __initcall7_start = .; *(.initcall7.init) *(.initcall7s.init) __initcall_end = .; __con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .; __security_initcall_start = .; *(.security_initcall.init) __security_initcall_end = .; . = ALIGN(4); __initramfs_start = .; *(.init.ramfs) . = ALIGN(8); *(.init.ramfs.info) }
. = ALIGN(4);
.mips.machines.init : AT(ADDR(.mips.machines.init) - 0) {
__mips_machines_start = .;
(.mips.machines.init)
__mips_machines_end = .;
}
/
.exit.text is discarded at runtime, not link time, to deal with

  • references from .rodata
    */
    .exit.text : {
    *(.exit.text)
    }
    .exit.data : {
    *(.exit.data)
    }
    . = ALIGN(16384); .data…percpu : AT(ADDR(.data…percpu) - 0) { __per_cpu_load = .; __per_cpu_start = .; *(.data…percpu…first) . = ALIGN(16384); __per_cpu_user_mapped_start = .; *(.data…percpu…user_mapped…page_aligned) . = ALIGN(1 << 6); *(.data…percpu…user_mapped) *(.data…percpu…user_mapped…shared_aligned) __per_cpu_user_mapped_end = .; . = ALIGN(16384); *(.data…percpu…page_aligned) . = ALIGN(1 << 6); *(.data…percpu…readmostly) . = ALIGN(1 << 6); *(.data…percpu) (.data…percpu…shared_aligned) __per_cpu_end = .; }
    /
  • Align to 64K in attempt to eliminate holes before the
  • .bss…swapper_pg_dir section at the start of .bss. This
  • also satisfies PAGE_SIZE alignment as the largest page size
  • allowed is 64K.
    */
    . = ALIGN(0x10000);
    __init_end = .;
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值