移动操作系统内核分析——Linux系统启动过程

实验一 Linux系统启动过程

  1. 编译带有调试信息的Linux内核。

1.    编译带有调试信息的Linux内核。
打开Shell终端,在Linux内核源码根目录里执行下列操作:
1)    清除以前的编译结果及配置信息:执行make mrproper
2)    以预定义的通用配置defconfig配置内核:执行make defconfig
3)    修改通用配置以满足内核调试需求:执行make menuconfig 或 make gconfig
▪    选中Kernel hacking / Compile-time checks and compiler options / Compile the kernel with debug info配置项及其子配置项:
•    Provide GDB scripts for kernel debugging
▪    去掉Processor type and features / Build a relocatable kernel / Randomize the address of the kernel image (KASLR)配置项。
4)    编译内核:执行make
编译结果说明:
▪    可启动内核镜像文件:arch/x86_64/boot/bzImage
▪    内核可执行文件(带调试信息):vmlinux

 

 

实验一:编译带有调试信息的Linux内核

 (1)、下载linux,我放在 /home/hws/linux 目录下

 

                                                  

(2)、首先清除以前编译结果及配置信息,在终端执行make mrproper,然后以通用配置defconfig配置内核,执行make defconfig 命令。

(3)、执行 make menuconfig命令来满足内核调试需求

                               

 

Ⅰ、勾选Provide GDB scripts for kernel debugging配置项

                                           

 

Ⅱ、去掉Processor type and features / Build a relocatable kernel / Randomize the address of the kernel image (KASLR)配置项。       

                                         

 

(4)、编译内核,执行make -j4 命令

                                                

(在初次写的时候,由于码字快了,没有加 -j4,等了一会儿时间,-j 参数是指定了可并行执行的作业数,通常设为计算机CPU数目的两倍以提高编译速度。)

(5)、编译结果:有一个内核可执行文件vmlinux,可用于调试,如图所示;还有一个是内核镜像文件bzImage,用于启动和运行 Linux 系统

                                      

 

 

实验二:构建小型Linux系统

(1)、下载busybox的源码,我存放的目录在/home/hws/busybox下面

 

                             

                                              

(2)、以动态链接方式(使用defconfig的默认配置选项)编译Busybox,

         Ⅰ、在Busybox 源码根目录下执行:make defconfig

                                    
         Ⅱ、在Busybox 源码根目录下执行:make install,编译好的 Busybox在 _install目录下,如图所示。

                             

(3)用 Busybox 制作根文件系统

                Ⅰ、创建 100M 磁盘镜像 rootfs.img

               

                   在磁盘镜像中创建 ext4 根文件系统

                    

                   挂载刚刚创建的根文件系统

                

               

Ⅱ、创建文件夹

             

            

            

           在根文件系统上安装busybox,busybox的_install的位置是/home/hws/busybox/_install

           

          

          

Ⅲ、创建系统配置文件 /etc/inittab,做这一步的时候,要先进行sudo bash操作,才能进行创建系统配置文件 /etc/inittab 的步骤。

       

Ⅳ、创建系统设置脚本 /etc/init.d/rcS

Ⅴ、创建系统配置文件 /etc/inittab

Ⅵ、卸载根文件系统 执行 sudo umount rootfs 命令,取消挂载rootfs,如果没有挂载,则相当于两台计算机同时挂载了同一个文件系统rootfs,写操作时可能会破坏rootfs文件系统。

     

(4)、将根文件系统挂载到Linux虚拟机上面

    

    

(5)、如果之前系统启动失败。这是因为以动态链接方式编译的Busybox可执行文件依赖于libc等多个动态链接库,而这些库没有被包含在根文件系统中,导致init程序加载失败,有两种方案可以解决,

方案一:

用ldd命令查看busybox可执行文件所依赖的库:
都能在Ubuntu主机的/lib/x86_64-linux-gnu和/lib64目录中找到。在根文件系统上创建/lib及/lib64这两个目录,
将主机上的libm.so.6、libresolv.so.2、libc.so.6库文件拷贝到根文件系统的/lib目录下;
将ld-linux-x86-64.so.2库文件拷贝到根文件系统的/lib64目录下。

方案二:

以静态链接方式编译Busybox:
(1)    在busybox源码根目录下执行make menuconfig修改配置文件,
勾选Settings / Build static binary (no shared libs)配置项,用新编译出来的busybox可执行文件替换根文件系统中的/bin/busybox文件。

 

