跟踪分析Linux内核的启动过程
作者:何振豪
原创作品转载请注明出处 http://www.cnblogs.com/scoyer/p/6516032.html
《Linux内核分析》MOOC课程 http://mooc.study.163.com/course/USTC-1000029000
这节课讲了如何查看linux内核源代码,构造一个简单的linux系统(算不上构造,实际上就是编译内核,然后打包git上的根文件系统,然后启动这个简单系统而已),最后还讲了如何利用gdb来跟踪调试linux内核的执行情况(重点)。
这次实验主要是编译linux内核。既然如此,那么就应该自己动手实践一下,不要直接套用实验楼的环境打开镜像就完事,自己动手一步一步编译内核较为关键,以下是从无到有的详细步骤:
首先当然是下载内核源码+解压+make
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分钟多则数小时
将git上的一个根文件系统下载下来打包成img文件
# 制作根文件系统
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
cd ../rootfs
cp ../menu/init ./
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
启动不带gdb调试信息的简单os
# 启动MenuOS系统
cd ~/LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
上述展示了怎么编译内核并且启动内核后加载我们制定的根文件系统,接下来为了可以跟踪调试这个os,必须重新make使之携带调试信息。
make menuconfig
kernel hacking
—> Compile-time checks and compiler options
[*]compile the kernel with debug info
保存之后make一次就可以使之携带调试信息了,然后就是利用gdb进行跟踪调试了。
现在一个shell里面启动menuos:
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选项
然后开启另外一个窗口,注意要进入LinuxKernel文件夹
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之前,也可以在之后
这样我们就可以逐步运行,观察这个简单的os的执行情况。
实质上,上面的命令在执行过程会遇到各种问题,我用的系统是ubuntu12.04,下面列出我执行过程中出现的部分问题:
(1)“gcc -o init linktable.c menu.c test.c -m32 -static –lpthread”执行这句话的时候gcc报错出现:找不到lpthread?
这个不知道是不是因人而异,我的解决方案是先去掉lpthread编译一次,报错之后在执行上述完整代码就成功了。(ps:这个几乎弄崩溃了。。。)
(2)make menuconfig时会出现: curses.h: 没有那个文件或目录?
这是因为ubuntu系统中缺少一个套件 ncurses devel ,把此套件安装下即可:
sudo apt-get install libncurses5-dev
(3)qemu找不到这个执行命令?
使用qemu-system-i386或qemu-system-x86_64指令替换qemu指令。如果你想还是使用qemu指令,可以建立一条软链接:
sudo ln -s /usr/bin/qemu-system-i386 /usr/bin/qemu
下面主要是调试start_kernel这个函数,里面执行了很多init函数,对操作系统的各个模块进行初始化,选出了其中几个老师上课点出的:
set_task_stack_end_magic(&init_task);
trap_init();
mm_init();
rest_init();
这个函数执行的时候qemu已经加载完毕,但是一直显示continuing表示还没有完全执行完。里面发生什么事呢?看0号进程的源代码:
void cpu_idle(void) { ...... /* endless idle loop with no priority at all */ while (1) { ...... while (!need_resched()) { if (cpu_is_offline(smp_processor_id())) { tick_set_cpu_plugoff_flag(1); cpu_die(); /* plugoff CPU */ } ...... if (cpuidle_idle_call()) pm_idle(); /* 进入低电 */ } ...... schedule_preempt_disabled(); /* 调用schedule() */ } }
这里idle进程进行的是死循环,表示空闲,等待进程的执行,gdb正在执行idle进程,没有新的进程产生,所以没有跳出循环,所以一直在执行。。。。