跟踪分析Linux内核5.0系统调用处理过程
学号339作品,转载请注明出处
实验出处: https://github.com/mengning/linuxkernel/
实验要求
举例跟踪分析Linux内核5.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/ ”,博客内容的具体要求如下:
- 题目自拟,内容围绕系统调用进行
- 博客中需要使用实验截图
- 博客内容中需要仔细分析系统调用、保护现场与恢复现场、系统调用号及参数传递过程
- 总结部分需要阐明自己对系统调用工作机制的理解
实验环境
- ubuntu 18
- gcc 4.8
编译环境
- 下载内核5.0内核代码,配置编译Linux内核,使之携带调试信息
mkdir LinuxKernel
- 下载内核源码:Linux内核5.0 source code
- 解压到LinuxKernel目录下,
cd linux-5.0.1
make menuconfig
,找到kernel hacking,->Compile-time checks and compiler options,选择 [*]compile the kernel with debug infomake
- 制作根文件系统
cd ..
mkdir rootfs
git clone https://github.com/mengning/menu.git
cd menu
sudo apt install gcc-multilib
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
- 启动MenuOS
qemu-system-i386 -kernel linux-5.0.2arch/x86/boot/bzImage -initrd rootfs.img
- 跟踪调试内核启动
qemu-system-i386 -kernel bzImage -initrd rootfs.img -S -s -append nokaslr
- 运行qemu虚拟机后,在当前目录新建一个终端窗口,运行下列命令:
cd linux-5.0
gdb vmlinux
注:如果出现了start_kernel断点断不住的情况,同时确认在kernel-hack选项勾了
[*] compile the kernel with debug info
后,可以考虑用hbreak (GDB的硬件断点) 替代普通断点,在这之后就可正常使用break。
- 进入gdb界面后,输入
`target remote:1234`
b sys_pipe
c
建立与qemu调试端口的attach
选择系统调用号后两位与您的学号后两位相同的系统调用进行跟踪分析
学号后两位39,在/usr/include/asm/unistd_32.h中可查得#define __NR_mkdir 39,mkdir系统调用作用是新建一个目录
#include <sys/stat.h>
int mkdir(const char *pathname, mode_t mode);
int _mkdir(int argc, char *argv[])
{
int returnValue = 0;
char *dirName = "new dirAsm";
mode_t mode = S_IRWXU;
asm
(
"movl $39, %%eax\r\n" /*将39号系统调用号放到eax寄存器中*/
"movl %1, %%ebx\r\n"/*将第一个参数文件名的地址,存入ebx寄存器*/
"movl %2, %%ecx\r\n"/*将第二个参数文件夹的访问权限存入ecx寄存器*/
"int $0x80\r\n"
"movl %%eax, %0"/*最后将返回值放入returnValue变量中*/
:"=m"(returnValue)
:"d"(dirName),"D"(mode)/*使用edx寄存器保存dirName的地址,edi保存mode*/
);
printf("%d\n",returnValue);
return 0;
}
int main()
{
...
...
MenuConfig("mkdir","creat new catalogue",_mkdir);
}
- 重新编译制作rootfs.img,并启动
qemu -kernel linux-5.0.2/arch/x86/boot/bzImage -initrd rootfs.img -s -S -append nokaslr
- 跟踪系统调用
qemu-system-i386 -kernel linux-5.0.2/arch/x86/boot/bzImage -initrd rootfs.img -S -s -append nokaslr
- 终端输入
cd linuxkernel/linux-5.0.2
gdb vmlinux
(gdb) target remote:1234
(gdb)b sys_mkdir
(gdb)c
- 当调用一个系统调用时,CPU从用户态切换到内核态并开始执行一个system_call和系统调用内核函数;
- 在Linux中通过执行int 0x80来触发系统调用,内核为每个系统调用分配一个系统调用号,用户态进程必须明确指明系统调用号,需要使用EAX寄存器来传递。
mkdir系统调用的流程是:
- 调用mkdir API
- 通过API调用
39号(学号339)
系统调用,使用int $80软中断进入内核态- 压入用户的栈顶地址、当前的状态字还有CS:EIP。
- SAVE ALL保存现场,内核进程根据用户的系统调用号,查找系统调用表(sys_call_table),找到中断处理子程序的地址,并且进入内核函数
- 完成调用中断处理子程序后,使用RESTORE ALL还原现场,返回调用的用户态空间
注:API调用
应用编程接口(Application program interface,API)和系统调用是不同的,API只是一个函数定义系统调用通过软中断向内核发出一个明确的请求。Libc库定义的一些API引用了封装例程(wrapper routine,唯一目的就是发布系统调用)一般每个系统调用对应一个封装例程库再用这些封装例程定义出给用户的API不是每个API都对应一个特定的系统调用API可能直接提供用户态的服务(如,一些数学函数)。一个单独的API可能调用了几个系用调用,不同的API可能调用同一个系统调用。
总结
本实验让我了解了系统调用的工作机制,系统调用是一个用户态到内核态再到用户态的过程(用户态->内核态->用户态),其中系统中断的调用有三层分别是API、system_call、system_service。内核中大多数系统函数调用名称以sys_为前缀,每个系统调用有唯一的系统调用号。应用程序通过软中断来通知内核,进入系统调用入口system_call,从而执行对应的系统调用函数。