系统调用

1.3.1 的三种受控入口中,中断和异常都是”被动进入”——程序自己没打算进内核,是被打断或出错了。

系统调用是唯一一种”主动进入”的方式,也是 1.1.2 说的用户程序请求操作系统服务的唯一途径。

机制

什么样的操作必须走系统调用

判据只有一句:凡是与资源有关的操作、以及会直接影响其他进程的操作,都必须由操作系统内核来完成。

理由是共享——资源是大家共用的,若允许应用程序自己去分配内存、自己去驱动打印机,多个程序之间必然打架。 必须有一个统一的裁判,而这个裁判就是内核。

因此这类请求只能通过系统调用提出,由内核检查合法性后代为执行。

系统调用按功能分为五类:设备管理(申请、释放、启动设备)、文件管理(读、写、创建、删除文件)、进程控制(创建、撤销、阻塞、唤醒进程)、进程通信(消息传递、共享存储)、内存管理(内存的分配与回收)。

这五类恰好覆盖了 1.1.2 的四项功能(进程控制与进程通信同属处理机管理),这不是巧合:系统调用就是四项管理功能对外暴露的接口。

系统调用的执行过程

四步:

① 传递系统调用参数 —— 告诉内核要调哪个服务、参数是什么。

② 执行陷入指令(在用户态执行)—— 陷入指令又称访管指令、trap 指令。执行它会立即引发一个内中断(陷入 trap),CPU 随即进入内核态。

③ 执行相应的内核程序处理系统调用(在内核态执行)—— 内核根据参数找到对应的处理例程,检查合法性并执行。

④ 返回应用程序 —— 恢复用户态,从陷入指令的下一条指令继续执行。

注意第 ② 步和第 ③ 步的分界正好是模式的分界:陷入指令本身在用户态执行,但对陷入指令的处理在内核态进行。 这是选择题的常见落点。

陷入指令的特殊地位

陷入指令是唯一一个只能在用户态执行、而不可在内核态执行的指令。

理由在 1.3.1 已给出:它的作用就是”从用户态进入内核态”,已经在内核态了再执行它没有意义。

同时要注意它不是特权指令——恰恰相反,它必须是非特权指令,否则用户程序连它都执行不了,就永远进不了内核。

边界

系统调用不是普通函数调用

两者在源代码里看起来一样(都是 f(x) 的形式),但发生的事情完全不同:

普通函数调用在同一个模式内完成——用户态的程序调用用户态的函数,只是压栈跳转,不涉及任何模式切换。

系统调用要执行陷入指令、引发内中断、切换到内核态、由内核代为执行、再切回用户态。它的开销比普通函数调用高出一到两个数量级。

这解释了一条常见的性能建议:频繁的小量读写(比如每次 read 一个字节)性能极差,不是因为磁盘慢,而是因为系统调用本身贵——这也正是 5.2.2 缓冲区存在的理由之一。

库函数不等于系统调用

这一对最容易混,判据是”它有没有进内核”。

有些库函数只是纯粹的用户态计算,例如 strlen()、sqrt(),它们从头到尾没有碰过内核。

有些库函数是对系统调用的封装,例如 C 语言的 fopen() / fread(),它们内部最终会执行系统调用,但在此之外还做了缓冲等额外工作。

因此准确的表述是:库函数是用户态的函数,系统调用是内核提供的服务;部分库函数封装了系统调用,但两者不是一回事。

一个实际后果:fread() 读一个字节不一定会触发一次系统调用——C 标准库自带缓冲,它可能一次性 read 了 4KB 存在用户态缓冲区里,后续 4095 次读取全都不进内核。

系统调用一定切换模式,但不一定切换进程

这一条在 1.3.1 和 2.2.4 都出现过,是全书最高频的结论之一:

系统调用必然发生”用户态 → 内核态”的转换(这是它的定义决定的)。

但它不一定引起进程切换——getpid() 这类调用进内核取个数就回来了,全程都是同一个进程。

只有当系统调用导致本进程阻塞(如读磁盘要等 I/O),或返回前发现有更该运行的进程时,才会发生进程切换。

陷入指令是”内中断”,不是”外中断”

这条容易记反。

陷入指令引发的是内中断(异常)中的”陷入”类——因为它由当前正在执行的这条指令直接引发,完全符合内中断的判据。

它不是外中断:外中断来自 CPU 外部、与当前指令无关,而陷入指令恰恰是当前指令本身。

由此还能推出返回位置:陷入属于三类内中断中的”陷入”,处理完毕后返回到下一条指令(不像缺页故障那样要重新执行原指令)。因为陷入指令是执行成功了的,它的使命就是把控制权交出去,而它做到了。

系统调用与”广义指令”是同一个东西

“广义指令”是系统调用的另一个名字,这个称呼强调的是:在应用程序员看来,系统调用就像是机器指令集的扩充——机器本来只有加减乘除跳转,加上系统调用之后,这台机器仿佛还有了”读文件""创建进程”这样的指令。

这正是 1.1.2 说的”扩充机器”的具体含义:扩充出来的那些”指令”,就是系统调用。

对照速查

系统调用四步在哪个模式
① 传递系统调用参数用户态
② 执行陷入指令用户态
③ 执行内核程序处理系统调用内核态
④ 返回应用程序回到用户态,从下一条指令继续
五类系统调用对应的管理功能
设备管理设备管理
文件管理文件管理
进程控制处理机管理
进程通信处理机管理
内存管理存储器管理
陷入指令的三条特殊性
①唯一只能在用户态执行、不可在内核态执行的指令
②不是特权指令(否则用户程序就永远进不了内核)
③引发的是内中断中的”陷入”类,返回到下一条指令
易混三对判据
系统调用 vs 普通函数调用有没有模式切换
库函数 vs 系统调用有没有进内核;部分库函数封装了系统调用
切换模式 vs 切换进程系统调用一定切模式,不一定切进程

考点

  • 凡是与资源有关、会影响其他进程的操作,都必须由内核完成
  • 五类系统调用;系统调用是四项管理功能对外暴露的接口
  • 陷入指令在用户态执行,对它的处理在内核态(高频落点)
  • 陷入指令是唯一只能在用户态执行的指令,且它不是特权指令
  • 陷入属于内中断,返回到下一条指令
  • 库函数 ≠ 系统调用;部分库函数封装了系统调用且自带缓冲
  • 系统调用一定发生模式切换,不一定发生进程切换
  • 系统调用又称广义指令,它就是”扩充机器”扩充出来的那部分

链接


考纲与教材编排的一处差异:408 考纲把”程序的链接与装入”和”程序的内存映像与地址空间” 也列在”程序运行环境”之下,但王道教材把它们放在了第 3 章。 本笔记跟随教材编排——链接与装入见 3.1.1, 进程的内存映像见 2.1.2。