首页 > 代码库 > 实验三:跟踪分析Linux内核的启动过程

实验三:跟踪分析Linux内核的启动过程

Ubuntu 16.04下搭建MenuOS的过程:

1、下载内核源代码编译内核

 1 # 下载内核源代码编译内核
 2 cd ~/LinuxKernel/
 3 wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz
 4 xz -d linux-3.18.6.tar.xz
 5 tar -xvf linux-3.18.6.tar
 6 cd linux-3.18.6
 7 make i386_defconfig
 8 make # 一般要编译很长时间,少则20分钟多则数小时
 9  
10 # 制作根文件系统
11 cd ~/LinuxKernel/
12 mkdir rootfs
13 git clone https://github.com/mengning/menu.git  # 如果被墙,可以使用附件menu.zip 
14 cd menu
15 gcc -o init linktable.c menu.c test.c -m32 -static -lpthread
16 cd ../rootfs
17 cp ../menu/init ./
18 find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
19  
20 # 启动MenuOS系统
21 cd ~/LinuxKernel/
22 qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img

 

2、重新配置编译Linux使之携带调试信息

 1 $ sudo apt-get install ncurses-dev
 2 
 3 首先需要安装图形化调试工具ncurses,ncurses-dev是一个库,利用它就可以显示图形化的控制页面了。
 4 
 5 $ make menuconfig
 6 
 7 选择kernelhacking—>
 8 
 9 选择Comile-time checks and comp[iler options —>
10 
11 按Y选中Compile the kernel with debug info
12 
13 保存退出
14 
15 重新make

 

3、使用gdb跟踪调试内核

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选项
4 
5 另开一个shell窗口
6 gdb
7 (gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表
8 (gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
9 (gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后

此时可以继续list接着向下查看,也可以step单步运行,或者利用汇编码ni(next instruction)逐指令地运行调试。

一些常用的gdb的命令:

      (gdb) break n :在第n行处设置断点

      (gdb) r:运行程序

      (gdb) n:单步执行

      (gdb) c:继续运行

      (gdb) p 变量:打印变量的值

      (gdb) bt:查看函数堆栈

      (gdb) set args 参数:指定运行时的参数

      (gdb) show args:查看设置好的参数

      (gdb)delete 断点号n:删除第n个断点

      (gdb)step:单步调试,若有函数调用,则进入函数(与命令n不同,n是不进入调用的函数的)

 

 

基础的工作就是这些,下面我尝试去分析一下内核从start_kernel到init进程启动的大体过程

使用list命令看到的start_kernel源码:

技术分享

lockdep_init() : Linux有一个死锁检测模块lockdep,看注释说的是初始化hash表,应该是内核会依赖这个表来做其他的事。

set_task_stack_end_magic(&init_task) : init_task是一个全局变量,即是手工创建的PCB。 它是在Linux/init/init_task.c中被初始化的。具体的代码是

                       struct task_struct init_task = INIT_TASK(init_task);  那么task_struct应该就是一个类似于PCB结构的结构体,而INIT_TASK便是初始化该结构体的函数。

后面是一些模块的初始化,水平有限就不一一解释了,就说说老师提到的函数吧。

技术分享

trap_init() : 初始化中断向量,设置一些中断门。

mm_init() : 内存管理模块的初始化。

技术分享

sched_init() : 调度模块的初始化。

技术分享

rest_init() : 源码如下:

static noinline void __init_refok rest_init(void)
394 {
395         int pid;
396 
397         rcu_scheduler_starting();
398         /*
399          * We need to spawn init first so that it obtains pid 1, however
400          * the init task will end up wanting to create kthreads, which, if
401          * we schedule it before we create kthreadd, will OOPS.
402          */
403         kernel_thread(kernel_init, NULL, CLONE_FS);
404         numa_default_policy();
405         pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
406         rcu_read_lock();
407         kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
408         rcu_read_unlock();
409         complete(&kthreadd_done);
410 
411         /*
412          * The boot idle thread must execute schedule()
413          * at least once to get things moving:
414          */
415         init_idle_bootup_task(current);
416         schedule_preempt_disabled();
417         /* Call into cpu_idle with preempt disabled */
418         cpu_startup_entry(CPUHP_ONLINE);
419 }

从rest_init开始,Linux开始产生进程,因为init_task是静态制造出来的,pid=0,它试图将从最早的汇编代码一直到start_kernel的执行都纳入到init_task进程上下文中。
第403行的kernel_init函数中有一个run_init_process函数,它的作用便是建立init_process进程,也就是Linux系统中的1号进程。它是第一个用户态进程。
而从最后的cpu_startup_entry中可以看到init_task以后便成为了一个idle进程,也就是0号进程,当系统没有进程运行时,一直在运行这个空闲进程。



总结:
通过本次试验,我对Liunx内核启动的过程有了大体的了解。
内核启动时,会先进行一部分硬件的初始化,这部分是汇编语言完成的,而后会运行start_kernel,也就是内核的C语言部分。start_kernel会继续进行一些内核的初始化,建立0号进程
,最后通过rest_init()建立1号进程,也就是第一个用户态进程,而以后0号进程便成为一个空闲(idle)进程,当没有其他进程运行时,系统便运行0号进程。
总的来说,虽然明白了大体的过程,但还是感觉对于内核的了解只是浮于浅浅的表面,好像懂了点什么,又好像什么也不懂。希望继续深入的学习能够解答我的疑惑。同时也以此自勉吧。

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

 

实验三:跟踪分析Linux内核的启动过程