作业要求
阅读学习教材「庖丁解牛Linux 操作系统分析 」第6章,有问题优先使用chatgpt等AI工具。或者到蓝墨云班课中提问,24小时内回复,鼓励解答别人问题,提问前请阅读「如何提问」。
教材深入学习关注豆列「Linux内核及安全」。
学习蓝墨云班课中第六周视频「扒开系统调用的三层皮?(下)」,并完成实验楼上配套实验五。,注意从下往上看。基于树莓派或其他平台完成ARM相关内容。
作业标题 “学号《Linux内核原理与分析》第X周作业”,重点是遇到的问题和解决方案内容涵盖教材学习和视频,提交格式用Markdown,同时提交转换的 PDF(VSCode 有相关插件)。
实验五:分析 system_call 中断处理过程
1、使用 gdb 跟踪分析一个系统调用内核函数(您上周选择的那一个系统调用),系统调用列表参见 torvalds/linux。推荐在实验楼 Linux 虚拟机环境下完成实验。
2、根据本周所学知识分析系统调用的过程,从 system_call 开始到 iret 结束之间的整个过程,并画出简要准确的流程图,撰写一篇署名博客
一、重新定义系统调用getpid和getppid
进入实验楼,与上周操作相同,重新编译内核后,进入menu界面下,编辑test.c。
cd ~/LinuxKernel
rm -rf menu
git clone https://github.com/mengning/menu.git
cd menu
vim test.c

在test.c中加入上一实验的的getpid和getppid代码,并在test.c中的main函数中加入两个MenuConfig函数进行这两个系统调用函数的配置:
int _20252803_main1(){
pid_t pid=getpid();
pid_t ppid=getppid();
printf("pid=%d ppid=%d\n",pid,ppid);
}
int _20252803_main2() {
pid_t pid, ppid;
asm volatile("mov $0x14, %%eax\n\t" // getpid 的ID为20
"int $0x80\n\t"
"mov %%eax, %0\n\t"
: "=r" (pid) :: "%eax");
asm volatile("mov $0x40, %%eax\n\t" // getppid 的ID为64
"int $0x80\n\t"
"mov %%eax, %0\n\t"
: "=r" (ppid) :: "%eax");
printf("pid=%d ppid=%d\n", pid, ppid);
return 0;
}

两个配置系统调用函数的MenuConfig函数如下:
MenuConfig("getpid1","system_call_1",_20252803_main1);
MenuConfig("getpid_asm_2","system_call_asm_2",_20252803_main2);

二、make rootfs自动编译脚本:

三、使用 gdb 跟踪分析getpid和getppid系统调用内核函数
先启动冻结的内核,再另打开一个shell,进行GDB调试,建立GDB和gdbserver之间的连接。
冻结命令如下:
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

gdb调试命令如下:
gdb file linux-3.18.6/vmlinux
gdb target remote:1234
gdb b sys_getpid


四、分析整个过程,并画出简要准确的流程图
1、用户态触发系统调用(陷入前)
准备工作:用户程序通过寄存器传递参数,将系统调用号存入 eax 寄存器(x86 架构)。
触发中断:执行 int $0x80(x86 传统)或 syscall(x86_64 现代)指令,触发软件中断。
CPU 自动操作:切换特权级(用户态 CPL=3 → 内核态 CPL=0)。硬件自动保存用户态关键上下文(cs、eip、eflags 寄存器入栈)。
2、进入内核态:system_call 入口
跳转入口:CPU 自动跳转到内核预设的中断处理入口 system_call(位于 arch/x86/kernel/entry_32.S 或 entry_64.S)。
保存完整上下文:内核手动将用户态寄存器(ebx、ecx、edx 等)压入内核栈,防止被内核操作覆盖。
合法性检查:验证系统调用号(eax)是否在允许范围内(≤ NR_syscalls),若非法则跳转至错误处理(返回 -ENOSYS)。
3、执行系统调用处理函数
查表映射:通过系统调用号(eax)查询系统调用表(如 sys_call_table),找到对应的内核处理函数(如 sys_getpid)。
执行操作:调用该函数完成具体功能(如获取进程 ID),将返回值存入 eax 寄存器。
4、系统调用返回前处理
信号检查:执行 syscall_exit 逻辑,检查用户态程序是否有未处理的信号。
信号处理:若有信号,先跳转至用户态的信号处理函数,处理完成后回到系统调用流程。
5、内核态返回用户态
恢复上下文:从内核栈弹出之前保存的用户态寄存器(ebx、ecx 等)。
执行返回指令:通过 iret(x86)或 sysret(x86_64)指令,CPU 自动完成:从栈中恢复用户态 cs、eip、eflags 寄存器。切换特权级(内核态 CPL=0 → 用户态 CPL=3)。
用户态续行:用户程序从 int $0x80 或 syscall 的下一条指令继续执行,通过 eax 获取返回值。


被折叠的 条评论
为什么被折叠?



