操作系统的系统调用接口

站在使用操作系统的角度会比较容易对操作系统的功能产生初步的认识。操作系统内核是一个需要提供各种服务的软件,其服务对象是应用程序,而用户(这里可以理解为一般使用计算机的人)是通过应用程序的服务间接获得操作系统的服务的,因此操作系统内核藏在一般用户看不到的地方。但应用程序需要访问操作系统获得操作系统的服务,这就需要通过操作系统的接口才能完成。操作系统的接口形式就是上一节提到的应用程序二进制接口 (ABI, Application Binary Interface)。

操作系统不能只提供面向单一编程语言的函数库的编程接口 (API, Application Programming Interface) ,它的接口需要考虑对基于各种编程语言的应用支持,以及访问安全等因素,使得应用软件不能像访问函数库一样的直接访问操作系统内部函数,更不能直接读写操作系统内部的地址空间。为此,操作系统设计了一套安全可靠的接口,我们称为系统调用接口 (System Call Interface)。系统调用接口通常面向应用程序提供了API的描述,但在具体实现上,还需要提供ABI的接口描述规范。

在现代处理器的安全支持(特权级隔离,内存空间隔离等)下,应用程序就不能直接以函数调用的方式访问操作系统的函数,以及直接读写操作系统的数据变量。不同类型的应用程序可以通过符合操作系统规定的ABI规范的系统调用接口,发出系统调用请求,来获得操作系统的服务。操作系统提供完服务后,返回应用程序继续执行。

注解

API 与 ABI 的区别

应用程序二进制接口 ABI 是不同二进制代码片段的连接纽带。ABI 定义了二进制机器代码级别的规则,主要包括基本数据类型、通用寄存器的使用、参数的传递规则、以及堆栈的使用等等。ABI 与处理器和内存地址等硬件架构相关,是用来约束链接器 (Linker) 和汇编器 (Assembler) 的。在同一处理器下,基于不同高级语言编写的应用程序、库和操作系统,如果遵循同样的 ABI 定义,那么它们就能正确链接和执行。

应用程序编程接口 API 是不同源代码片段的连接纽带。API 定义了一个源码级(如 C 语言)函数的参数,参数的类型,函数的返回值等。因此 API 是用来约束编译器 (Compiler) 的:一个 API 是给编译器的一些指令,它规定了源代码可以做以及不可以做哪些事。API 与编程语言相关,如 libc 是基于 C 语言编写的标准库,那么基于 C 的应用程序就可以通过编译器建立与 libc 的联系,并能在运行中正确访问 libc 中的函数。

对于实际操作系统而言,有多少操作系统,就有多少种不同类型的系统调用接口。通用服务器为支持各种应用,需要有相对多的系统调用服务接口,比如目前 Linux 有超过三百个的系统调用接口。 那么这些系统调用接口为应用程序提供了哪些功能呢?下面是一些值得了解的功能列表:

  • 一个运行的程序可以创建另一个程序的实例吗?需要等待另外一个程序执行完成吗?操作系统能停止或恢复另一个正在运行的程序吗?

  • 一个运行的程序如何与连接到计算机的设备通信并通过它们与物理世界通信?

  • 多个运行的程序如何同步互斥地对共享资源进行访问?

  • 一个运行的程序可以要求更多(或更少)的内存空间吗?

  • 一个运行的程序如何持久地存储用户数据?

  • 一个运行的程序如何能输出字符信息?如何能获得输入字符信息?

下面列出了一些相对比较重要的操作系统接口或抽象:

  • 进程(即程序运行过程)管理:复制创建进程 fork 、退出进程 exit 、执行进程 exec 等。

  • 线程管理:线程(即程序的一个执行流)的创建、执行、调度切换等。

  • 线程同步互斥的并发控制:互斥锁 mutex 、信号量 semaphore 、管程 monitor 、条件变量 condition variable 等。

  • 进程间通信:管道 pipe 、信号 signal 、事件 event 等。

  • 虚存管理:内存空间映射 mmap 、改变数据段地址空间大小 sbrk 、共享内存 shm 等。

  • 文件I/O操作:对存储设备中的文件进行读 read 、写 write 、打开 open 、关闭 close 等操作。

  • 外设I/O操作:外设包括键盘、显示器、串口、磁盘、时钟 … 但接口均采用了文件 I/O 操作的通用系统调用接口。

注解

上述表述在某种程度上说明了操作系统对计算机硬件重要组成的抽象和虚拟化,使得应用程序只需基于对简单的抽象概念的访问来到达对计算机系统资源的使用:

  • 文件 (File) 是外设的一种抽象和虚拟化。特别对于存储外设而言,文件是持久存储的抽象。

  • 地址空间 (Address Space) 是对内存的抽象和虚拟化。

  • 进程 (Process) 是对计算机资源的抽象和虚拟化。而其中最核心的部分是对CPU的抽象与虚拟化。

../_images/run-app.png

有了这些系统调用接口,简单的应用程序就不用考虑底层硬件细节,可以在操作系统的服务支持和管理下简洁地完成其应用功能了。在现阶段,也许大家对进程、文件、地址空间等抽象概念还不了解,在接下来的章节会对这些概念有进一步的介绍。值得注意的是,我们设计的最终操作系统可以只用二十几个系统调用功能接口,就可以支持应用需要的上述功能。而且这些调用与最初的 UNIX 的系统调用接口类似,几乎没有变化。尽管UNIX 的系统调用最早是在 1970 年左右设计和实现的,但这些调用中的大多数仍然在今天的系统中广泛使用。