System Call
系统调用
用户程序不能直接执行特权指令,也不能直接访问受保护的内核数据和设备。当它需要操作系统服务时,应执行系统调用指令,引发同步自陷;CPU 切换到内核态并进入内核的系统调用处理程序,由内核检查参数与权限后完成相应操作,必要时执行特权指令,最后返回用户态。
| 类型 | 例子 |
|---|---|
| 进程控制 | 创建进程、终止进程、等待子进程 |
| 文件管理 | 创建、打开、读写、关闭、删除文件 |
| 设备管理 | 请求 I/O、释放设备、获取设备状态 |
| 内存管理 | 申请内存、映射文件、调整地址空间 |
调用过程
一次系统调用跨越用户态与内核态:
- 用户程序按照约定准备系统调用号和参数。
- 用户程序在用户态执行体系结构规定的系统调用入口指令,例如 x86-64 的
syscall、ARM 的SVC、RISC-V 的ecall,以及早期 x86/Linux 使用的int 0x80。 - CPU 按照该指令规定的方式完成受控转移:保存必要的返回信息、切换到内核态,并跳转到内核预先设置的系统调用入口。
- 系统调用入口检查系统调用号和参数,再分派到对应的内核处理程序。
- 内核处理程序执行所请求的服务,将结果或错误码写入约定位置。
- 内核执行返回操作,恢复用户程序现场和用户态,用户程序从入口指令之后继续运行。
一次系统调用中,不同环节所处的状态如下:
| 环节 | CPU 状态 | 执行的操作 |
|---|---|---|
| 发起请求 | 用户态 | 准备系统调用号和参数,执行 syscall 等入口指令 |
| 进入内核 | 状态切换边界 | CPU 保存必要现场、切换特权级,并跳转到内核规定的入口 |
| 处理请求 | 内核态 | 内核检查参数和权限,执行系统调用服务;需要时执行特权指令 |
| 返回用户程序 | 内核态到用户态 | 内核恢复现场并执行返回操作,用户程序继续运行 |
用户程序主动请求进入内核属于同步控制转移,它由当前执行的指令触发,与外部设备异步产生的中断不同。这种受控进入内核的过程抽象即为 trap(自陷)。CPU 状态和切换条件见 CPU-Modes,同步异常的分类见 异常。
用户程序不能自行修改特权级
系统调用入口指令只负责发出受控请求。真正的状态切换、入口选择和现场保护由硬件按照预设规则完成,用户程序不能指定任意内核入口,也不能直接把 CPU 改为内核态。
system call $\neq$ syscall
`system call` 不等于 `syscall`
- system call 是一套完整机制流程,包括用户发出请求、进入内核、执行内核服务和返回用户态,在用户态被调用,在内核态执行。
syscall属于非特权指令,是用户程序进入内核的入口,即trap,调用与执行都在用户态。
后者不是前者的简写!!!二者完全不是同一个概念!!!
库函数
调用库函数不等于发起系统调用。 库函数是程序调用的普通函数接口,是否进入内核取决于其实现:
- 数学计算、字符串处理等库函数通常完全在用户态执行。
- 文件创建、进程创建、网络读写等库函数通常封装一个或多个系统调用。
- 一个库函数可能在用户态完成部分工作,只在必要时进入内核。例如带缓冲的输出函数可以先把数据写入用户态缓冲区,缓冲区需要提交时才调用内核写接口。
因此,调用库函数是语言或运行库层面的函数调用;执行系统调用则意味着通过受控入口请求操作系统服务,并发生用户态到内核态的切换。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Elian's blog page!