您的位置:首页 > 运维架构 > Linux

Linux内核分析(三)

2016-03-10 21:20 751 查看
Linux 内核分析——【实验三:初探Linux内核源代码】

前面两节介绍了计算机的基本工作方式,包括栈在程序执行过程中的作用,以及进程之前的调用。总的来说,有几点必须要牢记的:

1)目前计算机采用的都是冯诺依曼体系结构,它的特点是“程序存储,顺序执行”;

2)计算机能够处理多任务,主要是采用了中断的机制,中断正在执行的程序,保存现场,实现进程上下文切换;

3)栈在函数调用和中断过程起着至关重要的作用,它实现了多个进程“同时”执行的可能。

下面,我们将对Linux内核源代码进行简要分析,了解它的主要组成部分,每部分大致功能是做什么,程序的入口在哪?

首先,搭载实验环境,步骤如下:

(1)下载内核源代码,编译内核
~$ cd Linux/Lab3
~$ wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz  #下载Linux-3.18.6版本内核代码
~$ xz -d linux-3.18.6.tar.xz   #解压缩
~$ tar -xvf linux-3.18.6.tar   #解包
~$ cd linux-3.18.6
~$ make i386_defconfig   #配置编译环境
~$ make # 进行编译

(2)制作根文件系统,内核系统运行时需要挂载它
~$ cd Linux/Lab3
~$ mkdir rootfs   #创建一个根文件系统
~$ git clone https://github.com/mengning/menu.git     #下载menu,用于制作根文件系统
~$ cd menu
~$ gcc -o init linktable.c menu.c test.c -m32 -static –lpthread   #编译链接,输出为可执行文件init
~$ cd ../rootfs
~$ cp ../menu/init ./   #复制init到rootfs目录
~$ find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img    #将rootfs下init文件打包成rootfs.img文件
~$ cd .. && ls   #查看文件是否制作成功,如下**图3-1**

3)启动MenuOS系统
~$ cd Linux/Lab3
~$ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img    #-initrd rootfs.img就是挂载文件系统  如下图3-1

4)为了方便后面更加清楚地了解内核代码运行情况,我们需要在编译的时候加上调试信息,重新设置编译环境
~$ make menuconfig   #使用图形化界面设置编译环境,与1)中的"make i386_defconfig"对应,在linux-3.18.6目录下
#如下图3-2,图3-3,图3-4
#注意:这里可能会报错,需要安装一个ncurses库(ubuntu系统默认没有安装), ~$ sudo apt-get install libncurses5-dev 可以百度一下
~$ make #重新编译

5)编译好后我们就可以用gdb进行调试,如下图3-5
~$ qemu arch/x86/boot/bzImage -initrd ../roorfs.img -s -S #关于 -s ,-S 选项说明如下:
#-S 在cpu启动时暂停
#-s 为gdb提供一个调试端口tcp:1234 ,如是不想使用1234端口,可通过-gdb tcp::xxxx 来代替-s

6)新建一个shell终端(可以在当前shell窗口右键新建一个终端)
~$ gdb -q  #进入gdb调试,-q阻止打印gdb的相关信息
(gdb) file vmlinux  #加载符号表
(gdb) target remote:1234 #建立gdb和gdbserver之间的连接
(gdb) break start_kernel #设置断点
(gdb) c    #continue 让qemu上的linux运行
(gdb) list  #程序会运行到start_kernel的地方然后暂停,list可以查看代码,如下图3-6


此外,我们还可以使用更多的gdb调试命令来跟踪linux内核的运行过程,如下:

相关gdb调试命令

(1) clear #清除所有断点

(2) delete [linenum] #清除代码中行数为linenum的断点

(3) disable [linenum] #取消断点,可通过enable恢复

(4) step [count] #单步调试count步,会进入调用的函数中

(5) next [count] #单步调试count步,不会进入调用的函数

(6) finish #当前函数执行到返回

(7) information frame #显示栈信息

(8) search < regexp> #搜索源代码

(9) print < var>/< register>/< memory> #查看数据

(10) information registers #查看所有寄存器信息

相关截图

图3-1 根文件所有目录





图3-2 make menuconfig配置过程,选择”Kernel hacking —> ” 回车进入



图3-3 选择”Comile-time checks and comp[iler options —>” 回车进入



图3-4 找到”Compile the kernel with debug info”,输入星号”*”, 选择”save”保存,再依次退出



图3-5 使用gdb调试



图3-6 具体调试过程



接下来,我们来看一下,linux内核代码的目录结构





其中,init目录是关于内核初始化的,该目录下的main.c文件中,有一个start_kernel的函数,它可以看作是系统启动的入口。

计算机在开机时,会先让cpu的CS:EIP指向BIOS所在位置,即先执行BIOS中的指令,BIOS程序会进行硬件检测和相关初始化,然后会把引导程序(即BootLoader,一般在硬盘的第一个扇区MBR)加载到内存中,接着将控制权交给引导程序,实现开机过程。而引导程序需要负责操作系统的初始化,然后再启动系统。这整个过程就包括了在start_kernel函数之前和之后两部分,之前部分全部是初始化的汇编指令,之后部分是初始化的C代码,因为C语言也需要构造一个合适的运行环境,以便于启动操作系统。此外,启动时还需要指定kernel,initrd(初始RAM磁盘,在实际根系统文件可用之前临时挂载到系统中的一个临时根文件系统),和root所在的分区和目录。当所有初始化工作完成后,会启动一个0号进程,它处于死循环状态,等待用户任务的到来。当有任务时,则通过中断,跳转出去执行其他任务,执行完后又返回来。

=========== 王杰 原创作品转载请注明出处==============

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息