3跟踪分析Linux内核的启动过程

安大大 + 原创作品转载请注明出处 + 《Linux操作系统分析》MOOC课程


实验

  • 使用自己的Linux系统环境搭建MenuOS的过程
# 下载内核源代码编译内核
cd ~/LinuxKernel/
wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz
xz -d linux-3.18.6.tar.xz
tar -xvf linux-3.18.6.tar
cd linux-3.18.6
make i386_defconfig
make # 一般要编译很长时间,少则20分钟多则数小时
 
# 制作根文件系统
cd ~/LinuxKernel/
mkdir rootfs
git clone https://github.com/mengning/menu.git  # 如果被墙,可以使用附件menu.zip 
cd menu
gcc -o init linktable.c menu.c test.c -m32 -static –lpthread #init是系统启动后默认启动1号进程,init是第一个用户态进程,第一个用户态进程是1号进程
cd ../rootfs
cp ../menu/init ./ #把init拷贝到rootfs目录下边
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img #使用cpio方式把当前rootfs目录下所有的文件打包成rootfs.img一个镜像文件
#至此一个最简单的根文件系统镜像制作完毕
 
# 启动MenuOS系统
cd ~/LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
  • 重新配置编译Linux使之携带调试信息
1.在原来配置的基础上,make menuconfig选中如下选项重新配置Linux,使之携带调试信息
2.1 kernel hacking—>
2.2 [*] compile the kernel with debug info #把debug信息打开
          #使得跟踪调试时可以边跟踪边看跟踪到某一点的某一行代码时上下那一段的源代码
3.make重新编译(时间较长)
  • 内核启动完成后进入menu程序:
3跟踪分析Linux内核的启动过程_第1张图片
menu编译好的init文件放在rootfs里

把menu编译好的init文件放在rootfs里,然后把rootfs做成rootfs.img。根文件系统是rootfs.img,内核启动完后加载根文件系统,根文件系统里的init可执行文件就被执行起来了

使用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)  CPU初始化之前冻结起来
 -s shorthand for -gdb tcp::1234 在这个端口上创建了一个gdb server,若不想使用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之前,也可以在之后
3跟踪分析Linux内核的启动过程_第2张图片
被冻结的linux系统
3跟踪分析Linux内核的启动过程_第3张图片
把带有debug信息(符号表)的内核加载进来
3跟踪分析Linux内核的启动过程_第4张图片
建立gdb和gdbserver之间的连接
3跟踪分析Linux内核的启动过程_第5张图片
设置start_kernel断点
3跟踪分析Linux内核的启动过程_第6张图片
按c回车,系统开始执行,启动到start_kernel的位置
3跟踪分析Linux内核的启动过程_第7张图片
使用list命令就能查看到start_kernel上下的这段代码
3跟踪分析Linux内核的启动过程_第8张图片
设置rest_init断点
3跟踪分析Linux内核的启动过程_第9张图片
查看rest_init处的代码,它是在start_kernel函数的尾部调用的
3跟踪分析Linux内核的启动过程_第10张图片

全局变量init_task即手工创建的PCB在这里初始化,0号进程即最终的idle进程。
不管分析内核的哪一部分都会涉及到start_kernel,因为要通过start_kernel进行初始化。

3跟踪分析Linux内核的启动过程_第11张图片

trap_init();初始化一些中断向量
mm_init();内存管理模块初始化
sched_init();调度模块初始化
等等
start_kernel的最后一句rest_init();

当系统没有进程需要执行时就调度到idle进程

Linux内核启动过程相关的参考资料
计算机的启动过程概述

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程序进而将开机过程持续下去

init进程详解

Linux内核中的init_task进程和idle进程

道生一(start_kernel....cpu_idle),一生二(kernel_init和kthreadd),二生三(即前面0、1和2三个进程),三生万物(1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先),新内核的核心代码已经优化的相当干净,都符合中国传统文化精神了。

你可能感兴趣的:(3跟踪分析Linux内核的启动过程)