郑斌 《Linux内核分析》MOOC课程
该文章为课程的第三个实验记录及分析。
实验指导:
1.使用实验楼的虚拟机打开shell
cd LinuxKernel/qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
内核启动完成后进入程序,支持三个命令help、version和quit,您也可以添加更多的命令.
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之前,也可以在之后
实验环境:在中进行。
实验步骤:
打开shell,进入到LinuxKernel目录,通过qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img命令
运行对应的内核程序。
这里的 -initrd rootfs.img的作用是指明根文件系统。
可以看到menu程序已经启动。在这里支持help、version、quit共3个命令。分别为查看帮助,查看版本,和退出功能。
下面通过gdb调试来跟踪分心Linux内核的启动过程。
通过命令qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S 。
参数-S的作用是在开始的时候,CPU初始化之前冻结CPU
参数-s 的作用是创建gdbserver,对应 tcp的1234号端口。若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项。
file linux-3.18.6/vmlinux 这是一个带debug信息的内核。该命令加载符号集
target remote:1234 建立gdb和gdbserver之间的连接。
然后通过break设置断点,按c然程序继续执行到断点。按list可以查看断点附近的代码。
我们查看的内核代码。这里有很多个文件目录。
其中arch包括了所支持的多种CPU体系结构,这里我们只关心里面的x86文件夹就好了。
其他还有如drivers为驱动相关代码。fs为文件系统相关内核代码,ipc是进程相关代码,mm是存储管理代码,net是和网络相关的内核代码等等。
init为初始化相关的模块,在init/main.c/start_kernel中之后开始C代码的操作系统初始化,最后执行第一个用户态进程init。。
在start_kernel函数中可以看到很多_init相关函数,涉及到操作系统启动相关的多种初始化操作。
这里的init_task为相当于0号进程的pcb。 还有();为进程调度相关初始化。();为中断相关的内核代码。等等。
这里看到start_kernel 最后一句 rest_init().
当操作系统为空闲时,就会执行这个idle程序。
总结:
浅入的学习了xLinux内核的启动过程。
学习了gdb的操作使用。
x86 CPU启动的第一个动作CS:EIP=FFFF:0000H(换算为物理地址为000FFFF0H,因为16位CPU有20根地址线),即BIOS程序的位置。BIOS例行程序检测完硬件并完成相应的初始化之后就会寻找可引导介质,找到后把引导程序加载到指定内存区域后,就把控制权交给了引导程序。引导程序BootLoader开始负责操作系统初始化,然后起动操作系统。启动操作系统时一般会指定kernel、initrd和root所在的分区和目录。内核启动过程包括start_kernel之前和之后,之前全部是做初始化的汇编指令,之后开始C代码的操作系统初始化,最后执行第一个用户态进程init。一般分两阶段启动,先是利用initrd的内存文件系统,然后切换到硬盘文件系统继续启动。
参考资料:课程学习文档视频以及互联网资料。