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现场,然后返冋被中断进程或新进程,继续往下执行。

 

原文地址:https://www.cnblogs.com/20135235my/p/5322737.html