Linux设备模型初始化流程

Linux设备模型初始化流程

设备模型的初始化工作在内核初始化函数do_basic_setup中,整体流程可参考下图:

在这里插入图片描述

首先看下do_basic_setup函数:

/*
 * Ok, the machine is now initialized. None of the devices
 * have been touched yet, but the CPU subsystem is up and
 * running, and memory and process management works.
 *
 * Now we can finally start doing some real work..
 */
static void __init do_basic_setup(void)
{
        cpuset_init_smp();  /* reinitialize [cpuset] */
        driver_init();      /* 初始化驱动模型,即在/sys文件系统中建立对应的目录及文件 */
        init_irq_proc();    /*  */
        do_ctors();         /*  */
        usermodehelper_enable();    /* enable the user-mode helper `workqueue` */
        do_initcalls();/* 顺序调用所有编译进内核的驱动模块的初始化函数 */
}

与本文介绍的设备模型相关的即为driver_init和do_initcalls两个函数,下面分别介绍下这两个函数的作用:

driver_init建立了设备驱动模型的框架,后续所有的操作都是基于该框架进行扩充,主要是建立了/sys文件系统下的结构。

/**
 * driver_init - initialize driver model.
 *
 * Call the driver model init functions to initialize their
 * subsystems. Called early from init/main.c.
 */
void __init driver_init(void)   /*驱动初始化  */
{
        /* These are the core pieces */
        devtmpfs_init();    /* 初始化dev文件系统,/dev目录 */
        devices_init();     /* 建立device先关的设备模型,建立/sys/devices、/sys/dev目录 */
        buses_init();       /* 建立设备模型总线的/sys节点目录  */
        classes_init();     /* 建立class类的/sys节点目录*/
        firmware_init();    /* 建立/sys/firmware目录*/
        hypervisor_init();  /*  */

        /* These are also core pieces, but must come after the
         * core core pieces.
         */
        of_core_init();     /*  */
        platform_bus_init();/* 初始化platform总线,建立/sys/bus/platform目录 */
        cpu_dev_init();     /*  */
        memory_dev_init();  /*  */
        container_dev_init();/*  */
}

基本的架构初始化完成之后,就会进行设备的驱动模块初始化流程,也只有设备的初始化流程结束后,讲设备注册到系统上之后才能够使用。而调用驱动初始化函数的即do_initcalls。

do_initcalls从level 低到高依次加载,先加载level 0的初始化函数,最后加载level7s等级的初始化函数;

static void __init do_initcalls(void)
{
        int level;
        size_t len = strlen(saved_command_line) + 1;
        char *command_line;

        command_line = kzalloc(len, GFP_KERNEL);
        if (!command_line)
                panic("%s: Failed to allocate %zu bytes\n", __func__, len);

        for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) {
                /* Parser modifies command_line, restore it each time */
                strcpy(command_line, saved_command_line);
                do_initcall_level(level, command_line);
        }

        kfree(command_line);
}

initcall等级的定义如下:

#define pure_initcall(fn)               __define_initcall(fn, 0)
#define core_initcall(fn)               __define_initcall(fn, 1)
#define core_initcall_sync(fn)          __define_initcall(fn, 1s)
#define postcore_initcall(fn)           __define_initcall(fn, 2)
#define postcore_initcall_sync(fn)      __define_initcall(fn, 2s)
#define arch_initcall(fn)               __define_initcall(fn, 3)
#define arch_initcall_sync(fn)          __define_initcall(fn, 3s)
#define subsys_initcall(fn)             __define_initcall(fn, 4)
#define subsys_initcall_sync(fn)        __define_initcall(fn, 4s)
#define fs_initcall(fn)                 __define_initcall(fn, 5)
#define fs_initcall_sync(fn)            __define_initcall(fn, 5s)
#define rootfs_initcall(fn)             __define_initcall(fn, rootfs)
#define device_initcall(fn)             __define_initcall(fn, 6)
#define device_initcall_sync(fn)        __define_initcall(fn, 6s)
#define late_initcall(fn)               __define_initcall(fn, 7)
#define late_initcall_sync(fn)          __define_initcall(fn, 7s)

可在System.map中查看初始化函数的加载顺序;

ffff800011003f4c T __initcall0_start
ffff800011003f4c t __initcall_bpf_jit_charge_init0
ffff800011003f50 t __initcall_memory_stats_init0
ffff800011003f54 t __initcall_ipc_ns_init0
ffff800011003f58 t __initcall_init_mmap_min_addr0
ffff800011003f5c t __initcall_pci_realloc_setup_params0
ffff800011003f60 t __initcall_net_ns_init0
ffff800011003f64 T __initcall1_start
ffff800011003f64 t __initcall_fpsimd_init1
ffff800011003f68 t __initcall_tagged_addr_init1
ffff800011003f6c t __initcall_enable_mrs_emulation1
ffff800011003f70 t __initcall_kaslr_init1
ffff800011003f74 t __initcall_map_entry_trampoline1
ffff800011003f78 t __initcall_alloc_frozen_cpus1
ffff800011003f7c t __initcall_cpu_hotplug_pm_sync_init1
...
ffff800011004074 T __initcall2_start
ffff800011004074 t __initcall_debug_monitors_init2
ffff800011004078 t __initcall_irq_sysfs_init2
ffff80001100407c t __initcall_dma_atomic_pool_init2
ffff800011004080 t __initcall_audit_init2
...

最终驱动初始化加载的顺序由如下两个方面决定:

1、level等级从低到高

2、同level下看链接顺序

同一个level下初始化的执行顺序与链接顺序有关,即顺序与makefile文件中的排序有关;

obj-y                           += irqchip/    /*先调用module*/
obj-y                           += bus/        /*后调用module*/

obj-$(CONFIG_GENERIC_PHY)       += phy/

# GPIO must come after pinctrl as gpios may need to mux pins etc
obj-$(CONFIG_PINCTRL)           += pinctrl/
obj-$(CONFIG_GPIOLIB)           += gpio/
obj-y                           += pwm/

驱动中如何定义level的呢?

有些需要在很前面初始化的驱动直接将init函数定义为subsys_initcall

subsys_initcall(init_scsi);
module_exit(exit_scsi);

很早以前,不理解模块是如何插入到内核中的,特地的分析了一次 module_init 是如何工作的,在头文件 include/linux/module.h 中定义了:

#define module_init(x)  __initcall(x);

这个 __initcall 又是一个什么鬼?一起来继续深入分析,在 include/linux/init.h 定义如下:

#define __initcall(fn) device_initcall(fn)

原来所谓的 module_init 就是一个设备的初始化注册。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值