说明,本文为我学习孟宁老师的linux内核课的一点总结,同时作为上课的作业。
作者:唐建,《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
一、准备工作:将编译linux内核,并将调试信息也编译进去。
使用在linux 根目录执行make menuconfig命令进入配置界面;向下找到kernel hacking 选项并进入;
然后找到“compile-time checks and compiler options”选项并进入;然后向下找到“compile the kernel with debug info”通过空格键选择,然后保存退出。然后make 编译
二、启动调试信息
我们这里创建了一个简单的文件系统rootfs,过程就不细讲。
进入代码根目录,通过qemu 来模式具体的机器:qemu -kernel arch/x86/boot/bzImage -initrd ./rootfs.img -s -S
-s 为后面gdb 调试做准备
-S 然后系统启动后挂起,便于我们调试。
如下图,启动后就停止了
启动gdb开始跟踪:需要另起一个shell 窗口。
先file vmlinux 加载系统的符号表,然后让gdb 和启动的linux 建立连接target remote:1234。
三、开始跟踪linux 内核的启动过程
1、linux 内核都是从start_kernel 函数开始启动的,于是我们先将断点打在start_kernel这里。
可以看到当我们c执行后,就断到start_kernel这里了。下面我们结合代码来分析。
如上图,init_task 是第一个内核进程 pid=0(就是后续的IDLE进程,后面我们再详述)的结构体的初始化,0号进程就是start_kernel,这里是将
这个进程的信息写入进程结构体中。start_kernel这个函数是linux内核的起点,他主要完成系统启动前的初始化工作,这里只标注我认识的(不好意思)
2、init进程启动
这里我们着重看star_kernel函数最后的调用rest_init(),我们可以认为这时候初始化完成了。断点断住它
这个rest_init 这个函数代码如下,他主要通过kernel_thread(调用do_fork)创建了两个进程init、kthread。
init——进程入口kernel_init()。大家看下kernel_init这个函数的启动流程,其实他就是,挨个找各个目录中的init程序来采用shell的形式进行启动,直到找到为止,
所以这里实际上就启动了系统第二个进程,我们通常看到的init进程。
这里代码没有获取pid,所以我们没法打印出来,但是可以知道这个pid应该是1,因为pid是递增的。——有人会问,这里实际上是两个
再看下下面这个是我的linux系统的文件结构和进程列表,大家应该看到了吧我sbin下面有init,所以我的1号进程为/sbin/init.
3、kthread进程启动。
如下图,紧接着创建了kthreadd进程。kthreadd这个进程函数里面是一个死循环。这是第三个进程,所以可以推测pid为2
如下图,当创建完进程到下一步时,我们看到pid为2
4、idle 进程
好了,现在创建好了1、2号进程,我们再说回0号进程。我们前面说了start_kernel就是0号,我们继续往下看他是怎么变回0号进程的。
如上图,最终调用到cpu_idle_loop后是一个死循环。
总结:linux系统的起点是start_kernel,同时他也是第一个进程idle的入口函数。然后这个进程创建了init = 1进程和kthreadd = 2两个进程。
重点是idle进程,这个进程不是通过do_fork出来的,他是制造出来的。后续其他进程都是有1进程fork出来的