操作系统的接口
站在使用操作系统的角度会比较容易对操作系统的功能产生初步的认识。操作系统内核是一个需要提供各种服务的软件,其服务对象是应用程序,而用户(这里可以理解为一般使用计算机的人)是通过应用程序的服务间接获得操作系统的服务的,因此操作系统内核藏在一般用户看不到的地方。但应用程序需要访问操作系统获得操作系统的服务,这就需要通过操作系统的接口才能完成。操作系统的接口的形式就是上一节提到的应用程序二进制接口 (ABI, Application Binary Interface)。但操作系统不是简单的一个函数库的编程接口 (API, Application Programming Interface) ,它的接口需要考虑安全因素,使得应用软件不能直接读写操作系统内部函数的地址空间,为此,操作系统设计了一套安全可靠的接口,我们称为系统调用接口 (System Call Interface),应用程序可以通过系统调用接口请求获得操作系统的服务,但不能直接调用操作系统的函数和全局变量;操作系统提供完服务后,返回应用程序继续执行。
注解
API 与 ABI 的区别
应用程序二进制接口 ABI 是不同二进制代码片段的连接纽带。ABI 定义了二进制机器代码级别的规则,主要包括基本数据类型,通用寄存器的使用,参数的传递规则,以及堆栈的使用等等。ABI 是用来约束链接器 (Linker) 和汇编器 (Assembler) 的。基于不同高级语言编写的应用程序、库和操作系统,如果遵循同样的 ABI 定义,那么它们就能正确链接和执行。
应用程序编程接口 API 是不同源代码片段的连接纽带。API 定义了一个源码级(如 C 语言)函数的参数,参数的类型,函数的返回值等。因此 API 是用来约束编译器 (Compiler) 的:一个 API 是给编译器的一些指令,它规定了源代码可以做以及不可以做哪些事。API 与编程语言相关,如 LibC 是基于 C 语言编写的标准库,那么基于 C 的应用程序就可以通过编译器建立与 LibC 的联系,并能在运行中正确访问 LibC 中的函数。
对于实际操作系统而言,具有大量的服务接口,比如目前 Linux 有三百个系统调用接口。下面列出了一些相对比较重要的操作系统接口或抽象:
进程(即程序运行过程)管理:复制创建进程 fork 、退出进程 exit 、执行进程 exec 等。
同步互斥的并发控制:信号量 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的抽象与虚拟化。
有了这些接口,简单的应用程序就不用考虑底层硬件细节,可以在操作系统的服务支持和管理下简洁地完成其应用功能了。在现阶段,也许大家对进程、文件、地址空间等抽象概念还不了解,在接下来的章节会对这些概念有进一步的介绍。