学习内容:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
一、用户态、内核态
权限分级——为了系统本身更稳定,使系统不宜崩溃。(并不是所有程序员缩写的代码都很健壮!!)
x86 CPU四种不同的执行级别:0(内核态)—3(用户态)
区分方法:CS:EIP(CPU的每条指令都是通过这里执行)(代码段选择寄存器:偏移量寄存器)
CS寄存器的最低两位表明了代码级别——逻辑地址(不是物理地址)
内核态:可以访问4G地址空间的任意地址 用户态:0x00000000—0xbfffffff空间内的地址
二、中断——从用户态进入内核态
系统调用是一种特殊的中断(用户态进程与硬件设备的接口)
切换时就会涉及到上下文的问题?
即中断int指令在堆栈上会保存一些值:1.用户态栈顶地址 2.当时状态字 3.当时CS:EIP的值
save—cs:eip ss:eip eflag
load—cs:eip ss:eip
过程:1.SAVE_ALL——保存现场
判断是否有进程调度?
(无进程调度)last.RESTORE_ALL+iret(popl cs:eip/ss:eip/efalg)
(有进程调度)last.暂时保存当前的值直到该进程再次被调度
三、API——应用程序编程接口(不同于系统调用)
- API是一个函数定义
- 系统调用请求一个软中断
- 触发系统调用通过传递系统调用号实现(压栈传递参数)
(一)使用库函数API获取系统当前时间
代码:
运行结果:
(二) 使用嵌入式汇编代码获取系统当前时间
代码如下:
运行结果:
用户态向内核态传递了一个系统调用号和传递了参数 ,系统调用返回值使用eax存储,参数用ebx存储
(三)进行C语言代码实例编写:
代码如下:
运行结果:
(四)编写嵌入式汇编代码
代码如下:
运行结果:
通过内嵌汇编代码可以清晰的看出调用系统调用的工作过程:
①将ebx寄存器清零,表示无参数传入。
②将需要调用的系统调用号赋值给eax寄存器
③执行int 0x80来执行系统调用。
④eax寄存器保存了返回值,将它分别赋值给输出uid或gid变量。
(在Linux系统中是通过激活0x80中断来触发系统调用的,需要调用的系统调用号实现赋值给eax存储器,如果有传入参数可赋值给ebx寄存器,如果多于1个则按顺序赋值给ebx、ecx、edx、esi、edi、ebp,如果超过6个则通过指针变量指向另一片堆栈区,如果无参数传入则赋值为0)