linux内核启动过程追踪


一、使用自己的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

  cd ../rootfs

  cp ../menu/init ./

  find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img

  # 启动MenuOS系统

  cd ~/LinuxKernel/

  qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img

    // -initrd file Use file as initial ram disk.

二、重新配置编译Linux使之携带调试信息

在原来配置的基础上,make menuconfig选中如下选项重新配置Linux,使之携带调试信息

  kernel hacking—>[*] compile the kernel with debug info

  make重新编译(时间较长)



三、使用gdb跟踪调试内核

  1、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选项

 2、另开一个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之前,也可以在之后

四、过程图示:

  1、可以看到QEMU 已经运行而且被“冻结”。(有几个文件没有,不影响后续操作)

   \

  2、另外打开一个窗口,进入linuxkernel目录,输入gdb 回车

     \

  3、输入:(gdb)file linux-3.18.6/vmlinux

     (gdb)target remote:1234

       (gdb)break start_kernel

   \

  4、输入c 回车,可以看到内核继续启动,最后停在了start_kernel处:

\

更多gdb指令:

显示和查找程序源代码

(1)list :显示10行代码,但是我为什么没有显示成功呢?

(2)list 5,10:显示源文件第五行到第十行的代码

(3)list t4.c:5,10:显示源文件中第五行到第十行的代码,在跳是含有多个源文件的次序时使用;

(4)list get_sum:显示get_sum函数周围的代码//什么叫周围的代码呢?

(5)list t4.c :get_sum:显示源文件t4.c中第五行到第十行的代码,在跳是含有多个源文件的次序时使用;
(6)如果在调试中运行linux命令,则可以在gdb的提示符下输入shell命令. (gdb)shell ls
(7)search forward用来从当前行向前查找第一个匹配的字符串;
search get_sum forward get_sum
(8)reverse_search 用来从当前行想前查找第一个匹配的字符串: Example: reverse_search main

设置和管理断点:
(1)以行号设置断点:(gdb)break 7
(2)以函数名设置断点:(gdb)break get_sum
(3)以条件表达式设置断点:方法一:break 行号或者函数名 if 条件. Example: (gdb)break 7 if i==99
方法二:watch 条件表达式,下面是具体的举例:
方法三:awatch;用来给表达式设置断点,在表达式的值发生改变或者表达式的值杯读取的时候,程序暂时停止;
(4).查看当前设置的断点:info breakpoints

(5)使用“disable 断点编号”命令可以是某个断点失效,程序运行到该段点时不会停下来而是继续运行。

(6)使用“enable 断点编号”命令可以是某个断点恢复有效。

彻底的删除某个断点,可以使用clear或者delete命令。
(1)clear:删除程序中所有的断点;
(2)clear 行号:删除此行中的断点
(3)clear 函数名:删除该函数的断点
(4)delete 断点编号:删除指定编号的断点。如果一次要删除多个断点,各个断点编号以空格隔开。

控制程序的执行:
(1)continue命令:让程序继续运行,直到下一个断点或者运行完为止。格式:continue
(2)kill命令:用于结束当前程序的调试

(3)next和step命令
   区别:如果遇到函数,next会把函数调用当作一条语句来执行,再次输入next会执行函数调用后的语句;

   而step则会跟踪进入函数,一次一条的执行函数内的代码,直到函数内的代码执行完,在进行函数调用后的语句;
(4)nexti和stepi命令:用来单步执行一条机器指令,注意不是单步执行一条鱼据。单步执行一条语句使用next和step命令。通常一条语句有多条机器指令构成的。
   注意的是:gdb的一些命令可以简写,比如list可以用li来代替,continue命令可以用cont来代替。

  

  5、继续输入 list start_kernel 查看start_kernel函数为中心上下10行的代码

    然后接着输入list 查看后续的代码(每输入一次list,向后显示10行)

   \

我们继续输入list跟踪start_kernel以后的代码,看看后面的执行过程:

\

\

\

\

可以看到,在start_kernel之后是一大堆的初始化操作,初始化外设、时钟、寄存器等。后面还有很多操作,这里不作过多的截图展示,

  6、后面进入rest_init函数:

\

四、总结:

  在start_kernel中调用了一系列的初始化函数,已完成内核本身的设置:设置与体系结构相关的环境、进程调度器初始化、控制台初始化、系统IRQ初始化、内存初始化等。在Start_kernel函数的最后调用了rest_init()函数,在rest_init中建立了init线程,并在最后调用cpu_idle()函数。

  可以这样理解:start_kernel最后clone出一个新的进程,也就是init进程,然后原来的进程就去执行cpu_idle()函数了,也就变成了idle进程,当发生一次进程调度后,init进程被调度运行。

  核心进程init()主要进行一些外设初始化的工作包括SMP(Symmetric Multi-Processing 对称多处理)的初始化,以及调用do_basic_setup()完成外设及其驱动程序的加载和初始化,当do_basic_setup()函数返回init() ,init() 又打开了、dev/console设备,重定向输出文件到控制台,最后通过kernel——execve加载执行init程序。

  追踪到init后可以修改文件系统,比如把显示的MENUOS修改为MYOS,修改quit指令,让其退出文件系统等:

\

\

\
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img


    // -initrd file Use file as initial ram disk.

  这里将-initrd 后面的参数rootfs.img加载到内存中运行,所以,这里的rootfs.img 可以随便改名字,因为内核代码中不会直接用这个名字而是通过initrd来传递的。再者rootfs.img是由tootfs文件夹打包得来的,而rootfs文件夹中只有一个init可执行文件,而init可执行文件来自于menu文件夹,menu文件夹中执行make指令会发现生成了一个test可执行文件,运行menu文件夹中的test和init后会发现效果是一模一样的,所以显然init是由test通过(cp test init) 拷贝、重命名得来的,所以最终修改文件系统可以归结为修改menu文件夹中的main.c、test.c文件。

  在init/initramfs.c文件中:

\


相关内容