一、构建Linux系统MenuOs
启动内核
启动调试内核,结果如下图所示,内核运行被冻结
二、GDB跟踪调试内核
再打开一个窗口,水平分割,启动GDB,把内核加载进来,建立连接
在start-kernel处设置断点,按"c"使程序继续执行到“start_kernel”位置
"list"命令查看“start_kernel”上下句代码
在rest_init处设置断点,按"c"使程序继续执行到“rest_init”位置, "list"命令查看“rest_init”上下句代码
"rest_init"在"start_kernel"尾部被调用
三、简单分析“start_kernel”
“start_kernel”位于init目录下的main.c
“init_task”即手工创建的PCB,0号进程即最终的idle进程
内核非常庞大,涉及到很多模块,但是不管分析内核哪一部分都会涉及到“start_kernel”,因为所有模块在进行初始化的时候,都需要调用“start_kernel”来调用这一模块
trap_init函数主要是对一些系统保留的中断向量进行初始化
trap_init的代码在arch/x86/kernel目录下的trap_init.c中,可以看到trap_init中设置了很多中断门和硬件中断
系统陷阱门
mm_init:内存管理模块初始化,sched_init:调度模块初始化
init_process是系统中的1号进程,即第一个用户态进程,是默认的根目录下的程序,
通过rest_init()新建kernel_init、kthreadd内核线程
在rest_init函数中有一个“call into cpu_idle”的注释
rest_init实际上在start_kernel内核启动时会一直存在,当系统没有进程需要执行时就调度到idle进程,是0号进程
通过0号进程创建1号进程和一些其他的服务的内核线程,这样整个系统就启动起来了。
四、总结
init_task()(PID为0)在创建了init进程后,调用cpu_idle()演变成了idle进程,执行最后一次调度后,init进程运行。1号内核线程负责执行内核的部分初始化工作及进行系统配置,最后调用do_execve加载init程序,演变成init进程(用户态1号进程),init进程是内核启动的第一个用户态进程。kthreadd(PID为2)进程由0号进程创建,始终运行在内核空间,负责所有内核线程的调度和管理。