Linux内核分析——扒开系统调用的三层皮(下)
一、给MenuOS增加time和time-asm命令
1、实验过程:
rm menu -rf 强制删除
git clone http://github.com/mengning/menu.git 克隆相关信息
make rootfs 一个脚本,自动编译自动生成根文件系统,并自动启动MenuOS
2、如何给MenuOS增加time和time-asm命令
(1)更新menu代码到最新版
(2)在test.c中的main函数里增加MenuConfig
(3)增加对应的Time函数和TimeAsm函数
(4)make rootfs 自动编译脚本
二、使用gdb跟踪系统调用内核函数sys_time
1、实验过程:
2、使用gdb跟踪调试内核
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
# 关于-s和-S选项的说明:
# -S freeze CPU at startup (use ’c’ to start execution)
# -s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项
3、跟踪系统调用内核函数sys_time:
gdb
(gdb)file linux-3.18.6/vmlinux //在gdb界面中targe remote之前加载符号表
(gdb)target remote:1234 //建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
(gdb)b sys_time
(gdb)c //启动到MenuOs,在MenuOs中使用time,会停在time函数处
(gdb)list //对应代码
(gdb)s //单步执行
(gdb)finish //将这个函数执行完
以上两步重复使用,可以看到sys_time函数中的函数,直到看见return i
执行int 0x80之后执行system_call对应的代码
(gdb)b system_call //是可以设置断点的,但是这段是汇编代码,运行时不能在这个断点处停下逐句分析。
system_call不是一个正常意义上的函数,只是一段汇编代码。
三、系统调用在内核代码中的处理过程
1、系统调用在内核代码中的工作机制和初始化
(1)进系统调用机制的初始化:
(2) trap_init里面有一个set_system_trap_gate函数,函数定义中有系统调用的中断向量SYSCALL_VECTOR和汇编代码入口system_call。
(3)一旦执行int 0x80,CPU直接跳转到system_call来执行。
2、简化后便于理解的system_call伪代码
(1)伪代码:
分析:
1)系统调用就是一个特殊一点的中断,所以也有保护现场与恢复现场。可以从上图中看到SAVE_ALL。
2)sys_call_table是系统调用表
3)syscall_after_all,需要先保存返回值
4)没有sys_exit_work就restore_all,返回用户态。
5)一旦进入sys_exit_work,里面会有一个进程调度时机。
(2)简化后的伪代码:
分析:
1)从ENTRY(system_call)开始,下面有SAVE_ALL,也有sys_call_table系统调用表,对应的处理函数是:sys_call_table(,%eax,4)。
2)判断当前任务是否需要处理sys_exit_work,如果不需要,就restore all,返回用户态。
3)系统调用返回之前,可能发生进程调度,进程间通信可能有信号需要处理。
3、简单浏览system_call到iret之间的主要代码
(1)SAVE_ALL:保存现场
(2)call *sys_call_table(,%eax,4):调用了系统调用处理函数,eax系统调用号,这段是实际的系统调用的服务程序。
(3)restore_all
(4)INTERRUPT_RETURN:实际上就是iret,是个宏,中断系统调用的处理过程,结束。在这个过程中会发生sys_exit_work,sys_exit_work中会有work_pending,work_pending中会有work_notifysig,用来处理信号
(5)call schedule 决定进程调度代码。
(6)跳转到restore_all,恢复现场。
四、总结
系统调用的处理过程可分成以下3步:
(1)将处理机状态由用户态转为系统态。之后,由硬件和内核程序进行系统调用的一般性处理,即首先保护被中断进程的CPU环境,将处理机状态字PSW、程序计数器PC、系统调用号、用户找指针以及通用寄存器内容等压入堆栈,然后,将用户定义的参数传送到指定的地方保存起來。
(2)分析系统调用类型,转入相应的系统调用处理子程序。为使不同的系统调用能方便地转向相应的系统调用处理子程序,在系统中配置了一张系统调用入口表。表中的每个表目都对应一条系统调用,其中包含该系统调用自带参数的数目、系统调用处理子程序的入口地址等。内核可利用系统调用号去查找该表,即可找到相应处理子程序的入口地址而转去执行它。
(3)在系统调用处理子程序执行完后,恢复被中断的或设置新进程的CPU现场,然后返冋被中断进程或新进程,继续往下执行。