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

1.给内核加入调试信息

ubunte32位下要安装 libncurses5-dbg 库,直接apt-get安装即可。

打开图形化内核配置:

加入调试信息 Kernel hacking –> Compile-time checks and compiler options –> Compile the kernel with debug info,单击Y选中:

kernel0

save之后重新make即可加入调试信息。

2.调试Kernel

要想调试kernel就需要在kernel启动前将其挂起:

其中-S选项表示将kernel在启动时挂起,-s选项表示gdbserver打开TCP端口1234进行监听。

接下来另外打开一个窗口作为客户端,用gdb连接gdbserver:

接下来就可以下断点继续运行了。

3.从源码中跟踪init进程的启动过程

init进程是Linux操作系统下第一个用户态进程,通过pstree命令可以看到init进程是所有进程的祖先:

initprocess

init进程是从start_kernel函数中(/linux-3.18.6/init/main.c)开始执行的,这个函数被调用之前都是一些系统初始化操作(汇编语言编写), start_kernel不仅仅完成了内核初始化还启动了init进程,接下来就详细分析下init的启动过程:

start_kernel函数开始进一步设置了init_task,并且进行了一系列的初始化操作(中断处理初始化,内存管理初始化等),这里首先要看看这个init_task(/linux-3.18.6/init/init_task.c):

它其实就是一个task_struct,与用户进程的task_struct一样, task_struct中保存了一个进程的所有基本信息,如进程状态,栈起始地址,进程号pid等:

在进入start_kernel时看看这个init_task:

init5

可以看到,这个init_task进程的pid就是0,也就是说在之前的初始化工作中由内核创建并初始化了init_task,这个进程只在内核中出现,它也就是所谓的0号进程。

我们关心的是init进程(1号进程),所以跳过一堆的初始化来到rest_init函数(/linux-3.18.6/init/main.c):

让我们先来看看kernel_thread这个函数(/linux-3.18.6/kernel/fork.c),从源码中我们得知,它本质上是调用了do_fork来创建一个进程(内核中没有线程的概念,本质上都是进程):

函数中第一个参数是一个函数指针,也就是说内核此时fork出了一个新进程来执行kernel_init函数(低版本内核中这个函数名为init,为了区分init进程所以将其改为了kernel_init) 。在kernel_init函数正式启动了init进程:

这里要注意一定,正常情况init可执行文件是放在/sbin/init,/etc/init,/bin/init,/bin/sh之中,但是我们的MenuOS并没有模拟这些目录,init可执行程序是直接放在rootfs目录,即模拟内核的根目录下,这时候内核又该怎么执行init呢?答案就在上面几句代码,只需传入自定义的目录即可,跟踪调试如下:

init3

可以看到执行run_init_process时传入了/init路径,也就是模拟的根目录。至此,1号init进程就被启动起来了。

4.init进程与idle进程

继续上面的分析,在启动完init进程后继续回到rest_init函数。rest_init函数在启动完init进程后并没有退出,而是继续往下执行道了cpu_startup_entry函数(/linux-3.18.6/kernel/sched/idle.c):

在cpu_idle_loop中其实就是进入了一个无限循环(/linux-3.18.6/kernel/sched/idle.c

也就是说原执行流在fork出init进程后,把自己变成了idle进程。用一张图来简单梳理下init进程和idle进程的启动流程:

init4

最后对init进程和idle进程的作用做一个小总结:

  • 进程0:Linux引导中创建的第一个进程(只在内核中存在),完成加载系统后,演变为进程调度、交换及存储管理进程(idle进程)。
  • 进程1:init 进程,由0号进程创建,完成系统的初始化. 是系统中所有其它用户进程的祖先进程。

5.总结

本文简单分析了init进程和idle进程的启动流程,idle进程(0号进程)其实是最“原始”的进程,它由init_task来标识,并且fork出了init进程(1号),在内核初始化的结尾,原始进程完全变为idle进程(进入无限循环)。由于内核代码过于庞大,很多细节暂时没时间详细分析,但是通过对内核启动代码的分析,对内核的启动过程有了一个较为理性的认识。

 

 

观看更多有关 的文章?

*

    2015年4月5日

    It’s like you’re on a missoin to save me time and money!

      burningcodes
      2015年4月5日

      Haha, it’s my pleasure.

+
跳转到评论