您的位置:首页 > 运维架构 > Linux

Linux系统启动分析-从start_kernel到init进程的启动

2017-03-11 23:15 567 查看
#####################################

作者
:张卓
原创作品转载请注明出处:《Linux操作系统分析》MOOC课程 http://www.xuetangx.com/courses/course-v1:ustcX+USTC001+_/about #####################################

一 Linux源代码目录介绍


现在,我们研究Linux内核,在x86平台上,重点需要我们关注的是下面的三个目录:

1. arch/x86

与x86系统架构相关代码,是我们研究的重点

2. init

init目录下存放内核启动相关的代码

main.c中的start_kernel函数相当于普通的C程序的main函数

3. kernel

Linux内核核心代码都在kernel目录下

二 使用gdb跟踪调试内核从start_kernel到init进程启动过程

1 使用gdb跟踪调试内核

进入实验楼环境,执行下面的命令即可开始跟踪查看Linux启动过程:

  1. qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S # 关于-s和-S选项的说明:

  2. # -S freeze CPU at startup (use ’c’ to start execution)

  3. # -s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项

另开一个shell窗口

  1. gdb

  2. (gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表

  3. (gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行

  4. (gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后
Note:设置断点是时候,即可以指定函数名,也可以指定行号。

2. 内核启动分析

内核启动从init/main.c开始执行代码,于是可以设置断点:break start_kernel



start_kernel()函数相当于C语言中的mian函数,程序总是从main函数开始执行,

设置一个断点在系统设置PCB的时候,break  510



smp_task_stack_end_magic(&init_task)

init_task是一个全局变量,即手工创建的PCB,0号进程即最终的idle进程。

接下来执行一些列的初始化,在这里我们需要关注一下下面的几个初始化,在后面我们会继续分析他们。

trap_init()

与中断向量相关的初始化

mm_init()

与存储管理相关的初始化

接下来有一个特别重要的初始化,我们下一个断点看一下:break 569



sched_init()与进程调度相关的初始化

再接下来,还是一些列的初始化,我们直接跳到start_kernel函数的最后,下一个断点在680行:break 680



start_kernel函数调用rest_init,于是进入rest_init函数执行:

为了更直观看到进入rest_init()函数中执行,我们在403行下一个断点:break 403



kernel_thread(kernel_init,NULL,CLOSE_FS)

在此处创建一个进程去执行kernel_init,然后在文件系统中寻找并执行init程序,这个就是系统的1号进程。

接下来执行:

pid = kernel_thread(kthreadd,NULL,CLONE_FS| CLONE_FILES)

这个函数是创建一个内核线程去管理系统资源,也是就系统的2号进程。

在rest_init函数的末尾下一个断点:break  418



cpu_startup_entry(CPUHP_ONLINE)执行这个函数后系统启动完毕, 进入cpu_idle_loop()(函数位于linux-3.18.6/kernel/sched/idle.c)当系统没有进程需要执行时就调度到idle进程, 这个就是0号进程。



1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先。

三 总结

Linux系统启动,从start_kernel开始执行,中间执行一系列的初始化动作,为后面系统启动开始运行作准备。start_kernel函数的最后会调用rest_init,  然后在rest_init里创建一个进程去寻找文件系统中init程序并执行,这个就是系统的1号进程。1,2号进程后,系统会进入cpu_idle_loop()死循环,这个就是原始的启动进程,也就是0号进程;当系统没有进程需要执行的时候,就会调用这个idle进程。

总之,Linux系统启动过程,可以想象成一个超大型程序的执行过程,按顺序执行所有的初始化;当完成所有的工作时,程序不能退出,而要进人一个死循环。

关于更详细的内核启动函数的分析,可以参考一下的链接
http://blog.csdn.net/xichangbao/article/details/52938240

Linux内核启动之后,文件系统启动过程,可以参考下面的链接
http://blog.csdn.net/cl11010/article/details/24484979
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 操作系统