一 实验过程
1. 使用实验楼的虚拟机打开 shell
cd ~/LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
2. 使用 gdb 跟踪调试内核
$ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
# 关于-s和-S选项的说明:
# 1. -S
# -S freeze CPU at startup (use ’c’ to start execution)
# 2. -s
# -s shorthand for -gdb tcp::1234
# 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项
3. 另开一个shell窗口,打开 GDB 调试器
可以看到内核代码运行到start_kernel()函数时停止,之后再通过list命令查看断点附近代码:
再在rest_init处设置一个断点,按c继续执行:
break rest_init
c
二 分析
start_kernel
是 Linux 内核的启动过程中的入口函数,它位于 init/main.c
文件中。它负责初始化内核的各种数据结构,设置中断向量表、初始化调度器等,最终启动系统的用户空间进程。下面是对 start_kernel
函数执行过程的详细分析:
-
设置堆栈和堆栈指针:
在启动阶段,内核会设置一个堆栈,这个堆栈将用于执行内核代码。这是通过设置堆栈指针寄存器 ESP(x86 架构)或者栈指针寄存器 SP(ARM 架构)来实现的。 -
初始化初始化进程(
init
进程):start_kernel
函数会初始化一个特殊的进程结构体,即init
进程的描述符。这是 Linux 内核中的第一个用户态进程,通常具有 PID 为 1。init
进程会执行用户空间的/sbin/init
程序,这是系统初始化的第一个用户空间进程。 -
初始化调度器:
调度器的初始化包括初始化进程队列、定时器和调度策略等。
内核调度器会选择哪个进程运行,或者决定让 CPU 进入休眠状态等。 -
启动初始化进程(
init
进程):start_kernel
函数最终会调用rest_init
函数来启动init
进程。rest_init
函数会创建init
进程,并将控制权交给用户空间。 -
用户空间初始化:
一旦init
进程开始执行,它会执行用户空间的初始化程序,如/sbin/init
。这个用户态初始化程序负责初始化用户空间的系统服务、加载驱动程序和应用程序等。
三 总结
Linux的启动会从硬件初始化到用户空间,逐步建立系统的运行环境。
init进程:内核创建1号进程(init进程),这是Linux系统中的第一个用户态进程。
Idle进程:同时,内核还创建Idle进程,用于在CPU空闲时执行。