Linux4.1.15启动流程分析--内核模块的初始化机制

接上一篇文章
static void __init do_basic_setup(void)
{
cpuset_init_smp();
usermodehelper_init();
shmem_init();
driver_init();
init_irq_proc(); /* create /proc/irq */
do_ctors();
usermodehelper_enable();
do_initcalls();
random_int_secret_init();
}

void __init driver_init(void)
{
/* These are the core pieces /
devtmpfs_init(); // 初始化devtmpfs
devices_init(); // 初始化sysfs
buses_init(); // sys/bus sys/system
classes_init(); // sys/class
firmware_init(); // sys/firmware 设备树放在这里
hypervisor_init();
/
These are also core pieces, but must come after the
* core core pieces.
*/
platform_bus_init(); // 初始化platform_bus
cpu_dev_init(); // 初始化驱动模型中的devices/system/cpu子系统
memory_dev_init();
container_dev_init();
of_core_init();
}

static void __init do_initcalls(void)
{
int level;
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
// 内核模块的初始化函数都在这里执行
// 根据调用级别,从level0-leveln分别执行内核模块的初始化函数
}

static void __init do_initcall_level(int level)
{
initcall_t fn;
/
typedef int (*initcall_t)(void); 函数指针 */
strcpy(initcall_command_line, saved_command_line);
parse_args(initcall_level_names[level],
initcall_command_line, __start___param,
__stop___param - __start___param,
level, level,
&repair_env_string);
//fn为函数指针
for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
do_one_initcall(fn);
/
从initcall_levels数据中取出函数指针,并将指针指向的内容传递给do_one_initcall(),在do_one_initcall()执行函数中的内容。这个地方不好理解,但却是非常仰慕设计内核的这些大神们。
extern initcall_t __initcall_start[];
extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[];
extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[];
extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[];
extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[];
extern initcall_t __initcall_end[];
static initcall_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
}; */
}

  1. 调用所有编译进内核的驱动的初始化函数,按照各个内核模块初始化函数所定义的启动级别(1~7), 按顺序调用初始化函数;
  2. 对于同一级别的初始化函数,安装编译是链接的顺序调用,和内核的Makefile编写有关;
  3. 基于某一子系统的驱动,其初始化函数的级别必须低于该子系统初始化函数的级别,如果编写的模块必须和依赖的模块在同一级,就必须Makefile的编写了。

include/linux/init.h中关于启动级别的定义:
#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)

驱动开发中常用的module_init ()在不支持模块编译的系统中展开为:
module_init ===》 #define __initcall(fn) device_initcall(fn) ===》__define_initcall(fn, 6)

如果是编译为模块,则如下
#define early_initcall(fn) module_init(fn)
#define core_initcall(fn) module_init(fn)
#define core_initcall_sync(fn) module_init(fn)
#define postcore_initcall(fn) module_init(fn)
#define postcore_initcall_sync(fn) module_init(fn)
#define arch_initcall(fn) module_init(fn)
#define subsys_initcall(fn) module_init(fn)
#define subsys_initcall_sync(fn) module_init(fn)
#define fs_initcall(fn) module_init(fn)
#define fs_initcall_sync(fn) module_init(fn)
#define rootfs_initcall(fn) module_init(fn)
#define device_initcall(fn) module_init(fn)
#define device_initcall_sync(fn) module_init(fn)
#define late_initcall(fn) module_init(fn)
#define late_initcall_sync(fn) module_init(fn)

#define console_initcall(fn) module_init(fn)
#define security_initcall(fn) module_init(fn)

/* Each module must use one module_init(). */
#define module_init(initfn)
static inline initcall_t __inittest(void)
{ return initfn; }
int init_module(void) attribute((alias(#initfn)));

do_initcalls从内存的特定区域中取出内核模块初始化函数的指针,特定的内存区域即指
__initcall_start 到 __initcall_end之间的内存区域。然后来调用它的。不同等级的初始化函数指针放在自己的特定内存区域中。vmlinux.lds.h中定义了内存中关于初始化数据的section,如下:

#define INIT_DATA_SECTION(initsetup_align)
.init.data : AT(ADDR(.init.data) - LOAD_OFFSET) {
INIT_DATA
INIT_SETUP(initsetup_align)
INIT_CALLS
CON_INITCALL
SECURITY_INITCALL
INIT_RAM_FS
}
其中INIT_CALLS 定义了内核模块中初始化等级所在的section,

#define INIT_CALLS_LEVEL(level)
VMLINUX_SYMBOL(__initcall##level##_start) = .;
*(.initcall##level##.init)
*(.initcall##level##s.init) \

#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) = .;

INIT_CALLS_LEVEL(0)展开为
VMLINUX_SYMBOL(__initcall0_start) = .;
*(.initcall0.init)
*(.initcall0.init)
__initcall0_start就是会被do_initcalls调用的一个变量,它被do_initcalls用于定位初始化等级0的起始地址,进而找到定义为初始化等级0的所有初始化函数,并调用它们。
所有等级的初始化函数都被放在从__initcall_start到__initall_end之间的内存区域中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值