Linux驱动加载流程,关键字__init和module_init

参考了大神的博客,自己做了一下总结:

https://blog.csdn.net/qq_16777851/article/details/82121456?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

__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;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值