操作系统有两个核心职责:一是对硬件进行管理和抽象;二是管理应用并且为它们提供服务。
从硬件设备的角度看 ,现代计算机系统由各种各样的物理设备组成,例如内存、GPU、网卡等。操作系统的首要职责就是对这些设备进行管理,只有这样,软件才有可能通过GPU进行图像渲染,通过网卡访问互联网。其次,操作系统还要对这些资源进行抽象,从而向应用开发者提供统一的接口,方便开发者访问和使用硬件资源。例如,开发者在创建进程时,只需要面向统一、巨大而且连续的虚拟地址空间进行编程,在32位系统上,虚拟地址空间为4GB,开发者不需要知道物理内存具体有多大、是什么型号的、物理内存地址是否连续等硬件细节。
从应用的角度看 ,所有的用户应用都受操作系统管理。当新启动一个应用进程的时候,操作系统会为它准备进程控制块、内核栈空间、进程页表等软硬件资源,然后对所有的应用进程进行统一的调度管理。当进程退出时,操作系统负责回收内存资源、销毁页表、通知与该进程有关的其他进程,从而实现对应用进程的管理。
另外,操作系统提供了各种不同功能的接口(人们称之为系统调用),以方便应用程序访问和使用各种资源。例如,应用程序可以通过write接口向屏幕上打印字符串。
由此可以推断,一个最基本的操作系统至少包含以下4个部分。
1)进程管理:负责创建进程、为进程分配资源、管理进程的调度,以及在进程退出时销毁进程关联的资源。
2)内存管理:管理物理内存的分配和回收,创建虚拟内存,向应用程序提供分配和回收内存的接口。
3)输入/输出管理:负责显示器、键盘、鼠标等输入/输出设备的管理,将信息输入到计算机系统中,经过运算和处理以后再输出给用户。磁盘数据的读入/读出也是输入/输出管理的一部分。
4)文件系统:提供文件的增删查改等功能。在Linux的设计哲学中,强调一切皆是文件,它将网络、管道都抽象成了文件,所以文件系统在Linux中扮演着极其重要的角色。
除了这些核心的功能以外,现代系统中还有网络系统、AI加速器等更多复杂的模块。随着我们对操作系统的认识逐渐加深,读者将会很容易推理出这些系统是如何与操作系统的核心模块交互的。
人们把进程管理、内存管理、输入/输出管理文件系统等核心模块统称为操作系统内核,而把网卡、显卡等设备的管理模块统称为设备驱动。在操作系统的发展历史中,内核的设计主要有两种思路,分别是宏内核(Macrokernel)和微内核(Microkernel)。
宏内核是一种将大多数操作系统服务和驱动程序集成在内核空间中的设计。进程管理、内存管理、文件系统管理、设备驱动等组件都作为内核的一部分运行,具有直接访问硬件的权限。
宏内核的系统服务紧密集成,内核中的各个模块可以直接操作硬件资源,因为减少了用户态和内核态之间的切换,处理速度快。但它同时也有一些缺陷。首先是安全性,任何内核模块的错误都可能导致整个系统崩溃,尤其是来自第三方的设备驱动程序,其安全性有可能影响到内核的整体稳定性。其次是内核体积大,针对不同平台的移植难度较高。
微内核的内核空间仅包含最基本的服务(如进程通信和地址空间管理)。文件系统、设备驱动等服务则位于用户空间,通过消息传递机制与内核通信。相比宏内核,微内核只将最基本的功能保留在内核中,其余作为独立的服务运行,而且各服务组件相互独立,运行在用户空间,通过定义良好的接口通信。内核与用户服务运行在不同的地址空间,增强了安全性。
但这种设计也带来了以下三个问题。一是,用户态与内核态频繁切换,导致消息传递延迟;二是实现复杂,消息传递机制和同步问题增加了系统复杂度;三是会导致与直接操作硬件有关的效率下降。
宏内核的典型代表是Linux,微内核的典型代表包括Minix和Windows。这两种设计并不是绝对对立的,现代操作系统设计往往采用混合内核的方式,试图结合两者的优点。