现用方案一将Busybox依赖的动态链接库放入根文件系统中。

 

 

 

实验三:通过调试内核了解Linux系统的启动过程。

一、用qemu创建Linux虚拟机,并进入调试模式

二、用gdb调试虚拟机上运行的Linux内核程序

三、使用gdb调试命令,设置、查看、删除断点

                       

四、激活源码浏览窗口。

  Ⅰ、查看激活源码浏览窗口

                                   

Ⅱ、查看函数调用栈

Ⅲ、查看源码、显示当前执行的源码、显示当前执行的源文件信息

Ⅳ、gdb 调试命令- 单步执行等

Ⅵ、内核启动过程中各阶段入口函数的执行顺序、相互调用关系及调用时机:

  1)、在此之前内核一直是stop状态,如果按“c”则继续执行,系统开始启动,并启动到start_kernel函数的位置停在断点处,如下图所示:

                                              

 2)、查看一下start_kernel函数,里面实现了内核核心数据结构的初始化

                                      

 3)、start_kernel一直翻阅下去,可以看到arch_call_rest_init()这个函数,可见,这个start_kernel最终是调用了arch_call_rest_init()进入了外设的初始化阶段。

4)、在上面找到了这个arch_call_rest_init()函数之后,继而调用了rest_init()函数,于是在rest_init处设置断点

                   

5)、可以看出,在左边的调试窗口已经有数据显示出来

查看一下rest_init函数的代码,发现里面是创建了第一个内核线程kernel_init。也就是用0号进程创建1号进程,这个进程的执行函数就是kernel_init()

                              

6)、接下来,在kernel_init设置断点

              

7)、“c”则继续执行,系统开始启动

8)、然后我们找到这个kernel_init()函数,看其函数内容,发现里面通过调用run_init_process来运行init用户程序

                

如果ramdisk_execute_command不为0,就执行该命令成为init User Process;如果execute_command不为0,就执行该命令成为init User Process;如果上述都不成立,就依序执行如下指令

run_init_process(/sbin/init);run_init_process(/etc/init);

run_init_process(/bin/init);run_init_process(/bin/sh);

也就是说会按照顺序从/sbin/init, /etc/init, /bin/init/bin/sh依序执行第一个 init User Process.

如果都找不到可以执行的 init Process,就会进入Kernel Panic.如上图所示的panic(No init found. Try passing init= option to kernel. ”“See Linux Documentation/init.txt for guidance.);

9)、然后我们查看一下run_init_process,并设置断点。

10)、“c”则继续执行,左侧调试窗口也开始启动

run_init_procrss()处断点调试,run_init_process 就是通过 execve()来运行 init 程序。最后,init用户程序将执行后续的操作系统环境初始化设置工作。

 总结

实验一,把这个linux内核编译出来,比较容易,只要把修改通用配置以满足内核调试需求就可以了,编译速度提高的话,后面就加上-j4就可以了。

实验二,创建小型的Linux系统上面,主要是配置根文件系统,最后的话,要卸载根文件系统,如果不卸载的话,rootfs.img类似于用作qemu虚拟机的虚拟磁盘,一块磁盘不能被两台计算机同时使用,否则,当这两台计算机都有写磁盘操作的时候,会引起冲突,将彼此数据覆盖,造成磁盘里的文件系统损坏。运行到最后可能会遇到启动不了的问题,我是通过方案一的方法将Busybox依赖的动态链接库放入根文件系统中来解决的。

实验三,了解Linux系统的启动该过程,是三个实验中我耗费时间最长的一个模块,通过两个终端来了解了Linux系统启动过程的几个主要阶段。首先是内核初始化阶段,设置断点,按”C”键继续执行后,发现了start_kernel,进行了核心数据结构初始化,从start_kernel开始执行,它中间实现了内核的一系列的初始化动作,而且这函数最后面还包含了arch_call_rest_init(),来进入外设初始化阶段。继续运行后调用了rest_init来创建了第一个内核线程kernel_init完成外初始化,点开kernel_init()函数,看其函数内容,发现里面通过调用run_init_process来运行init用户程序,内核线程kernel_init转化为系统中第一个用户进程——init进程,最后init用户程序将执行后续的操作系统环境初始化设置工作。

 

隐形的稻草人,加油鸭~~~

发布了12 篇原创文章 · 获赞 2 · 访问量 1560

猜你喜欢

转载自blog.csdn.net/hongweisong666/article/details/102610449