Linux操作系统分析与设计——跟踪分析Linux5.0内核处理系统调用的过程
实验要求
编译内核5.0 qemu -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd
rootfs.img 选择系统调用号后两位与您的学号后两位相同的系统调用进行跟踪分析
https://github.com/mengning/menu
给出相关关键源代码及实验截图,撰写一篇博客(署真实姓名或学号最后3位编号),并在博客文章中注明“原创作品转载请注明出处 +
https://github.com/mengning/linuxkernel/ ”,博客内容的具体要求如下:
题目自拟,内容围绕系统调用进行; 博客中需要使用实验截图 博客内容中需要仔细分析系统调用、保护现场与恢复现场、系统调用号及参数传递过程
总结部分需要阐明自己对系统调用工作机制的理解。
实验环境
- 内核代码:Linux Kernel Ubuntu虚拟机:
- 运行环境,本次使用VM Ware安装的Ubuntu14
- QEMU:本次使用的模拟器,
- 运行内核代码 gdb断点调试工具
实验过程
1.编译内核
git clone 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
效果如下图
sudo make -j8
编译完成会在arch/x86/boot/
下产生bzImage
2.制作根文件系统
在linux-5.0文件夹下:
sudo mkdir rootfs
git clone https://github.com/mengning/menu.git
cd menu
sudo gcc -pthread -o init linktable.c menu.c test.c -m32 -static
cd ../rootfs
sudo cp ../menu/init ./
sudo su
find . | cpio -o -Hnewc |gzip -9 > ../../rootfs.img
cd ../../
qemu -kernel linux-5.0/arch/x86/boot/bzImage -initrd rootfs.img
这里运行的是老师的demo,测试可以启动MenuOS。
3.编写自己的系统调用
我的学号后两位是57,查询系统调用表,看到对应的系统调用是getpgid //获取指定进程组标识号
进入menu文件夹下修改test.c文件,增加系统调用。
在test.c文件中增加函数:
int SetPgid(int argc,int *argv[] ){
printf("Excute No.57 System Call\n");
int current_pid = getpgrp();
printf("current pid=%d\n",current_pid);
current_pid = 225357;//my student number
printf("modify to %d",current_pid);
return 0;
}
同时修改help菜单项增加一条:
MenuConfig("set_pgid","Set Progress Group id",SetPgid);
再次执行步骤3中的制作根文件系统的过程:
sudo mkdir rootfs
git clone https://github.com/mengning/menu.git
cd menu
sudo gcc -pthread -o init linktable.c menu.c test.c -m32 -static
cd ../rootfs
sudo cp ../menu/init ./
sudo su
find . | cpio -o -Hnewc |gzip -9 > ../../rootfs.img
cd ../../
qemu -kernel linux-5.0/arch/x86/boot/bzImage -initrd rootfs.img
执行结果是:
4.使用gdb打断点追踪系统调用过程
让qemu启动时暂停
qemu -kernel linux-5.0/arch/x86/boot/bzImage -initrd rootfs.img -S -s
另开一个窗口
gdb
file linux-5.0/vmlinux
target remote:1234
//设置个三个断点
b start_kernel
b sys_getpgrp
b sys_setpgid
c//继续执行
list//查看代码
i r//查看寄存器的值
可以发现,在获取进程组号PGID确实调用了sys_getpgrp这个函数,但是由于我写的修改代码是在函数内部修改了局部变量,并没有实际达到修改group id的效果,这一点有待改善。相当于追踪了sys_getpgrp这个函数调用,从寄存器的值可以看到sys_getpgrp函数的指令的地址,确实进行了系统调用。
实验总结
发生系统调用时,int 0x80触发中断,系统保存现场后进入内核态,根据调用号寻找到相应的处理程序完成中断处理,结束后再返回。
系统调用的工作机制是:当用户态进程调用一个系统调用时,CPU切换到内核态并执行对应的内核函数,内核函数由操作系统预先定义并分配了编号,调用过程中系统通过eax寄存器传递调用函数的编号,通过编号寻找相应的中断处理程序完成所需的功能,完成后通过已保存的现场返回并继续往下执行。
当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数。
在Linux中是通过执行int $0x80来执行系统调用的, 这条汇编指令产生向量为128的编程异常。
整个系统调用过程如下:
1.用户应用程序填充系统调用的寄存器
2.进程从用户态切换到内核态, 并执行系统调用 entry_SYSCALL_32
3.entry_SYSCALL_32切换到内核栈, 保存现场(通用寄存器, 旧的栈. flags)
4.entry_SYSCALL_32 调用 sys_call_table 中的函数, 如果正确调用对应的函数, 如果错误退出.
5.系统调用完成, 恢复现场(通用寄存器, 旧的栈. flags).