扒开系统调用的三层皮(下)
注:作者:臧文君,原创作品转载请注明出处,《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
一、给MenuOS增加time和time-asm命令
1、先将menu强制删除:rm menu -rf
2、重新克隆一个新版本的menu:
git clone https://github.com/mengning/menu.git
注:linux系统已是最新版,不需重新克隆。
3、cd menu,查看ls,make rootfs可以自动生成根文件系统并启动MenuOS。
4、help查看命令:
5、如何添加:
(1)vi test.c,在main函数中加入三行MenuConfig的代码。
(2)增加对应的Time函数、TimeAsm函数和MyIdle函数。
(3)make rootfs
二、使用gdb跟踪系统调用内核函数sys_time
1、先进入usr/src/linux-source-4.4/arch/x86_64/boot路径下,然后启动内核并冻结:qemu-system-x86_64 -kernel bzImage -initrd /home/YL/menu/rootfs.img -s -S
2、水平分割,启动gdb,加载符号表file:
gdb /usr/src/linux-source-4.4/vmlinux
注:gdb vmlinux = gdb,再file vmlinux
由于kali是64位机,所以需要设置set arch i386:x86-64,否则会报错“Remote ‘g’packet reply is too long:”
3、连接target remote:1234,设置断点break sys_time,按C继续执行。
4、在qemu窗口中输入time命令,在gdb窗口中list查看代码。
5、按n/s进行单步执行,会进入schedule函数,finish将函数全部执行完。sys_time返回后进入汇编代码处理gdb无法继续跟踪。
6、执行int 0x80后执行system_call对应的代码,设置断点后再执行,并不能停在system_call的位置。
三、系统调用在内核代码中的处理过程
1、系统调用在内核代码中的工作机制和初始化
(1)xyz()和sys_xyz()通过系统调用号匹配起来,int 0x80和system_call通过中断向量匹配起来。
(2)系统调用机制的初始化:trap_init();
2、简化后便于理解的system_call伪代码
int 0x80的下一条指令是ENTRY(system_call),是中断系统调用的处理过程。
sys_call_table(,%eax,4)系统调用表,括号里传递的是系统调用号,整个调用的是sys_time。
简化后的伪代码:
关键信息:
1)在系统调用返回之前,可能会发生进程调度call schedule;
2)在当前进程有可能需要处理进程间通信的信号work_pending。
3、将system_call到iret之间的处理过程画成流程图
4、中断上下文的切换和进程上下文的切换
内核提供的服务在返回到用户态之前可能会发生进程调度,进程调度的里面就存在进程上下文的切换。
内核就是很多不同的中断处理过程的集合。
5、简单浏览system_call到iret之间的主要代码
SAVE_ALL:保存现场。
syscall_call:sys_call_table:调用系统调用处理函数。
iret:中断系统调用过程结束。
总结:
这两周我们完整的学习了系统调用的知识,系统调用实际上就是一种特殊的中断。
在系统调用时,我们需要保存现场:SAVE_ALL,用于保存系统调用时的上下文。接着,根据eax传入的系统调用号,通过sys_call_table查询到调用的系统调用,然后跳转相应的系统调用处理函数后,进行处理。最后,调用restore_all恢复系统调用时的现场,并用iret返回用户态。