系统调用
应用程序执行系统调用
受限直接执行(LDE, limited direct execution)是操作系统的关键底层机制之一,让用户程序受限的运行在硬件上,只是在进行危险操作时向操作系统发出请求。
现代 CPU 存在两个状态 (intel 有 4 个,不过 linux 只使用其中的两个),一个是用户态,一个是内核态。
操作系统为了保护资源,自身是运行在内核模式的,能够执行所有指令。但是其他的进程必须运行在用户模式。然后内核通过 系统调用
来将部分功能开放给用户程序。
从用户态进入到内核态需要 陷入 (trap)
指令,这个指令是不能被用户态程序随便调用的,所以操作系统提供了库函数来对系统调用进行封装。
库函数隐藏了陷入中断等等的细节,实际上系统调用的处理函数都是运行在内核态下的。
库函数基本是首先搜集参数,然后调用软中断来实现系统调用。
软中断
硬件本身会产生中断,比如 IO 完成,时钟中断,网络信息到达等。还有一类软中断,是软件自身触发的,比如 INT
指令跟上一个中断指令号,就可以进行中断操作。
在中断时,硬件会去查询中断向量表,其中注册了中断时应执行的处理函数的地址。
系统调用就是通过软中断来实现,在linux中,是使用 INT 0x80
来触发所有的系统调用( system_call
)。 可以理解为, 在使用 INT 0x80
之后,就进入了内核态。执行完对应的处理程序后,即 INT 0x80
返回后,就回到了用户态。
system_call
首先保存所有寄存器到内核栈中,然后通过查看 %eax
中的值, 来调用具体的系统调用。调用完成后执行 ret_from_sys_call()
从内核栈中恢复所有寄存器,然后回到用户态。
总结
LDE 协议分为两段。
- 在操作系统内核启动时,内核会将硬件陷阱表 (trap table) 注册到硬件中。
- 在用户程序可以通过库函数发起进行系统调用。发起后,库函数会进行参数的收集和中断(
INT 0x80
),然后进入内核态。中断处理程序会调用0x80号中断处理程序,即系统调用。结束后返回用户态,执行系统调用的下一条语句。