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

这是我的第三篇博客,渐渐的意识到写博客不只是为了完成作业,而是要为互联网贡献力量,为别人提供借鉴的模板。

本实验老师已经给了具体的操作步骤,我在给出每步的运行结果后,简要的介绍了每一步涉及到的相关原理知识,以及自己在操作过程中犯过的错误整理。

 

一、本地Linux系统在线环境完成构建调试Linux内核网络代码的环境MenuOS系统

本实验基于ubuntu14.04LTS系统构建,可使用如下命令查看本机的内核版本和系统位数。

cat /proc/version #查看linux内核版本号、gcc编译器版本号、Ubuntub版本号
uname -a #系统位数

Linux version 4.4.0-31-generic (buildd@lgw01-43)          linux内核版本号

gcc version 4.8.4                                                              gcc编译器版本号

Ubuntu 4.8.4-2ubuntu1                                                    Ubuntu版本号

x86_64                                                                             64位系统

1.下载linux内核源代码

从github地址下载:https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.0.1.tar.xz

使用以下代码解压缩:

xz -d linux-5.0.1.tar.xz
tar -xvf linux-5.0.1.tar

最后得到linux-5.0.1内核文件。

 

2.安装内核编译工具

使用下列命令一步安装完毕

build-essentia  Ubuntu缺省情况下,并没有提供C/C++的编译环境,安装该软件包后,编译c/c++所需要的软件包也都会被安装。

Flex       一个生成词法分析器的工具,它可以利用正则表达式来生成匹配相应字符串的C语言代码,其语法格式基本同Lex相同。
Bison      把一个关于“向前查看 从左到右 最右 `LALR’ 上下文无关文法的描述转化成可以分析该文法的 C 或 C++ 程序。它也可以为二义文法生成 “通用的 从左到右 最右 `(GLR)’ 语法分析器。

 

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

 

 

3.配置编译内核

本实验使用如下指令生成了32位x86的配置文件(推荐32位编译速度快)

make i386_defconfig #生成32位x86的配置文件,x86_64_defconfig为64为配置
make menuconfig     #开启文本菜单选项,对窗口有限制,尽量调大窗口
make 或 make -j*    # *为cpu核心数

在第二步的时候会弹出一个菜单项让你选择,选择步骤参见如下:

(1)选择Kernel hacking ,它可以让你接下来配置调试选项

(2)选择Compile-time checks and compiler options

(3)选择Compile the kernel with debug info,使内核映像包含调试信息

(4)选择save和ok

(5)esc退出图形化界面

其次,第三步的make 若后面接-j*为加速编译选项,可以使内核编译速度提升很多。

很多小伙伴看到这会觉得这实验很简单,我按步骤一步一步来就完事了,但背后涉及的东西特别多。比如编译后会生成什么,编译过程又是怎么样的,下面让我来给你一一解答。

首先给大家附上一张图,这张图展示了编译从vmlinux一步一步生成可执行文件bzImage的过程:

这张图中涉及到的相关文件名解释如下:

vmlinux 是ELF文件。即编译出来的最原始的文件,用于kernel-debug,产生system.map符号表

不能用于直接加载,不可以作为启动内核,只是启动过程中的中间媒体。

vmlinuz应该是由ELF文件vmlinux经过OBJCOPY后。并经过压缩后的文件

zImage是vmlinuz经过gzip压缩后的文件,适用于小内核

bzImage是vmlinuz经过gzip压缩后的文件,适用于大内核

vmlinux.bin二进制形式的vmlinux

 

4.升级当前内核系统

因为我使用的并非虚拟机,而是单系统ubuntu电脑,为了以防万一,还是没有敢尝试升级自己的系统内核。

相关的升级命令如下:

sudo make modules_install # 慎重
sudo make install
sudo update-grub
reboot

升级完后,可使用uname -a指令查看自己电脑的内核版本是否升级成功。

 

5.通过QEMU虚拟机加载内核

首先下载qemu工具包,然后通过qemu工具加载编译内核后生成的bzImage。相关指令如下:

 

sudo apt install qemu
qemu-system-i386 -kernel linux-5.0.1/arch/x86/boot/bzImage # make i386_defconfig

 

这个时候并不会显示我们的MenuOS系统,我们还并未构建。

首先从github下载menu文件夹放在liunx-5.0.1下。

然后因此是在64位系统下编译32位需要安装一个依赖包libc6-dev-i386

最后还需要更改menu中的makefile文件(linux版本不一样)。然后执行make rootfs即可加载我们的MenuOS了。

