利用gdb跟踪分析Linux内核的启动过程

刘文 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

今天的实验我们使用gdb对Linux内核的源码进行了跟踪调试,主要是内核从start_kernel到init进程启动的部分。init/start_kernel()是内核启动后第一个进程,即PID 0;这里我们使用实验楼的虚拟机进行实验。
首先使用实验楼虚拟机打开shell,内核启动完成后进入menu程序,支持三个命令help、version和quit,当然也可以添加更多的命令,实验楼截图如下:


接下来我们启动gdb,开始跟踪调试内核。使用命令在gdb界面中加载符号表,并建立gdb和gdbserver之间的连接,并且设置断点为start_kernel,之后按c让qemu上的Linux继续运行:


下面我们来研究一下源代码从而理解一下Linux内核启动的过程。找到init/main.c文件中的start_kernel函数,代码如下:


这个函数进行各种模块的初始化工作,重点关注这一句代码:
set_task_stack_end_magic(&init_task);
这一句代码用于启动0号进程。它的主体动作都是是init_task中进行的,其内核栈通过静态方式分配的。再来关注一下start_kernel函数最后结尾时的这句代码:
rest_init();
在初始化完毕后,调用了函数rest_init,该函数非常重要,它创建了1号(用户态的第1个进程)和2号进程(内核态的祖先),包括pid1的kernel_init(line403),以及pid2的kthreadd(line405)。与此同时,该函数在执行后期将自己变为idle进程(line420),该进程是一个while(1)的loop。

从代码中可以看出,对于1号进程,我们可以把它为三个过程:初始化、调度过程以及执行过程,首先分析其初始化过程。1号进程的初始化是在kernel_thread中进行的,它会将kernel_init的地址传入。其具体工作在kernel/fork.c中的copy_process()中进行,我们打开代码进行分析:

函数的调度是通过函数schedule_preempt_disabled()来启动,在kernel_init处打上断点,然后查看其堆栈信息:

下面我们分析一下1号进程的执行,它的执行是在kernel_init()函数中进行的,我们查看一下这个函数:

函数kernel_init调用run_init_process,启动文件根目录的/init程序,而init就是我们之前编译出来的进程。因此我们可以看到0号进程会调用execute()命令将init作为一个可执行程序运行起来:

这样的话整个的分析过程就结束了,此时menuOS可以正常地运行起来。
总结:
  0号进程是通过直接给定内存地址来启动的,因此它是一个很特殊的进程;1号进程是第一个用户态进程,它是由0号进程来创建的。创建过程是0号进程先将自己的内存空间复制一份,然后再将1号进程的信息写入;而1号进程的实际启动是通过kernel_init来执行的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值