第一部分:基础知识
1:用户态、内核态和中断处理过程
一般现代CPU都有几种不同的指令级别,在高执行级别下,代码可以执行特权指令,访问任意物理地址,这种CPU执行级别就对应着内核态。
Intel x86 cpu 有四种不同的执行级别0-3,Linux只使用了其中的0级和3级,分别来表示内核态(0级)和用户态(3级)。
中断处理是从用户态进入内核态的主要方式。
系统调用只是一种特殊的中断。
2:系统调用概述
操作系统为用户态进程与硬件设备进行交互提供了一组接口——系统调用。
应用程序接口(API)和系统调用是不同的,API只是一个函数的定义,而系统调用通过软中断向内核发出一个明确的请求。API可能封装了一个或多个系统调用,也可能没有封装。一般一个系统调用对应一个封装例程,库(如libc)再用这些例程定义出给用户的API。
系统调用的三层皮:API 中断向量 中断服务程序
进程需传递一个名为“系统调用号”的参数来指明需要哪个系统调用,传递方式为将要传递的系统调用号存入eax寄存器中。若还需要其他参数,可以通过ebx,ecx,edx,esi,edi,ebp来传递。若超过6个参数,则将其中一个寄存器变为一个指针,指向一边内存区域。
第二部分:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用实验
1:实验内容
选择一个系统调用(13号系统调用time除外),系统调用列表参见http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/syscalls/syscall_32.tbl
参考视频中的方式使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
2:选择的系统调用
我选择了78号系统调用,sys_gettimeofday,它对应的API是gettimeofday,它用于Linux中的计时,使用C语言编写程序需要获得当前精确时间(1970年1月1日到现在的时间),或者为执行计时,可以使用gettimeofday()函数。
3:实验过程
首先创建gettimeofday.c文件,内容如下:
然后创建gettimeofday_asm.c文件,内容如下:
然后编译执行:
4:重要代码分析
这里重点分析下系统调用时的代码。
"mov $0,%%ecx
"压gettimeofday的第二个参数,这里使用NULL,就是0。
"mov %0,%%ebx
"压gettimeofday的第一个参数,也就是start变量的地址存入ebx。
"mov $0x4e,%%eax
"将系统调用号78,其十六进制就是0x4e,存入eax。
"int $0x80
"使用中断指令,进入内核态,执行系统调用。
5:总结
汇编代码调用系统调用时,ebx作为参数传入,然后eax是系统调用号。然后调用中断进入系统内核态。