linux init=/bin/ash,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程序:

9e7fd3648ae1

9e7fd3648ae1

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之前,也可以在之后

9e7fd3648ae1

被冻结的linux系统

9e7fd3648ae1

把带有debug信息(符号表)的内核加载进来

9e7fd3648ae1

建立gdb和gdbserver之间的连接

9e7fd3648ae1

设置start_kernel断点

9e7fd3648ae1

按c回车,系统开始执行,启动到start_kernel的位置

9e7fd3648ae1

使用list命令就能查看到start_kernel上下的这段代码

9e7fd3648ae1

设置rest_init断点

9e7fd3648ae1

查看rest_init处的代码,它是在start_kernel函数的尾部调用的

9e7fd3648ae1

全局变量init_task即手工创建的PCB在这里初始化,0号进程即最终的idle进程。

不管分析内核的哪一部分都会涉及到start_kernel,因为要通过start_kernel进行初始化。

9e7fd3648ae1

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值