git clone https://github.com/mengning/menu.git
cd menu
sudo apt-get install libc6-dev-i386 # 在64位环境下编译32位需安装
make rootfs

效果图:

碰到的问题总结,执行make rootfs时,明明bzImage的路径对了,相应的路径下也有这个文件,但是系统总是无脑提示找不到没有这个文件或目录。

这个问题折腾了我特别久,甚至卸载重试了3遍还是不行,后来我直接把bzImage拖到当前menu目录下,把makefile中的路径改为当前路径,就执行成功了。

以后碰到类似的问题,先别,忙着质疑自己推翻重做,可以先尝试用其他方法验证到底是哪一步的问题所在,不要一出错就重来。

至此,构建调试Linux内核网络代码的环境MenuOS系统完毕,接下来我们验证MenuOS的网络功能是否正常。

 

 

二、MenuOS的网络功能

首先在github下载Linuxnet文件夹,该文件夹下的lab2为服务端程序,lab3为客户端程序,我们需要分别将其集成到MenuOS系统中。

1.将TCP服务端集成到MenuOS系统中

使用如下指令:

 

cd LinuxKernel 
git clone https://github.com/mengning/linuxnet.git
cd linuxnet/lab2
make
cd ../../menu/
make rootfs 

 

集成成功后,menuOS-help下会多一条replyhi命令,效果图如下:

其实打开lab2的makefile文件可知,它完成的工作如下:

cp test_reply.c       ../../test.c    即将lab2下的test_reply.c的内容复制到menu的test.c中。
cp syswrapper.h    ../../menu   即将lab2下的syswrapper.h文件复制到menu文件夹下。

碰到的问题:还是路径的问题,明明路径没错,但是就是一直弹出文件不存在,但是懂的make rootfs的含义后,你可以自己手动完成make的工作。

我就是自己把test_reply.c的内容复制到menu的test.c中,syswrapper.h文件复制到menu文件夹下。然后就成功了。

 

2.将TCP服务端集成到MenuOS系统中

 

使用如下指令:

cd linuxkernel 
git clone https://github.com/mengning/linuxnet.git
cd linuxnet/lab3
make rootfs  

打开lab3中的makefile文件,可以发现同样可以手动完成make的工作,但是奇怪的是这次我没有报上次的错误,直接就成功了。

集成成功后,menuOS-help下会多一条hello命令,效果图如下:

 

 

3.测试通信功能

输入hello,可以看到如下结果:

 

可见在MenuOS上能够完成TCP客户端和服务器发送和接收hello/hi,也就是MenuOS的网络可以正常工作。

 

 

三、gdb跟踪内核代码

首先执行下列指令

 

qemu-system-x86_64 -kernel linux-5.0.1/arch/x86_64/boot/bzImage -initrd rootfs.img-append nokaslr -s -S

 

该指令可以观察到QEMU启动MenuOS的过程中停止了,可以理解为等待执行。

得到的结果图如下:

然后另外开一个终端,打开gdb指令,设置断点

运行如下指令:

 

gdb
file ~/LinuxKernel/linux-5.0.1/vmlinux
target remote:1234
break start_kernel #设置断点在start_kernel函数
c #继续运行
list #查看上下文

 

运行结果图如下:

 

 

可以看到gdb追踪到了start_kernel()函数

同理,通过这种设置断点的方式,可以追踪到大多数内核函数。

对于sys_socketcall()函数,其所在的位置在大概2500多行,执行过程会比较慢。

但是,小伙伴可能很疑惑,start_kernel()这个函数作用到底是啥??从字面意思来看它好像是内核开启的起点。接下来我将结合分析linux内核的启动过程,来向你

阐述该函数在内核启动中的重要作用。

 

该图引自博客:https://www.cnblogs.com/jjmcao/p/9322324.html

这篇博客写的很好,详细的阐述了linux启动过程。

start_kernel()函数用来启动一系列的初始化函数并初始化各种设备,完成Linux核心环境的建立。

0号进程:所有的进程的祖先叫做进程0,它就是系统在初始化阶段由start_kernel()函数创建的第一个内核线程。

1号进程:又陈init进程,由进程0在start_kernel()调用rest_init创建。

start_kernel也是正式进入c语言的标志,之前都是汇编代码。

至此,通过gdb可以跟踪到内核代码,比如start_kernel、sys_socketcall等内核函数

 
posted @ 2019-12-10 22:06  一只猫的旅行~~  阅读(277)  评论(0编辑  收藏  举报