一、构造一个简单的Linux内核
通过两个命令把Linux系统和一个简单的文件系统运行起来:
cd ~/LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
内核启动完成后进入 menu程序,支持三个命令 help、version 和 quit
二、跟踪调试Linux内核的启动过程
-s:在1234端口创建一个gdb-server
-S:CPU初始化之前冻结起来
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
通过上述命令启动内核,并冻结它,之后重新打开一个shell进行调试
cd ~/LinuxKernel/
gdb
(gdb)file linux-3.18.6/vmlinux
(gdb)target remote:1234
在start_kernel处设置断点,因为内核的主要模块的初始化工作都是在start——kernel函数里调用的。main.c中没有main函数,start_kernel()就相当于C语言中的main函数,start_kernel是一切的起点,所以首先在start_kernel()设置断点进行分析 单步执行分析start_kernel函数,,init_task变量,是task_struct类型,相当于之前分析mykernel时有第一个进程的PCB,就是在这里进行初始化的,在此之后才是对各种模块的初始化。 trap_init函数的调用涉及中断向量的初始化,mm_init函数则是内存管理模块的初始化 在执行完rest_init()后,内核成功启动: 最后的rest_init函数是在start_kernel的尾部进行调用的,能够实现后续的初始化工作,正式执行内核线程和其他服务进程。 故在此处设置断点,进一步进行调试分析 单步执行。到调用kernel_thread()时创建1号内核线程: // 这里可以对比一下init_task和kernel_thread(),kernel_thread()是fork出的一个新进程来执行kernel_init函数,而init_task是使用宏进行初始化的。也就是说init_task不是通过fork方式产生的进程
继续运行,内核成功启动:
三、总结
init_task()(PID为0)在创建了init进程后,调用cpu_idle()演变成了idle进程,执行一次调度后,init进程运行。1号内核线程负责执行内核的部分初始化工作及进行系统配置,最后调用do_execve加载init程序,演变成init进程(用户态1号进程),init进程是内核启动的第一个用户态进程。kthreadd(PID为2)进程由0号进程创建,始终运行在内核空间,负责所有内核线程的调度和管理。
|