林万喜 原创作品 转载请注明出处 USTC 2015.3
《Linux内核分析》MOOC课程:在线课程链接http://mooc.study.163.com/course/USTC-1000029000


























-
1.使用实验楼的虚拟机打开shell
- cd LinuxKernel/
- qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
-
2.使用gdb跟踪调试内核
- qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S # 关于-s和-S选项的说明:
- # -S freeze CPU at startup (use ’c’ to start execution)
- # -s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项
另开一个shell窗口
- gdb
- (gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表
- (gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
- (gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后
在start_kernel中进行一系列极为重要的初始化。最后执行位于init/main.c中的rest_init()来创建系统的第一个进程init。至此,内核启动完成。之后init进程会进行一些初始化和根文件系统挂载。
在start_kernel()的最后,系统会调用rest_init(),通过执行其中的kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND)来创建系统的第一个进程,即init进程。Init进程首先进行一系列的硬件初始化,然后挂载根文件系统。最后 init 进程会执行用户传递过来的“init=”启动参数执行用户指定的命令,或者执行以下几个进程之一:
execve(“/sbin/init”,argv_init,envp_init);
execve(“/etc/init”,argv_init,envp_init);
execve(“/bin/init”,argv_init,envp_init);
execve(“/bin/sh”,argv_init,envp_init)。
如果这些都无法执行(无法找到),系统的启动会宣告失败。Init启动后,会读取/etc/inittab这一配置文件,根据inittab文件内容进行一些设置或对一些指令做出解释。
加载linux内核开始运行后,进程开始执行start_kernel()完成Linux内核的初始化工作。包括初始化页表,初始化中断向量表,初始化系统时间等。继而调用 fork(),创建第一个用户进程: kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); 这个进程就是着名的pid为1的init进程,它会继续完成剩下的初始化工作,然后execve(/sbin/init), 成为系统中的其他所有进程的祖先。这就是我们看到的init进程,进程号为1。初始化的最后linux调用scheule()整个系统就运行起来了。可以用进程查看命令来验证 # ps aux 。
本文介绍如何使用实验楼的虚拟机及gdb调试Linux内核,并详细分析了start_kernel函数的工作流程。从启动虚拟机到跟踪调试内核,再到start_kernel函数内部的初始化过程,包括创建init进程和idle进程等关键步骤。

被折叠的 条评论
为什么被折叠?



