构建调试Linux内核网络代码的环境MenuOS系统

实验目的:

本次的实验主要构建MenuOS,在其中构建gdb环境,并将通信实验的代码嵌入其中,在MenuOS中验证通信实验结果。

实验原理:

一、操作系统的启动

最初计算机依靠一段二进制码来启动,并不是真正的计算机启动程序。计算机在开始加电的时候不具备工作能力,此时RAM芯片中包括的都是一些没有意义的随机数据,而没有操作系统在运行。在开始启动的时候,一个特殊的硬件电路在CPU的引脚上产生一个RESET复位信号,就是那个复位信号。在这个信号产生之后,就会把处理器的一些寄存器设置成固定的值,并执行物理地址0xfffffff0(x86架构是高地址,对于arm架构一般是低地址)那个地方的代码。硬件把这个地址映射独到ROM,ROM中所存放的程序集实际上在80x86体系中叫做基本输入/输出系统(Basic Input/Output System——BIOS)因为它包括了几个中断的低级过程。所有的操作系统在启动时,都要通过这些程序对计算机硬件设备进行初始化。一些操作系统,如微软的MS-DOS,依赖于BIOS实现大部分系统调用。

Linux中BIOS使用的是实模式,以为这是在一通电的时候就加载的所以不能用一些逻辑描述。实模式的地址用一个段地址和一个偏移量表示(seg段基址+offset)所以物理地址就是seg*16+offset。所以CPU这里面就可以直接的找到内存地址,此时还没建立一张逻辑表示和物理地址对应表。

Linux在启动阶段必须使用BIOS,此时Linux必须从磁盘或者其他的外部存储介质上获得系统的内核镜像(kernel Image)BOIS启动过程实际上分为四个过程:

1、对硬件的检测

2、硬件初始化

3、索索一个操作系统来启动

4、找到一个有效的设备。

 

二、内核的搭建

构建一个简单的Linux内核。大体上是分为两个步骤,首先是下载内核源代码编译内核,然后制作根文件系统。

 

实验工具简介:

一.QEMU介绍

QEMU是一套由法布里斯·贝拉(Fabrice Bellard)所编写的以GPL许可证分发源码的模拟处理器,在GNU/Linux平台上使用广泛。

QEMU是一个主机上的VMM(virtual machine monitor),通过动态二进制转换来模拟CPU,并提供一系列的硬件模型,使guest os认为自己和硬件直接打交道,其实是同QEMU模拟出来的硬件打交道,QEMU再将这些指令翻译给真正硬件进行操作。

 

二.gbbsever简介

gdb+gdbserver是一种远程调试方法,它其实主要运用于嵌入式系统的调试,由于嵌入式系统资源有限性,一般不能直接在目标系统上进行调试,通常采用gdb+gdbserver的方式进行调试。Gdbserver在目标系统中运行,gdb则在宿主机上运行。

在本次实验中,我们在虚拟机qemu上运行menuos系统,这个系统比较简单,不具备太多的资源,所以也采用gdb+gdbserver的远程调试方法。

 

此时要进行GDB调试,目标系统必须包括gdbserver程序,宿主机也必须安装gdb程序。很gdbServer是gdb的一个服务端,那gdb就是相对应的那个客户端了。这二者之间是TCP连接,gdb发送命令给gdbServer,gdbServer收到命令后会控制进程进行相对应的操作,然后把操作结果反馈给gdb。

 

 

实验流程:

总的实验流程有如下几步:

一、下载linux内核源代码编译内核

1.安装内核编译工具和搭建 内核编译环境

使用apt命令下载内核编译

sudo apt install build-essential flex bison libssl-dev libelf-dev libncurses-dev

make i386_defconfig          #生成32位x86的配置文件

 

2.下载Linux内核

这里选用linux-5.0.1的内核

mkdir LinuxKernel  #   创建内核目录

cd LinuxKernel     #   打开内核目录

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.0.1.tar.xz   #下载linux-5.0.1的内核

xz -d linux-5.0.1.tar.xz  #解压命令,得到一个tar的归档包

tar -xvf linux-5.0.1.tar #解包命令,得到归档包中的文件

cd linux-5.0.1       #进入内核目录

 

 

3.编译内核配置

输入命令行:

make menuconfig

