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
就是一个设备的初始化注册。