学号372,原创作品转载请注明出处
参考:Linux Kernel
实验环境
- 内核代码:Linux Kernel
- Ubuntu虚拟机:运行环境,本次使用VM Ware安装的Ubuntu18
- QEMU:本次使用的模拟器,运行内核代码
- gdb断点调试工具
实验过程
1.编译内核
- 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 //得到内核文件
- cd linux-5.0.1
- sudo apt install build-essential flex bison libssl-dev libelf-dev libncurses-dev //安装依赖工具,按需安装,否则会报错,贴出所需供参考,有些工具我装过所以我并未全部安装。遇到的问题之一如下图所示,反正缺什么工具装什么
sudo apt install libssl-dev 即可解决 - make i386_defconfig //这部很关键,我使用的是32位的qemu,因此kernel需要同为32位的;
- make menuconfig //然后kernel hacking,->Compile-time checks and compiler options,选择 [*]compile the kernel with debug info。效果如下图:
//我在此处并未make,在第3步检查完qemu后才make
2.制作img文件
在linux-5.0.1目录下:
- mkdir rootfs
- git clone https://github.com/mengning/menu.git
- cd menu
- gcc -o init linktable.c menu.c test.c -m32 -static
- cd ../rootfs
- cp ../menu/init ./
- find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
3.虚拟机加载内核
在linux-5.0.1目录下:
- sudo apt install qemu //没装过的话装一下,我安装过
- sudo ln -s /usr/bin/qemu-system-i386 /usr/bin/qemu //连接一下,以后简单的qemu即可,不需要打长长的qemu-system-i386
- make //开始编译,过程可能比较久,等待即可。若出现卡死,然后过一段时间后虚拟机重启这种问题,需检查依赖工具是否齐全,我出现过类似问题,补齐需要的工具后正常编译完
- qemu -kernel linux-5.0.1/arch/x86/boot/bzImage //和3行二选一,32位就本条命令;若64位则 qemu-system-x86_64 -kernel linux-5.0.1/arch/x86/boot/bzImage
4.构造MenuOS
在linux-5.0.1目录下:
- mkdir rootfs
- git clone https://github.com/mengning/menu.git
- cd menu
- gcc -pthread -o init linktable.c menu.c test.c -m32 -static
- cd ../rootfs
- cp ../menu/init ./
- find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
- qemu arch/x86/boot/bzImage -initrd rootfs.img //此处可打开qemu窗口,确保是在linux-5.0.1目录下执行的本命令,正常情况下是可以打开下图界面的;若报错“文件无法找到”,请修改指令内使用到的文件为其对应目录
若可正常运行,则关闭页面,开始下一步。
5.测试用例
我的学号是372,取最后2位相同的为sigsuspend()函数,如下图所示:
72号进程为中断,正常demo中尚可通过强制中断来发送一个中断信号使该函数触发,但在gdb调试中无法触发中断信号因此无法触发该函数,所以无法测试。因此改为172号:prctl().
写一个.c文件调用该函数(即prctl())
#include<stdio.h>
#include<pthread.h>
#include<sys/prctl.h>
void* tmain(void*arg)
{
char name[32];
prctl(PR_SET_NAME,(unsigned long)"xx");
prctl(PR_GET_NAME,(unsigned long)name);
printf("%s\n", name);
}
int main(void)
{
pthread_t tid;
pthread_create(&tid,NULL, tmain,NULL);
pthread_join(tid,NULL);
return 0;
}
编译并运行之,可运行出期望的结果
使用gdb断点调试,分析系统调用过程:
设置断点至函数调用处
在函数调用前的断点前,指令在call上一条,eax值如下:
发生系统调用时,指令执行至call时,eax中的值为调用编号,系统通过存储在eax中的系统调用编号实现定向的系统调用。
通过eax寄存器中的值的变化可以发现,用户程序使用的prctl()函数会请求一个系统调用,系统调用会触发中断来执行该函数;该函数对应的内核的函数编号为172号,因此系统通过eax寄存器传递172这个值,在中断处理函数中找到对应的函数并执行。
实验总结
发生系统调用时,int 0x80触发中断,系统保存现场后进入内核态,根据调用号寻找到相应的处理程序完成中断处理,结束后再返回。
系统调用的工作机制是:当用户态进程调用一个系统调用时,CPU切换到内核态并执行对应的内核函数,内核函数由操作系统预先定义并分配了编号,调用过程中系统通过eax寄存器传递调用函数的编号,通过编号寻找相应的中断处理程序完成所需的功能,完成后通过已保存的现场返回并继续往下执行。