系统调用的三层机制(上)
(一)用户态、内核态和中断
(1)Intel x86 CPU有4种不同的执行级别
分别是0、1、2、3,数字越小,特权越高。Linux操作系统中只是采用了其中的0和3两个特权级别,分别对应内核态和用户态。用户态和内核态很显著的区分方法就是CS:EIP的指向范围,在内核态时,CS:EIP的值可以是任意的地址,而在用户态时,该值是受限制的。中断处理是从用户态进入内核态的主要方式,系统调用是一种特殊的中断。从用户态切换到内核态时,中断/int指令会在堆栈上保存用户态的寄存器上下文,其中包括用户态栈顶地址、当时的状态字、当时的cs:eip的值,还有内核态的栈顶地址、内核态的状态字、中断处理程序的入口。
(2)用户态和内核态:
-
用户态:低级别指令;
-
内核态:可执行特权指令,访问任意物理地址;
从用户态到内核态的切换,一般存在以下三种情况:
-
当然就是系统调用;
-
异常事件: 当CPU正在执行运行在用户态的程序时,突然发生某些预先不可知的异常事件,这个时候就会触发从当前用户态执行的进程转向内核态执行相关的异常事件,典型的如缺页异常;
- 外围设备的中断:当外围设备完成用户的请求操作后,会像CPU发出中断信号,此时,CPU就会暂停执行下一条即将要执行的指令,转而去执行中断信号对应的处理程序,如果先前执行的指令是在用户态下,则自然就发生从用户态到内核态的转换。
(二)系统调用
(1)功能与特性
-
把用户从底层的硬件编程中解放出来,操作系统管理硬件,用户态进程不用直接与硬件设备打交道;
-
极大的提高了系统的安全性,如果用户态进程直接与硬件设备打交道,会产生安全隐患,可能引起系统崩溃;
-
使用户程序具有可移植性,用户程序与具体的硬件已经解耦合并用接口代替了,不会有紧密的关系,便于在不同的系统间移植。
(2)参数传递
系统调用也可能校传递参数,普通函数调用时参数压栈实现的,用户态和内核态使用的是不同的堆栈,所以在从用户态切换到内核态的过程中不能使用参数压栈,而是通过比较特殊的寄存器传递,寄存器传递的参数个数也有限制,其长度不能超过寄存器的长度。
(三)实验
(1)使用库函数API触发一个系统调用
(2)含两个参数的系统调用范例
-
C语言调用rename系统调用
-
汇编语言调用rename系统调用