参考了大神的博客,自己做了一下总结:
__init 解析:
->#define __init __section(.init.text) __cold notrace __noretpoline
-->#define __section(S) __attribute__ ((__section__(#S)))
-->#define #define __cold __attribute__((__cold__))
-->#define notrace __attribute__((no_instrument_function))
-->#if defined(RETPOLINE) && !defined(MODULE)
#define __noretpoline __attribute__((indirect_branch("keep")))
#else
#define __noretpoline /一般情况下,定义了MODULE,就会走这个分支**/
#endif
宏展开后,变成以下语句
#define __init __attribute__ ((__section__(#.init.text) __attribute__((__cold__)) __attribute__((no_instrument_function))
在链接脚本vmlinux.lds.S中,可以看到语句:
SECTIONS
{
........
INIT_TEXT_SECTION(8) /*部分text*/
.exit.text : {
ARM_EXIT_KEEP(EXIT_TEXT)
}
.init.data : { /*.init.data*/
INIT_DATA
INIT_SETUP(16)
INIT_CALLS
CON_INITCALL
SECURITY_INITCALL
INIT_RAM_FS
}
......
}
在__init中,我们只需先关注INIT_TEXT_SECTION(8)这个语句就行。下面是语句的宏展开
#define INIT_TEXT_SECTION(inittext_align) \
. = ALIGN(inittext_align); \
.init.text : AT(ADDR(.init.text) - LOAD_OFFSET) { \ /*#define LOAD_OFFSET 0*/
VMLINUX_SYMBOL(_sinittext) = .; \ /*#define VMLINUX_SYMBOL(x) __VMLINUX_SYMBOL(x) --> #define __VMLINUX_SYMBOL(x) x */
INIT_TEXT \ /*#define INIT_TEXT *(.init.text) *(.text.startup) MEM_DISCARD(init.text) *//*#define MEM_DISCARD(sec) *(.mem##sec)*/
VMLINUX_SYMBOL(_einittext) = .; \
}
/*宏展开后*/
INIT_TEXT_SECTION(8)
-> . = ALIGN(8); .init.text : AT(ADDR(.init.text) - 0) { _sinittext = .; *(.init.text) *(.text.startup) .meminit.text _einittext) = .; }
可以看到,连接器最终是把以 __init 修饰的函数都链接到这个自定义的代码段段中去了
下面是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) \
static initcall_t __initcall_##fn##id __used \ /*typedef int (*initcall_t)(void);*/ /*#define __used __attribute__((__used__))*/
__attribute__((__section__(".initcall" #id ".init"))) = fn; \
LTO_REFERENCE_INITCALL(__initcall_##fn##id) /*#define LTO_REFERENCE_INITCALL(x) */
以module_init(hello)为例子,展开后:
module_init(hello) -> __initcall(x) -> device_initcall(fn) -> __define_initcall(fn, 6)
->static initcall_t __initcall_hello6 __attribute__((__used__)) __attribute__((__section__(".initcall6.init"))) = hello;
实际上会在 init.data段中,创建一个__initcall_hello6指针,指向数据段的hello
其中,__initcall_hello6带有属性__section__(".initcall6.init"),可先看一下vmlinux.lds.S中是如何处理的
.init.data : {
INIT_DATA //*(.init.data) *(.meminit.data) *(.init.rodata) *(.meminit.rodata) . = ALIGN(32); __dtb_start = .; *(.dtb.init.rodata) __dtb_end = .; /*还有部分的初始化就不一一列举了*/
INIT_SETUP(16) //. = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .;
INIT_CALLS //__initcall_start = .; *(.initcallearly.init)
//__initcall0start = .; *(.initcall0.init) *(.initcall0s.init)
//__initcall1start = .; *(.initcall1.init) *(.initcall1s.init)
//__initcall2start = .; *(.initcall2.init) *(.initcall2s.init)
//__initcall3start = .; *(.initcall3.init) *(.initcall3s.init)
//__initcall4start = .; *(.initcall4.init) *(.initcall4s.init)
//__initcall5start = .; *(.initcall5.init) *(.initcall5s.init)
//__initcallrootfsstart = .; *(.initcall0.init) *(.initcall0s.init)
//__initcall6start = .; *(.initcall6.init) /*所有被module_init修饰的函数指针,都会被存在该段里*/ *(.initcall6s.init)
//__initcall7start = .; *(.initcall7.init) *(.initcall7s.init)
//__initcall_end = .;
CON_INITCALL
SECURITY_INITCALL
INIT_RAM_FS
}
以下就是我板子上的链接脚本展开:
.init.data : {
INIT_DATA
INIT_SETUP(16)
INIT_CALLS
CON_INITCALL
SECURITY_INITCALL
INIT_RAM_FS
}
/*INIT_DATA 对本章的流程的影响不大,就不一一展开了*/
#define INIT_DATA \
*(.init.data) \
MEM_DISCARD(init.data) \
KERNEL_CTORS() \
MCOUNT_REC() \
*(.init.rodata) \
FTRACE_EVENTS() \
TRACE_SYSCALLS() \
KPROBE_BLACKLIST() \
MEM_DISCARD(init.rodata) \
CLK_OF_TABLES() \
RESERVEDMEM_OF_TABLES() \
CLKSRC_OF_TABLES() \
IOMMU_OF_TABLES() \
CPU_METHOD_OF_TABLES() \
CPUIDLE_METHOD_OF_TABLES() \
KERNEL_DTB() \
IRQCHIP_OF_MATCH_TABLE() \
ACPI_PROBE_TABLE(irqchip) \
ACPI_PROBE_TABLE(clksrc) \
EARLYCON_TABLE()
/*展开后为:*/
-> *(.init.data) *(.meminit.data) *(.init.rodata) *(.meminit.rodata) . = ALIGN(32); __dtb_start = .; *(.dtb.init.rodata) __dtb_end = .;
#define INIT_SETUP(initsetup_align) \
. = ALIGN(initsetup_align); \
VMLINUX_SYMBOL(__setup_start) = .; \
*(.init.setup) \
VMLINUX_SYMBOL(__setup_end) = .;
/*展开后为:*/
-> . = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .;
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start) = .; \
*(.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) = .;
#define INIT_CALLS_LEVEL(level) \
VMLINUX_SYMBOL(__initcall##level##_start) = .; \
*(.initcall##level##.init) \
*(.initcall##level##s.init) \
/*展开后为:*/
->
__initcall_start = .; *(.initcallearly.init)
__initcall0start = .; *(.initcall0.init) *(.initcall0s.init)
__initcall1start = .; *(.initcall1.init) *(.initcall1s.init)
__initcall2start = .; *(.initcall2.init) *(.initcall2s.init)
__initcall3start = .; *(.initcall3.init) *(.initcall3s.init)
__initcall4start = .; *(.initcall4.init) *(.initcall4s.init)
__initcall5start = .; *(.initcall5.init) *(.initcall5s.init)
__initcallrootfsstart = .; *(.initcall0.init) *(.initcall0s.init)
__initcall6start = .; *(.initcall6.init) *(.initcall6s.init)
__initcall7start = .; *(.initcall7.init) *(.initcall7s.init)
__initcall_end = .;
#define CON_INITCALL \
VMLINUX_SYMBOL(__con_initcall_start) = .; \
*(.con_initcall.init) \
VMLINUX_SYMBOL(__con_initcall_end) = .;
/*展开后:*/
->__con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .;
#define SECURITY_INITCALL \
VMLINUX_SYMBOL(__security_initcall_start) = .; \
*(.security_initcall.init) \
VMLINUX_SYMBOL(__security_initcall_end) = .;
/*展开后:*/
->__security_initcall_start = .; *(.security_initcall.init) __security_initcall_end = .;
#define INIT_RAM_FS \
. = ALIGN(4); \
VMLINUX_SYMBOL(__initramfs_start) = .; \
*(.init.ramfs) \
. = ALIGN(8); \
*(.init.ramfs.info)
/*展开后:*/
->. = ALIGN(4); __initramfs_start = .; *(.init.ramfs) . = ALIGN(8); *(.init.ramfs.info)
/*综上*/
.init.data : {
*(.init.data) *(.meminit.data) *(.init.rodata) *(.meminit.rodata) . = ALIGN(32); __dtb_start = .; *(.dtb.init.rodata) __dtb_end = .;
. = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .;
__initcall_start = .; *(.initcallearly.init)
__initcall0start = .; *(.initcall0.init) *(.initcall0s.init)
__initcall1start = .; *(.initcall1.init) *(.initcall1s.init)
__initcall2start = .; *(.initcall2.init) *(.initcall2s.init)
__initcall3start = .; *(.initcall3.init) *(.initcall3s.init)
__initcall4start = .; *(.initcall4.init) *(.initcall4s.init)
__initcall5start = .; *(.initcall5.init) *(.initcall5s.init)
__initcallrootfsstart = .; *(.initcall0.init) *(.initcall0s.init)
__initcall6start = .; *(.initcall6.init) *(.initcall6s.init)
__initcall7start = .; *(.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)
}
上面是.init.data的启动内容,下面是启动流程(部分): kernel\init\main.c
int __init_or_module do_one_initcall(initcall_t fn) /*这个是通用的调用函数,与流程无关*/
{
int count = preempt_count();
int ret;
char msgbuf[64];
if (initcall_blacklisted(fn))
return -EPERM;
if (initcall_debug)
ret = do_one_initcall_debug(fn);
else
ret = fn(); /*这里便是真正调用了module_int()注册的函数指针*/
msgbuf[0] = 0;
if (preempt_count() != count) {
sprintf(msgbuf, "preemption imbalance ");
preempt_count_set(count);
}
if (irqs_disabled()) {
strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));
local_irq_enable();
}
WARN(msgbuf[0], "initcall %pF returned with %s\n", fn, msgbuf);
return ret;
}
static noinline void __init kernel_init_freeable(void) /*kernel 启动时的初始化工作*/
{
......
do_pre_smp_initcalls(); /*最开始初始化的,调用的是.initcallearly.init段里的函数*/
...... /*中间是最了SMP的页面相关的初始化*/
do_basic_setup(); /*这里才是所有的设备真正初始化的地方*/
......
}
static void __init do_pre_smp_initcalls(void)
{
initcall_t *fn;
for (fn = __initcall_start; fn < __initcall0_start; fn++) /*该循环,对应的段是__initcall_start 到 __initcall0start, 也就是*(.initcallearly.init)*/
do_one_initcall(*fn);
}
static void __init do_basic_setup(void)
{
.......
do_initcalls(); /*这里才是所有的设备真正初始化的地方*/
.......
}
static void __init do_initcalls(void)
{
int level;
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) /*每一层都要调用,从0到ARRAY_SIZE(initcall_levels)*/
do_initcall_level(level);
}
static void __init do_initcall_level(int level)
{
initcall_t *fn;
......
for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) /*每一层中,逐个函数调用*/
do_one_initcall(*fn);
}
下面是insmod的流程:
insmod是由用户调用到底层的一个过程,其中就会涉及到系统调用的流程
insmod对应的调用接口是sys_init_module。具体的实现是
SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs);
参考kernel\module.c,下面只分析和内核加载关联的部分,其他的拷贝ELF检查的过程不做分析。
SYSCALL_DEFINE3(init_module, void __user *, umod,
unsigned long, len, const char __user *, uargs)
{
int err;
struct load_info info = { };
err = may_init_module();
if (err)
return err;
pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n",
umod, len, uargs);
err = copy_module_from_user(umod, len, &info); /*elf格式,拷贝到内核中去*/
if (err)
return err;
return load_module(&info, uargs, 0); /*前面的都是做一些基本的检查功能*/
}
static int load_module(struct load_info *info, const char __user *uargs,
int flags)
{
....... /*前面的也是做一些导入的工作*/
return do_init_module(mod);
}
static noinline int do_init_module(struct module *mod)
{
.......
if (mod->init != NULL)
ret = do_one_initcall(mod->init); /*这里跟上述的内核加载调用的函数是一致的,mod->init指向的也是module_init修饰的函数指针*/
if (ret < 0) {
goto fail_free_freeinit;
}
........
return ret;
}