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

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


      这次实验说是跟踪分析Linux内核的启动过程,但难点实际上是在环境的搭建与调试器的使用。等这些都解决了,调试器会非常清晰地带我们走进整个程序运行的过程。

      因为有许多人之前并没有怎么用过linux,所以我尽量将每个命令都解释得详细点。


      下面我们开始实验。

      首先,我们进行环境的搭建。

      $ cd LinuxKernel

      $ wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz

      这个命令是下载Linux-3.18.6.tar.xz,也就是下载我们实验的内核代码。Linux的命令其实都非常简单好记,wget就是web get,从一个网站获取,就是下载,后面跟下载地址就好了。这里要说明一下,这步就是简单的下载,所以我的建议是,大家利用迅雷等下载工具下载就好了,然后拷进来是一样的。直接利用linux的命令下载,受网络状况影响较大(因为官方服务器基本上都是国外的,直接连接网络不稳定),下载速度不稳定且容易失去链接,像我就是重下了两遍。。。。。。



      $ xz -d linux-3.18.6.tar.xz

      这步是解压缩,xz是一种压缩格式,就像zip一样。xz a.tar就会将a.tar压缩成a.tar.xz,而加-d就代表解压缩,xz a.tar.xz就将解压为a.tar。



      $ tar -xvf linux-3.18.6.tar

      这步是解包,命令格式与上面类似。x从档案文件中释放文件,v 详细报告tar处理的文件信息,f 使用档案文件或设备。f就是file,通常是必选的,这样才能使tar找到文件。

      由此我们还能看出一件事,linux的压缩是先装包后压缩,缺一不可。而windows下常用的zip是合二为一的。


      $ cd linux-3.18.6  

      $ make i386_defconfig

      这步是配置编译环境。

      $ make

      这步进行编译。

      make实际上是GNU的一个工具,会解释运行Makefile里的规则(命令)。Makefile实际上就相当于一个批处理文件。make后面加什么参数对应执行什么都在Makefile里写好,所以拿到别人的一个工程,先看它的Makefile文件,就知道大概怎么编译配置了。



      这里注意一下,我们看到编译的过程里最多出现的两个命令就是LDCC


      其实LD是GNU的链接器,Linker Device。CC是c语言的编译器,c complier,cc是c的真正的编译器,常用的gcc实际上就是靠cc实现的。


      这个编译过程要好久,为节约时间,我们可以新开一个终端来完成其他工作。

      $ cd LinuxKernel

      $ mkdir rootfs

      创建一个文件夹叫rootfs,mkdir就是make directory。


      $ git clonehttps://github.com/mengning/menu.git

      下载menu,这步就别做了!按之前说的,用下载工具下载好拷过来就好了。

      $ cd menu

      $ gcc -o init linktable.c menu.c test.c-m32 -static –lpthread

      这步是编译链接,-o表示输出的可执行文件命名为init,后面三个c文件,-m32表示生成32位的代码,-static静态编译(不加载动态连接库),- lpthread表示在链接阶段时链接这个库。

      $ cd ../rootfs

      $ cp ../menu/init ./

      这步是复制init到rootfs目录,cp就是copy。

 

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

      这步将rootfs下init文件打包成rootfs.img文件。这个rootfs.img就是系统的镜像文件。



      等编译完成后

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

      这步是运行我们的vmlinux。



      为了方便接下来的跟踪调试,我们应在编译的时候加上调试信息。这次我们利用图形化的调试界面,所以我们需要先安装图形化工具ncurses,ncurses-dev实际上是一个库,利用它就可以显示图形化的控制页面了。安装命令是$ sudo apt-get install ncurses-dev,再次说明linux的命令都十分好记,sudo就是使用root权限(super user do超级用户做……),apt-get就是获取apt文件(apt是linux下的安装包文件,现在android的apk就是由它移植的),install安装,后面加文件名而已。

 

      $ make menuconfig

      使用图形化界面,选择kernelhacking—>


      然后选择Comile-time checks and comp[iler options —>


      按Y选中Compile the kernel with debug info,保存退出。


 

      $ make

      编译


      这样环境就搭载好了,下面我们就可以进行真正的跟踪了。

      $ qemu arch/x86/boot/bzImage -initrd../roorfs.img -s –S

      这次加了-s和-S,-s为gdb提供一个调试端口tcp:1234,-S就是Stoped(cpu启动时暂停)。


      可以看到系统暂停了。


      现在新开一个终端,运行gdb进行调试跟踪。

      $ gdb –q

      -q就是quiet,这样gdb就不会打印版本等相关信息。

      (gdb) file vmlinux

      将vmlinux文件装载进来。

      (gdb) target remote:1234

      通过之前预留的tcp:1234建立连接。

      (gdb) b start_kernel

      b就是break,设置断点。

      (gdb) c

      c就是continue继续运行。


      运行到断点处,通过qemu可以看到现在是booting the kernel阶段。


      (gdb) list

      list查看断点处代码,默认是显示10条,断点处在中间。



      此时可以继续list接着向下查看,也可以step单步运行,或者利用汇编码ni(next instruction)逐指令地运行调试。具体的跟踪调试过程因每个人的关注点不同,这里就不具体论述了。


一些常用的gdb的命令:

      (gdb) break n :在第n行处设置断点

      (gdb) r:运行程序

      (gdb) n:单步执行

      (gdb) c:继续运行

      (gdb) p 变量:打印变量的值

      (gdb) bt:查看函数堆栈

      (gdb) set args 参数:指定运行时的参数

      (gdb) show args:查看设置好的参数

      (gdb)delete 断点号n:删除第n个断点

      (gdb)step:单步调试,若有函数调用,则进入函数(与命令n不同,n是不进入调用的函数的)

 

 

总结

      在这里,我来简述一下我跟踪调试后对linux内核启动过程的观察。

      在具体说明启动过程之前,要先说明一些简单的知识。一台计算机,从硬件电源开关开始,到最后启动一个操作系统,本质上是一个cpu从硬盘读取数据的过程。因此要明白两个专有名词BIOS和Boot。

      BIOS:Basic Input Output System,基本输入输出系统。它实际上是一段固化到主板上一个ROM芯片上的程序,用以保存计算机最重要的基本输入输出的程序、开机后自检程序和系统自启动程序,为计算机提供最底层的、最直接的硬件设置和控制。

      Boot:系统引导文件,可以说这是进入一个系统真正的入口(虽然这样形容有点不准确)。

 

      下面具体说一下linux的启动过程(从硬件启动开始)。



      第一步:加载BIOS

      之前说明了BIOS是计算机最基本、最重要的信息,包含了cpu和其他设备等众多相关信息、启动顺序等,先加载了BIOS,计算机就知道了自己设备的信息,也就知道从哪里开始加载设备了。


      第二步:读取MBR

      MBR:Master Boot Record,主引导记录,大小512字节,位于硬盘上第0磁道第一个扇区。实际上里面存放的就是Boot Loader的代码。


      第三步:装载Boot(Boot Loader)

      操作系统内核运行前运行,用来初始化硬件设备、建立内存空间的映射图等。这个过程就好比之前实验里配置环境的过程,为系统内核的启动准备一个合适的状态。最常用的Boot Loader是Grub。


      第四步:加载内核

      系统读取内核镜像,相当于之前实验里rootfs.img。当时我们手动输入了命令,以确定位置,而系统真正启动时,这个位置信息存放在Grub这样的Boot Loader里的,这也是为什么要通过Boot Loader才能加载内核。

 

      第五步:init初始化

      内核加载好了,第一个运行的函数就是init目录下的main.c里的start_kernel函数,这是linux的入口。但希望大家了解的是,计算机显然运行的都是可执行程序而不是一个一个的c文件,所以其实init目录下的c编译后的程序是/sbin/init,因此,系统执行的第一个程序应该是/sbin/init,它里面的start_kernel函数。/sbin/init运行后,会读取文件/etc/inittab文件,这是进行初始化的根据。

      至此,基本的LinuxKernel内核已经运行起来了。这里也是我们使用gdb跟踪的开始。

 

      第六步:执行rc.sysinit

      Linux执行的第一个用户层的文件是/etc/rc.d/rc.sysinit脚本程序。


      再往后的启动内核模块等一直到登录Login程序等的具体过程就不陈述了,至此,基本的Linux的启动过程就非常清晰了。

 


附:LinuxKernel的目录结构(该图来源:网络教材)

 



-----------------------------END--------------------------------

刘建鑫 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

------------------------------------------------------------------

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值