Linux内核的启动过程
一、构造一个简单的Linux内核
首先,在实验楼环境中将Linux系统和一个简单的文件系统运行起来:
cd ~/LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
二、跟踪调试内核的启动过程
# -s:在1234端口上创建一个gdb-server
# -S:CPU初始化之前冻结起来
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
在另一个shell窗口中进行调试:
#进入LinuxKernel/下
cd ~/LinuxKernel/
# 打开GDB调试器
gdb
# 在gdb界面中targe remote之前加载符号表
(gdb)file linux-3.18.6/vmlinux
# 建立gdb和gdbserver之间的连接
(gdb)target remote:1234
start_kernel函数相当于c语言的main函数,是一切的起点。因此,首先在start_kernel函数处设置断点进行分析。
# 在start_kernel处设置断点
(gdb)break start_kernel
# 输入c运行至断点处
(gdb)c
单步执行分析start_kernel函数。其中,init_task变量相当于第一个进程的PCB,在此处进行初始化:
trap_init()实现中断向量的初始化,mm_init()实现内存管理的初始化,sched_init()实现调度模块的初始化,等等。
运行至最后是rest_init(),能够实现后续的初始化工作,正式执行内核线程和其他服务进程。
执行完rest_init()后,内核成功启动:
对rest_init函数设置断点,进一步调试分析:
单步执行。其中,rest_init函数通过调用kernel_thread()创建1号内核线程:
运行至最后,内核成功启动:
三、总结与分析
Linux内核启动过程:首先在入口start_kernel()处对0号进程init_task进行初始化,0号进程创建了1号内核线程kernel_init()和2号内核线程kthreadd()后,调用cpu_idle()转变为idle进程。1号内核线程kernel_init()负责执行内核的部分初始化工作及系统配置,之后调用do_execve演变成用户态1号进程;2号内核线程kthreadd()始终运行在内核空间,负责所有内核线程的调度和管理。