从start_kernel到init进程启动 《Linux内核分析》笔记
GDB的使用
在进入GDB调试前,首先掌握GDB最常用的命令,以方便完成跟踪。
- b[reak] linenumber:加断点
- s[tep]:单步进入
- n[ext]:单步跳过
- c[ontinue]:继续执行
- r[un]:运行至结束或者崩溃
- q[uit]:退出
- info:查看已设置的断点和观察点
- watch:设置观察点
其他有用的命令包括:
命令 | 用途 |
---|---|
ptype | 打印变量的数据类型 |
info share | 打印当前装入的共享库的名称 |
info functions | 打印所有函数原型 |
list | 显示当前行周围的 10 行源代码 |
help | 显示主题列表 |
计算机的启动过程
- x86 CPU启动的第一个动作CS:EIP=FFFF:0000H(换算为物理地址为000FFFF0H,因为16位CPU有20根地址线),即BIOS程序的位置。http://wenku.baidu.com/view/4e5c49eb172ded630b1cb699.html
- BIOS例行程序检测完硬件并完成相应的初始化之后就会寻找可引导介质,找到后把引导程序加载到指定内存区域后,就把控制权交给了引导程序。这里一般是把硬盘的第一个扇区MBR和活动分区的引导程序加载到内存(即加载BootLoader),加载完整后把控制权交给BootLoader。
- 引导程序BootLoader开始负责操作系统初始化,然后起动操作系统。启动操作系统时一般会指定kernel、initrd和root所在的分区和目录,比如root (hd0,0),kernel (hd0,0)/bzImage root=/dev/ram init=/bin/ash,initrd (hd0,0)/myinitrd4M.img
- 内核启动过程包括start_kernel之前和之后,之前全部是做初始化的汇编指令,之后开始C代码的操作系统初始化,最后执行第一个用户态进程init。
- 一般分两阶段启动,先是利用initrd的内存文件系统,然后切换到硬盘文件系统继续启动。initrd文件的功能主要有两个:1、提供开机必需的但kernel文件(即vmlinuz)没有提供的驱动模块(modules) 2、负责加载硬盘上的根文件系统并执行其中的/sbin/init程序进而将开机过程持续下去
计算机一旦加电以后,PC就指向BIOS的一段区域,这段区域会完成硬件自检,当硬件检查完成以后,没有发现问题。便开始从硬盘的一个扇区(大小为512个字节,在Linux中可以认为是grub)读取字节,然后把控制权交给这段代码,这段代码大小比较小,能做的事情比较少,能让用户进行一些选择操作。当选择完成以后,这样这段代码就负责加载内核到内存里了,加载完成以后,控制权就交给操作系统了,这样一个操作系统就加载并开始运行起来了。
跟踪分析Linux内核的启动过程
使用实验楼的虚拟机打开shell
cd LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
内核启动完成后进入menu程序(《软件工程C编码实践篇》的课程项目),支持三个命令help、version和quit,您也可以添加更多的命令,对选修过《软件工程C编码实践篇》的童鞋应该是a piece of cake.
使用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之前,也可以在之后
Linux操作系统的基本初始化在init模块中完成,我们实验也从这个模块开始。
首先按照说明断点打在其中的main.c函数的start_kernel函数,然后执行c[ontinue],接着就停在了main.c:501行的start_kernel函数。