在基本分析完内核启动流程的之后,还有一个比较重要的初始化函数没有分析,那就是do_basic_setup。在内核init线程中调用了do_basic_setup,这个函数也做了很多内核和驱动的初始化工作,详解如下:
-
/*
-
* 好了, 设备现在已经初始化完成。 但是还没有一个设备被初始化过,
-
* 但是 CPU 的子系统已经启动并运行,
-
* 且内存和处理器管理系统已经在工作了。
-
*
-
* 现在我们终于可以开始做一些实际的工作了..
-
*/
-
static void __init do_basic_setup(void)
-
{
-
cpuset_init_smp();
-
针对SMP系统,初始化内核control group的cpuset子系统。如果非SMP,此函数为空。
-
cpuset是在用户空间中操作cgroup文件系统来执行进程与cpu和进程与内存结点之间的绑定。
-
本函数将cpus_allowed和mems_allwed更新为在线的cpu和在线的内存结点,并为内存热插拨注册了钩子函数,最后创建一个单线程工作队列cpuset。
-
usermodehelper_init();
-
创建一个单线程工作队列khelper。运行的系统中只有一个,主要作用是指定用户空间的程序路径和环境变量, 最终运行指定的user space的程序,属于关键线程,不能关闭。
-
init_tmpfs();
-
driver_init();
-
初始化驱动模型中的各子系统,可见的现象是在/sys中出现的目录和文件
-
init_irq_proc();
-
在proc文件系统中创建irq目录,并在其中初始化系统中所有中断对应的目录。
-
do_ctors();
-
调用链接到内核中的所有构造函数,也就是链接进.ctors段中的所有函数。
-
-
在Linux-2.6.31开始内核启动增加了对构造函数的支持。
-
-
git提交:
-
commit b99b87f70c7785ab1e253c6220f4b0b57ce3a7f7
-
Author: Peter Oberparleiter
-
Date: Wed Jun 17 16:28:03 2009 -0700
-
-
kernel: constructor support
内核:构造函数支持
-
Call constructors (gcc-generated initcall-like functions) during kernel
-
start and module load. Constructors are e.g. used for gcov data
-
initialization.
-
在内核启动和模块挂载时,调用构造函数(gcc生成的类初始化函数)。构造函数就是
-
比如用于初始化gcov数据的函数
-
-
Disable constructor support for usermode Linux to prevent conflicts with
-
host glibc.
-
对于Linux的用户模式禁用构造函数支持,以避免和glibc冲突。
-
-
Signed-off-by: Peter Oberparleiter
-
Acked-by: Rusty Russell
-
Acked-by: WANG Cong
-
Cc: Sam Ravnborg
-
Cc: Jeff Dike
-
Cc: Andi Kleen
-
Cc: Huang Ying
-
Cc: Li Wei
-
Cc: Michael Ellerman
-
Cc: Ingo Molnar
-
Cc: Heiko Carstens
-
Cc: Martin Schwidefsky
-
Cc: Al Viro
-
Signed-off-by: Andrew Morton
-
Signed-off-by: Linus Torvalds
-
$ git tag --contains b99b87f7
v2.6.31
v2.6.31-rc1
v2.6.31-rc2
v2.6.31-rc3
v2.6.31-rc4
v2.6.31-rc5
v2.6.31-rc6
v2.6.31-rc7
v2.6.31-rc8
v2.6.31-rc9
v2.6.32
v2.6.32-rc1
v2.6.32-rc2
v2.6.32-rc3
v2.6.32-rc4
v2.6.32-rc5
v2.6.32-rc6
v2.6.32-rc7
v2.6.32-rc8
-
do_initcalls();
-
调用所有编译内核的驱动模块中的初始化函数。
-
这里就是驱动程序员需要关心的步骤,其中按照各个内核模块初始化函数所自定义的启动级别(1~7),按顺序调用器初始化函数。
-
对于同一级别的初始化函数,安装编译是链接的顺序调用,也就是和内核Makefile的编写有关。
在编写内核模块的时候需要知道这方面的知识,比如你编写的模块使用的是I2C的API,那你的模块的初始化函数的级别必须低于I2C子系统初始化函数的级别(也就是级别数(1~7)要大于I2C子系统)。如果编写的模块必须和依赖的模块在同一级,那就必须注意内核Makefile的修改了。
这方面的知识会在有空的时候总结下,网上也有相关的文章。
-
}
上面的函数调用了driver_init函数,作用是驱动模型子系统的初始化,对于内核驱动工程师来说比较重要,详解如下:
drivers/base/init.c:
-
/**
-
* driver_init - 初始化驱动模型.
-
*
-
* 调用驱动模型初始化函数来初始化它们的子系统。
-
* 由早期的init/main.c中调用。
-
*/
-
void __init driver_init(void)
-
{
-
/* 它们为核心部件 */
-
devtmpfs_init();
-
初始化devtmpfs文件系统,驱动核心设备将在这个文件系统中添加它们的设备节点。
-
这个文件系统可以由内核在挂载根文件系统之后自动挂载到/dev下,也可以在文件系统的启动脚本中手动挂载。
-
devices_init();
-
初始化驱动模型中的部分子系统和kobject:
-
devices
-
dev
-
dev/block
-
dev/char
-
buses_init();
-
classes_init();
-
firmware_init();
-
hypervisor_init();
-
-
/* 这些也是核心部件, 但是必须
-
* 在以上核心中的核心部件之后调用。
-
*/
-
platform_bus_init();
-
system_bus_init();
-
初始化驱动模型中的devices/system子系统
-
cpu_dev_init();
-
初始化驱动模型中的devices/system/cpu子系统
-
memory_dev_init();
-
初始化驱动模型中的devices/system/memory子系统
-
虽然从代码上看这样,但是我在实际的系统中并没有找到/sys/devices/system/memory这个目录。
-
}