跳出图形界面,在图形界面中选中Kernel hacking ,再选中这个目录下的Compile-time checks and compiler options,然后在Compile the kernel with debug info选项前键入y

做这个配置主要是为了后面进行gdb调试

 

4.进行系统内核升级

鉴于其他同学的经验,没有做这一部分的操作,只贴出相关代码

sudo make install

sudo update-grub

reboot

uname -a

 

 

二.制作根文件系统

1.安装QEMU虚拟机

我把该虚拟机也装在了上文建立的LinuxKernel目录下

cd ~/LinuxKernel/

sudo apt install qemu  # 安装qemu软件包

 

2.下载制作好的根文件系统MenuOS,并将其进行修改和编译

git clone https://github.com/mengning/menu.git      #下载MenuOS系统

mkdir rootfs                                     #创建rootfs当作MenuOS的根目录

cd menu                                        #打开下载好的Menu目录

sudo apt-get install libc6-dev-i386 #安装libc6-dev-i386,在64位机上编译32位系统所需要的环境

 

后面要修改一下,makefile文件,因为原有的根文件系统对应的Linux内核版本和我使用的版本不符,具体修改语句为:

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

将其中的内核版本改成你使用的版本即可

然后重新编译根文件系统,这里的makefile内嵌了qemu语句,及上述语句,编译成功后会自动打开qemu虚拟机,加载MenuOS。

make rootfs  #制作根文件系统

编译成功后截图:

 构建调试Linux内核网络代码的环境MenuOS系统_第1张图片

 

 

 

 

三. 构建调试Linux内核网络代码的环境MenuOS

1.将TCP网络通信程序的服务端程序嵌入到MenuOS 系统中

下载代码

cd ~/LinuxKernel 

git clone https://github.com/mengning/linuxnet.git

cd linuxnet/lab2

 

编译

make

 

将程序嵌入文件系统,通过运行脚本将该程序嵌入进MenOS系统

cd ../../menu/

make rootfs     

2.将TCP网络通信程序的客户端程序嵌入到MenuOS系统中

这和步骤一类似

cd ~/LinuxKernel 

cd linuxnet/lab3

 

但在lab3中需要修改一下Makefile文件,修改方法和上文一样,改一下版本号即可

vim Makefile #修改Makefile文件

make rootfs

 

3.在menuOS中输入help指令,发现多出了replyhi和hello指令即代表,嵌入成功

 构建调试Linux内核网络代码的环境MenuOS系统_第2张图片

 

 

 

4.运行结果

 构建调试Linux内核网络代码的环境MenuOS系统_第3张图片

 

 

 

四. 进行gdb调试跟踪start_kernel内核函数

1.在qemu中运行系统并启动gdbserver

qemu -kernel linux-5.0.1/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.在宿主机上进行gdb调试

新建一个终端

然后输入下列指令

gdb

file ~/LinuxKernel/linux-5.0.1/vmlinux   #在gdb界面中targe remote之前加载符号表

break start_kernel  #断点的设置可以在target remote之前,也可以在之后

target remote:1234 #建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行

c #按c 让qemu上的Linux继续运行

list #输入list指令可以查看断点处的代码

运行结果:

 构建调试Linux内核网络代码的环境MenuOS系统_第4张图片

 

  

start_kernel函数分析

在start_kernel中进行一系列极为重要的初始化。最后执行位于init/main.c中的rest_init()来创建系统的第一个进程init。至此,内核启动完成。之后init进程会进行一些初始化和根文件系统挂载。

在start_kernel()的最后,系统会调用rest_init(),通过执行其中的kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND)来创建系统的第一个进程,即init进程。Init进程首先进行一系列的硬件初始化,然后挂载根文件系统。最后 init 进程会执行用户传递过来的“init=”启动参数执行用户指定的命令,或者执行以下几个进程之一:

execve(“/sbin/init”,argv_init,envp_init);

execve(“/etc/init”,argv_init,envp_init);

execve(“/bin/init”,argv_init,envp_init);

execve(“/bin/sh”,argv_init,envp_init)。

 

如果这些都无法执行(无法找到),系统的启动会宣告失败。Init启动后,会读取/etc/inittab这一配置文件,根据inittab文件内容进行一些设置或对一些指令做出解释。

你可能感兴趣的:(构建调试Linux内核网络代码的环境MenuOS系统)