系统调用

用户程序不能直接执行特权指令,也不能直接访问受保护的内核数据和设备。当它需要操作系统服务时,应执行系统调用指令,引发同步自陷;CPU 切换到内核态并进入内核的系统调用处理程序,由内核检查参数与权限后完成相应操作,必要时执行特权指令,最后返回用户态。

类型 例子
进程控制 创建进程、终止进程、等待子进程
文件管理 创建、打开、读写、关闭、删除文件
设备管理 请求 I/O、释放设备、获取设备状态
内存管理 申请内存、映射文件、调整地址空间

调用过程

一次系统调用跨越用户态与内核态:

  1. 用户程序按照约定准备系统调用号和参数。
  2. 用户程序在用户态执行体系结构规定的系统调用入口指令,例如 x86-64 的 syscall、ARM 的 SVC、RISC-V 的 ecall,以及早期 x86/Linux 使用的 int 0x80
  3. CPU 按照该指令规定的方式完成受控转移:保存必要的返回信息、切换到内核态,并跳转到内核预先设置的系统调用入口。
  4. 系统调用入口检查系统调用号和参数,再分派到对应的内核处理程序。
  5. 内核处理程序执行所请求的服务,将结果或错误码写入约定位置。
  6. 内核执行返回操作,恢复用户程序现场和用户态,用户程序从入口指令之后继续运行。

一次系统调用中,不同环节所处的状态如下:

环节 CPU 状态 执行的操作
发起请求 用户态 准备系统调用号和参数,执行 syscall 等入口指令
进入内核 状态切换边界 CPU 保存必要现场、切换特权级,并跳转到内核规定的入口
处理请求 内核态 内核检查参数和权限,执行系统调用服务;需要时执行特权指令
返回用户程序 内核态到用户态 内核恢复现场并执行返回操作,用户程序继续运行

用户程序主动请求进入内核属于同步控制转移,它由当前执行的指令触发,与外部设备异步产生的中断不同。这种受控进入内核的过程抽象即为 trap(自陷)。CPU 状态和切换条件见 CPU-Modes,同步异常的分类见 异常

用户程序不能自行修改特权级

系统调用入口指令只负责发出受控请求。真正的状态切换、入口选择和现场保护由硬件按照预设规则完成,用户程序不能指定任意内核入口,也不能直接把 CPU 改为内核态。

system call $\neq$ syscall

`system call` 不等于 `syscall`
  • system call 是一套完整机制流程,包括用户发出请求、进入内核、执行内核服务和返回用户态,在用户态被调用,在内核态执行。
  • syscall 属于非特权指令,是用户程序进入内核的入口,即trap,调用与执行都在用户态。

后者不是前者的简写!!!二者完全不是同一个概念!!!

库函数

调用库函数不等于发起系统调用。 库函数是程序调用的普通函数接口,是否进入内核取决于其实现:

  • 数学计算、字符串处理等库函数通常完全在用户态执行。
  • 文件创建、进程创建、网络读写等库函数通常封装一个或多个系统调用。
  • 一个库函数可能在用户态完成部分工作,只在必要时进入内核。例如带缓冲的输出函数可以先把数据写入用户态缓冲区,缓冲区需要提交时才调用内核写接口。

因此,调用库函数是语言或运行库层面的函数调用;执行系统调用则意味着通过受控入口请求操作系统服务,并发生用户态到内核态的切换。