linux课程第三周实验及总结
一、实验:跟踪分析Linux内核的启动过程
- 使用gdb跟踪调试内核从start_kernel到init进程启动
- 使用实验楼的虚拟机打开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内核的启动过程
启动Linux内核的三个参数:
-
- kernel
- initrd
- root所在分区、目录
qemu -kernel (文件名) -initrd (rootfs.img)
- qemu相当于打开一个虚拟机
- kernel启动一个内核,位置由其后的文件名指定。如果在当前目录下,可以直接输入文件名,如果不是,则需要输入该内核的全路径。
- initrd指令是挂了一个ramdisk虚拟硬盘,是内核的重要补充,rootfs.img就是这个虚拟硬盘,内有分区,然后启动的其实是其中的init文件,这个文件是由之前的menuOS编译而成,gcc -o命名为init。
0号进程
有一个全局变量init_task,即手工创建的PCB,0号进程,即最终的idle进程。0号进程一直存在,系统没有进程需要执行时调度到0号进程。
init_process 是默认的一号进程
-
实验截图
-
-
运行截图
-
第一个断点,start_kernel
-
实验目录
-
arch目录
占有相当庞大的空间
arch/x86目录下的代码是需要重点关注的。
arch下其他目录可以删掉。 -
init目录
内核启动相关的基本代码基本都在init目录下。
main.c 文件中有一个start_kernel函数,初始化Linux内核的起点,这个函数相当于普通c程序的main函数。 -
kernel目录
Linux内核的核心代码在kernel目录中 -
其他
Documentation 文档
drivers 驱动
fs-filesystem 文件系统
include
ipc 进程间通信 -
README:
INSTALLING 怎样安装内核源代码——怎么解压怎么打补丁
make mrproper 把生成的中间代码清理干净
menuconfig
-
- GDB使用
-
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之前,也可以在之后
(gdb)c # 系统开始启动,启动到start_kernel
(gdb)list # 可以看到start_kernel上下的代码
(gdb)break rest_init
(gdb)c # 当前系统执行到rest_init (gdb)list # 可以看到rest_init是在start_kernel的尾部调用的。
5.实验函数
trap_init() 初始化一些中断向量
mm_init() 内存管理模块
sched_init() 调度模块
rest_init()中有kernel_thread(kernel_init,NULL,CLONE_FS),kernel_init中有run_init_process,创建了一号进程,默认路径下的程序。
二、课程总结
1、Linux内核启动过程
- 计算机的启动过程概述
- x86 CPU启动的第一个动作CS:EIP=FFFF:0000H(换算为物理地址为000FFFF0H,因为16位CPU有20根地址线),即BIOS程序的位置。
- 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程序进而将开机过程持续下去
2、怎么编译内核?
- 最简单的是make config,但是这个需要的时间很长
- make menuconfig,是图形化的界面,比上面更为方便
- make allnoconfig,所有能选no的都选no,简单粗暴。
-
Makefile和config
这两个和在一起能够决定内核中哪些需要被编译,哪些不会被编译。