Linux内核源码分析 linux内核情景分析目录
标签:rest_initLinux分类: Linux内核研究
前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化。在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并启动内核线程init。这个函数虽然意思为剩下的初始化,但是这个“剩下”的可是内容颇多,下面详细分析如下:
/*
* 我们必须确定在一个非__init函数或
* 其他根线程(root thread)和初始化线程(init thread)间的竞态。
* (这种竞态可能导致start_kernel在根线程运作到cpu_idle前被free_initmem“收割”。)
*
*
*gcc-3.4 偶尔会将这个函数作为内联函数, 所以使用了noinline.
*/
static __initdata DECLARE_COMPLETION(kthreadd_done);
点击(此处)折叠或打开
定义一个complete变量来告诉init线程:kthreads线程已经创建完成。
从前似乎不是用complete锁,而是用大内核锁。
static noinline void __init_refok rest_init(void)
{
int pid;
rcu_scheduler_starting();
点击(此处)折叠或打开
内核RCU锁机制调度启动,因为下面就要用到
/*
*我们必须先创建init内核线程,这样它就可以获得pid为1。
*尽管如此init线程将会挂起来等待创建kthreads线程。
*如果我们在创建kthreadd线程前调度它,就将会出现OOPS。
*/
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
点击(此处)折叠或打开
创建kernel_init内核线程,内核的1号进程!!!!!
numa_default_policy();
点击(此处)折叠或打开
设定NUMA系统的内存访问策略为默认
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
点击(此处)折叠或打开
创建kthreadd内核线程,它的作用是管理和调度其它内核线程。
它循环运行一个叫做kthreadd的函数,该函数的作用是运行kthread_create_list全局链表中维护的内核线程。
调用kthread_create创建一个kthread,它会被加入到kthread_create_list 链表中;
被执行过的kthread会从kthread_create_list链表中删除;
且kthreadd会不断调用scheduler函数让出CPU。此线程不可关闭。
上面两个线程就是我们平时在Linux系统中用ps命令看到:
点击(此处)折叠或打开
tekkaman@Super-MAGI:~/development/analyze/linux-3.0$ ps -A
PID TTY TIME CMD
1 ? 00:00:00 init
2 ? 00:00:00 kthreadd
......
rcu_read_lock();
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
rcu_read_unlock();
complete(&kthreadd_done);
点击(此处)折叠或打开
获取kthreadd的线程信息,获取完成说明kthreadd已经创建成功。并通过一个complete变量(kthreadd_done)来通知kernel_init线程。
/*
*为让系统运作起来,
*bootidle线程必须至少执行一次schedule():
*/
init_idle_bootup_task(current);
点击(此处)折叠或打开
设置当前进程为idle(闲置)进程类。
preempt_enable_no_resched();
点击(此处)折叠或打开
使能抢占,但不重新调度
schedule();
点击(此处)折叠或打开
执行调度,切换进程。
preempt_disable();
点击(此处)折叠或打开
进程调度完成,回到这里,禁用抢占。
/*在抢占禁用时调用cpu_idle*/
cpu_idle();
点击(此处)折叠或打开
此时内核本体进入了idle状态,用循环消耗空闲的CPU时间片,该函数从不返回。在有其他进程需要工作的时候,该函数就会被抢占!这个函数因构架不同而异。
}
在以上的函数中,内核创建了两个内核线程,一个是内核线程的管理者,另一个是内核初始化线程init,后者是我们分析内核启动需要关注的,这个线程继续做系统的初始化(其中就包含了设备驱动系统):
点击(此处)折叠或打开
下面这个函数就是内核init线程运行的函数,它将完成设备驱动程序的初始化,并调用init_post函数启动用户空间的init进程。
static int __init kernel_init(void * unused)
{
/*
*等待kthreadd的启动完成.
*/
wait_for_completion(&kthreadd_done);
/*
*init可以在任何节点(node)分配到内存页
*/
set_mems_allowed(node_states[N_HIGH_MEMORY]);
/*
*init可以在任何CPU上运行.
*/
set_cpus_allowed_ptr(current, cpu_all_mask);
点击(此处)折叠或打开
增加当前进程的CPU亲和力,使所有的CPU(如果是SMP)都可以运行本线程。
线程可以被迁移到被设置掩码的CPU上运行,但如果在位掩码中删除该CPU位,此线程就不会在那个CPU上运行。
cad_pid = task_pid(current);
点击(此处)折叠或打开
cad_pid为接收Ctrl-alt-del操作的INT信号的进程ID,此处很明显是设为了init的PID
smp_prepare_cpus(setup_max_cpus);
do_pre_smp_initcalls();
lockup_detector_init();
smp_init();
sched_init_smp();
点击(此处)折叠或打开
以上代码是在SMP系统做准备,激活所有CPU,并开始SMP系统的调度。
do_basic_setup();
点击(此处)折叠或打开
到此,与构架相关的部分已经初始化完成了,do_basic_setup函数主要是初始化设备驱动,完成其他驱动程序(直接编译进内核的模块)的初始化。内核中大部分的启动数据输出(都是各设备的驱动模块输出)都是这里产生的。
此函数比较重要,以后会详细分析!
/*打开根文件系统中的 /dev/console , 此处不可失败*/
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.n");
点击(此处)折叠或打开
这是kernel_init(以后的init进程)打开的第一个文件,它也就成为了标准输入。
这里需要打开 /dev/console,如果没有这个节点,系统就出错。这个错误信息也是经常碰到的。可能的原因是:
1、制作文件系统的时候忘记创建/dev/console节点
2、文件系统挂载问题,挂载上的文件系统不是什么都没有,就是挂错了节点。
(void) sys_dup(0);
(void) sys_dup(0);
点击(此处)折叠或打开
复制两次标准输入(0)的文件描述符(它是上面打开的/dev/console,也就是系统控制台):
一个作为标准输出(1)
一个作为标准出错(2)
现在标准输入、标准输出、标准出错都是/dev/console了。
这个console在内核启动参数中可以配置为某个串口(ttySn、ttyOn等等),也可以是虚拟控制台(tty0)。所以我们就在串口或者显示器上看到了之后的系统登录提示。
/*
*检查是否有早期用户空间的init程序。如果有,让其执行
*
*/
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
/*
*Ok, 我们已经完成了启动初始化, and
*且我们本质上已经在运行。释放初始化用的内存(initmem)段
*并开始用户空间的程序..
*/
init_post();
return 0;
}
在内核init线程的最后执行了init_post函数,在这个函数中真正启动了用户空间进程init,详解如下:
/*这是一个非__init函数。强制让它为非内联函数,以防 gcc
*让它内联到init()中并成为init.text段的一部分。
*/
点击(此处)折叠或打开
从此函数名可知,这个函数是运行在用户空间的init程序之前
static noinlineintinit_post(void)
{
/*在释放内存前,必须完成所有的异步 __init 代码*/
async_synchronize_full();
free_initmem();
点击(此处)折叠或打开
释放所有init.* 段中的内存。
mark_rodata_ro();
点击(此处)折叠或打开
通过修改页表,保证只读数据段为只读属性。大部分构架为空函数。
system_state=SYSTEM_RUNNING;
点击(此处)折叠或打开
设置系统状态为运行状态
numa_default_policy();
点击(此处)折叠或打开
设定NUMA系统的内存访问策略为默认
current->signal->flags|=SIGNAL_UNKILLABLE;
点击(此处)折叠或打开
设置当前进程(init)为不可以杀进程(忽略致命的信号)
if(ramdisk_execute_command){
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING"Failed to execute %sn",
ramdisk_execute_command);
}
点击(此处)折叠或打开
如果ramdisk_execute_command有指定的init程序,就执行它。
/*
*我们尝试以下的每个函数,直到函数成功执行.
*
*如果我们试图修复一个真正有问题的设备,
*Bourne shell 可以替代init进程。
*/
if(execute_command){
run_init_process(execute_command);
printk(KERN_WARNING"Failed to execute %s. Attempting "
"defaults...n",execute_command);
}
点击(此处)折叠或打开
如果execute_command有指定的init程序,就执行它。
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No init found. Try passing init= option to kernel. "
"See Linux Documentation/init.txt for guidance.");
点击(此处)折叠或打开
在检查完ramdisk_execute_command和execute_command为空的情况下,顺序执行以下初始化程序:如果都没有找到就打印错误信息。这也是我们做系统移植的时候经常碰到的错误信息,出现这个信息很有可能是:
1、你的启动参数配置有问题,通过 指定了init程序,但是没有找到,且默认的那四个程序也不在文件系统中。
2、文件系统挂载有问题,文件不存在
3、init程序没有执行权限
点击(此处)折叠或打开
至此,内核的初始化结束,正式进入了用户空间的初始化过程!!
}
博客推荐文章
kernel 启动过程之四start_kernel中的rest_init函数到init进程 (2012-06-30 10:43:41)
Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) (2012-04-12 13:56:49)
Linux系统x86架构初始化流程 (2011-10-31 00:06:20)
busybox内init进程启动过程 (2011-11-30 21:40:35)
system.map文件解析 (2012-02-07 16:55:56)
更多阅读
股票公式全解析:16 股票软件源码引入
股票公式全解析:[16]股票软件源码引入——简介我们上一篇文章主要说明了大智慧的源码引入的基本方法,大家在使用这个源码的时候一定要注意具体的设置,按照我的要求一步一步实现,源码的编写我会专门有一个介绍,现在我继续说明其他股票软件
易语言进度条源码 精易论坛
易语言进度条源码——简介今天,我给大家带来如何弄进度条!易语言进度条源码——工具/原料电脑易语言易语言进度条源码——方法/步骤易语言进度条源码 1、打开易语言!拉
绝对比美黑马赢家的黑马营通达信主图、双核及系列选股公式源码 宜兴环保黑马营
1、回马枪B公式源码:当日成本:=IF(C>REF(C,1),(3*H+4*C+3*OPEN+2*L)/12,(2*H+4*C+3*OPEN+3*L)/12);疯牛线:=EXPMEMA(当日成本,3);龙头线:=EXPMEMA(当日成本,8);慢牛线:=EXPMEMA(当日成本,25);生命线:=EXPMEMA(当日成本,79);牛熊线:=
windows中下载android源码 android系统源码下载
由于ubuntu出现问题了,repogit下载android总是出现问题,因此寻求在windows下下砸android的源码1. 进入http://code.google.com/p/msysgit/下载最新的Git-1.7.0.2-pr
动态百分比公式源码! 百分比计算公式
动态百分比公式源码!a1:=hhv(h,256);a2:=llv(l,256);m1:ma(c,64);m2:ma(c,256);k1:(0.618*a1+0.382*a2);k2:(0.5*a1+0.5*a2);k3:(0.382*a1+0.618*a2);说明:一 代码说明