==================================================================
这周的实验是通过gdb工具,跟踪到linux内核代码中,分析启动过程。
进入实验楼环境后,打开终端如入命令运行后结果如下图:
cd LinuxKernel/ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
输入help命令查看支持的3条命令如下图:
接下来输入如下命令运行后结果如下图:
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选项
接下来另开一个窗口gdb,输入如下代码运行后如下图:
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之前,也可以在之后
接下来敲入continue命令让系统继续运行,这是linux就开始初始化启动啦,并且会断在start_kernel函数处,此时我们用list命令就可以看到start_kernel函数前后的代码,如下图:
还可以继续设置一个断点break rest_init,然后continue运行,如下图:
现在我们开始简单分析下start_kernel:
首先找到内核启动起点start_kernel(),它在内核代码文件夹init目录下的main.c文件中。可以在start_kernel()函数体中找到一个全局变量&init_task,它相当于在分析mykernel案例时候的0号进程创建的PCB,0号进程即最终的idle进程。所有模块的初始化都是通过start_kernel()函数调用进程初始化进行工作的。
在start_kernel()函数中还进行了很多函数的初始化工作,如:
trap_init()用来中断的,初始化中断向量
mm_init()内存管理模块初始化
sched_init()调度模块初始化
rest_init()中的run_init_process,init_process是linux系统中的1号进程,是第一个用户态进程,默认是根目录下的程序,如果根目录下没有,还会去找sbin/init,bin/init,sd/init其他路径下的程序来作为1号进程。
kthreadd内核线程来管理系统资源
然后系统进入cpu_startup_entry(cpuhp_online) // call into cpu_idle with preempt disabled
当系统没有进程需要执行时就调度到idle进程
总结:rest_init()就是start_kernel从进程内核一启动的时候就一直会存在,就是0号进程
0号进程创建了1号进程以及其他的一些服务的内核线程,这样整个系统就启动起来了