try ai
科普
编辑
分享
反馈
  • 操作系统设计的艺术与科学

操作系统设计的艺术与科学

SciencePedia玻尔百科
核心要点
  • 操作系统扮演两个基本角色:管理硬件资源和为应用程序提供安全的高级抽象。
  • 通过双模式操作和虚拟内存等机制实现的硬件强制保护,是安全多程序环境的基础。
  • 操作系统通过与内存管理单元(MMU)协作管理虚拟到物理地址的转换,从而创建出进程相互隔离的假象。
  • 高效的调度算法,如多级反馈队列,通过优先处理I/O密集型任务来平衡系统吞吐量和交互延迟。
  • 高级的操作系统安全依赖于不可绕过的内核级控制,例如强制访问控制,以防御现代威胁。

引言

操作系统(OS)可以说是任何计算机上最关键的软件,它是一个无形但不可或缺的层,将原始硬件转变为一个可用且功能强大的平台。它扮演着总指挥的角色,协调处理器、内存和设备之间复杂的交互,使我们的应用程序能够无缝、安全地运行。但是,操作系统是如何从物理硬件固有的混乱中创造出这种简洁有序的假象的呢?它又是如何安全地管理多个相互竞争的程序,而不会让它们互相干扰?本文旨在通过深入探讨操作系统设计的艺术与科学,来填补这一基础知识的空白。我们将首先探索核心的“原理与机制”,揭示构成任何现代操作系统基石的保护、抽象和资源管理等基本概念。随后,“应用与跨学科联系”一章将展示这些原理如何付诸实践,以实现从实时音频制作到强大的网络安全等一切功能,从而揭示操作系统作为数字世界伟大赋能者的角色。

原理与机制

操作系统是一位幻术大师,它施展软件魔法,将计算机硬件原始、混乱且常常难以驾驭的现实,转变为一个供其他程序栖居的稳定、有序且功能强大的世界。它介于您运行的应用程序与物理芯片、线路和磁盘之间,扮演着两个基本角色:一个是严厉而公正的​​资源管理者​​,另一个是富有创造力的​​抽象提供者​​。它就是您计算机程序社会中的政府、警察和土木工程师。

宏大的幻象:操作系统的角色

想象一下,您受命为一个仅有1KB内存(比一本小说的单页内容还少)的微型传感器设备设计“灵魂”。它的任务很简单:每十分之一秒采样一次传感器并发送数据。这样一个简单的设备真的需要操作系统吗?要回答这个问题,我们必须探究操作系统的本质是什么。

其核心功能是管理。即使在这个简单的设备中,两个逻辑活动也在争夺唯一的CPU:采样和传输。操作系统必须​​仲裁​​对该CPU的访问,确保两个任务都能按时完成。这种仲裁被称为​​调度​​。此外,原始硬件——传感器、传输链路——非常挑剔。操作系统为这些设备提供了更清晰、更稳定的​​抽象​​,这是一套简化的控制接口,应用程序逻辑可以使用它们,而无需了解硬件操作的每一个细节。这便是设备驱动程序的精髓。

在资源极其受限的环境中,我们可能不得不舍弃许多习以为常的功能。带有昂贵内存隔离的完整​​进程抽象​​可能成本过高。动态内存分配,即​​堆​​,可能会消耗掉我们宝贵的1KB预算中的太多部分。在这种情况下,采用精简的、​​事件驱动​​的设计,使用轻量级任务(协程)和静态分配的内存,通常是最明智的选择。它履行了操作系统的核心职责——管理资源和提供抽象——同时量入为出。它之所以能被称为操作系统,不是因为它功能繁多,而是因为它代表应用程序履行了管理硬件的基本职能。

门卫:特权、保护与进入

在一个只有一个简单、可信程序的世界里,管理是件容易的事。但通用计算机运行着许多来自不同来源的程序,它们并非全部可信。因此,操作系统最关键的角色是​​保护​​:防止一个程序干扰操作系统本身或其他程序。

为实现这一点,硬件提供了一个基本机制:​​双模式操作​​。处理器可以处于至少两种状态之一:一种是拥有特权的​​内核模式​​(也称监控模式),另一种是受限制的​​用户模式​​。操作系统在内核模式下运行,可以完全访问硬件。应用程序在用户模式下运行,其权力受到CPU硬件强制规则的限制。它们不能触碰不属于自己的内存,也不能执行特权指令,例如停止机器或重新配置设备。

那么,一个用户程序如何合法地请求强大的操作系统内核提供服务,比如打开文件或通过网络发送数据呢?它不能简单地调用一个内核函数;硬件禁止这样做。相反,它必须执行一条特殊的指令,作为进入内核的受控“大门”。这就是​​系统调用​​。可以把它想象成敲一扇特定的、戒备森严的门。硬件作为守卫,不会让程序在内核中随意游荡;相反,它会触发一次定义明确且安全的控制权转移,转移到操作系统预先安排好的入口点。用户代码可以触发这一转换,但无法选择目标地址。现代处理器甚至提供专门的、“快速”系统调用指令,这些指令专为此目的而优化,比老式的通用软件中断机制提供了更快的进入路径,同时严格维护了特权边界。

但是,如果程序出错,比如除以零或试图访问不属于自己的内存,会发生什么呢?硬件守卫会再次介入,触发一个​​异常​​。这是一次非自愿但仍受控的向内核的转移。处理器会查询一个受保护的特殊表——​​异常向量表​​——以找到操作系统处理该特定错误的句柄地址。一个关键的设计挑战是确保这个表始终可用,但又不受用户篡改。一个绝妙的解决方案是,操作系统将包含此表的页面映射到每个进程的虚拟地址空间中,但用一个“仅监控模式可访问”的权限位来标记它。当异常发生时,CPU已经切换到内核模式,因此被允许读取句柄地址。但如果用户代码试图读取,或者更糟地,写入该页面,硬件将触发一个保护错误,从而挫败攻击。这是硬件特性与巧妙软件设计之间优雅共舞的完美范例,也是一个安全操作系统的根基。

构建世界:进程与线程

随着保护原则的牢固确立,操作系统可以开始构建其强大的抽象。其中最基本的是​​进程​​。进程远不止是一个“正在运行的程序”。它是一个容器,一个隔离之岛。操作系统为每个进程赋予其私有的​​虚拟地址空间​​、自己的一套资源(如打开的文件)和自己的身份(凭据)。这个容器是保护和资源管理的主要单元。

在这个进程容器内,可以存在一个或多个​​线程​​。线程是实际的“执行单元”——它有自己的程序计数器(PCPCPC)、栈指针(SPSPSP)和寄存器状态。可以把进程想象成一个作坊,而线程是里面的工人。他们共享作坊的空间(地址空间)和工具(资源),但可以同时处理不同的任务。

要真正理解进程抽象,可以做一个思想实验:如果我们构建一个只有线程而没有进程的操作系统会怎样?想象一个只有工人,没有作坊的世界,所有工人都在一个巨大、共享的工厂车间里漫游。在这个“只有线程”的世界里,将存在一个单一的全局地址空间。没有进程容器作为边界,一个有故障或恶意的线程可能会覆写任何其他线程的内存,导致灾难性的、难以调试的失败。此外,我们将如何管理资源或强制执行安全?如果一个线程打开一个文件,它只属于那个线程,还是属于所有人?如果一个用户登录,系统中的所有线程现在都以该用户的身份运行吗?访问控制的主体概念就瓦解了。我们要么被迫将所有线程归入一个单一的安全主体,要么必须发明一种新的分组抽象来为一组线程持有资源和凭据——到那时,我们实际上已经重新发明了进程!。因此,进程不仅仅是一个实现细节;它是在多程序环境中隔离和身份的基石。

杂耍的艺术:调度与并发

当可能有许多进程和线程都想运行时,操作系统必须像一个杂耍演员,决定在任何特定时刻哪个可以使用CPU。这就是​​调度​​的艺术。调度器面临着两个竞争目标之间的根本性矛盾:最大化​​吞吐量​​(单位时间内完成的总工作量)和最小化​​延迟​​(交互式任务的响应时间)。

考虑一个混合工作负载,既有长时间运行、计算密集型的“批处理”作业,也有频繁等待磁盘或网络的短时、交互式的“I/O密集型”作业。一个简单的先来先服务(FCFS)调度器可能看起来公平,但它可能导致“护航效应”。如果一个长的计算密集型作业占用了CPU,一大队短的I/O密集型作业可能会被堵在它后面。交互式用户会看到极差的延迟,而整体系统吞吐量也会受损,因为当计算密集型作业独占处理器时,磁盘处于空闲状态,阻止了I/O密集型作业发出它们的下一次磁盘请求。

一个更好的策略是使用​​抢占式​​调度器。它给每个进程一个小的CPU时间片,或称​​量子​​。如果一个进程在其时间片结束时仍在运行,操作系统会强制抢占它,让另一个进程有机会运行。这确保了没有单个进程可以垄断CPU。为了平衡吞吐量和延迟,像​​多级反馈队列(MLFQ)​​这样的复杂调度器被使用。它们给予交互式作业高优先级和短时间片,使其能够快速响应。那些用完整个时间片而没有因I/O阻塞的作业被假定为计算密集型,并被移到具有更长时间片的较低优先级队列中,从而减少了它们的上下文切换开销。通过优先处理短的、I/O密集型的任务,调度器可以同时保持CPU和I/O设备的繁忙,从而改善交互响应性和整体吞吐量。

无限画布:虚拟内存的魔力

进程抽象承诺为每个程序提供其自己的私有世界,一个巨大的地址空间来工作,似乎与所有其他程序隔离开来。操作系统和硬件的​​内存管理单元(MMU)​​协作,在一个有限的物理RAM池之上创造了这种幻象。这就是​​虚拟内存​​的魔力。

每个进程都有自己的“地图”(页表),该地图将其代码使用的虚拟地址转换为RAM中的物理地址。这种映射带来了深远的影响。它允许操作系统将进程的数据放置在物理内存的任何位置,在进程之间共享内存(通过将不同的虚拟地址映射到相同的物理地址),以及保护一个进程的内存不受其他进程的侵害(通过根本不在它们的页表中包含对此内存的映射)。

也许最优雅的是,虚拟内存带来了难以置信的效率。一个完美的例子是按需栈增长。操作系统不是在进程启动时就分配一个巨大的栈——其中大部分可能都用不上——而是只分配一个页面。紧邻这个页面下方的虚拟地址空间中,它放置了一个未映射的​​保护页​​。程序运行,其栈向下增长。一旦它触及保护页中的一个地址,MMU硬件就会触发一个​​页错误​​,这是一种异常。操作系统的页错误处理程序被唤醒,看到该错误是由合法的栈增长引起的,便分配一个新的物理页面,将其映射到进程地址空间中原来保护页的位置,然后返回。因为现代CPU支持​​精确异常​​,处理器会将控制权返回给导致该错误的那条指令。这一次,内存访问成功,程序继续运行,完全没有意识到这短暂的停顿和内核的神奇干预。当然,在多线程世界中,处理程序必须经过精心设计,使用锁来保护共享的地址空间结构,以确保正确工作。

共享的危险:死锁与其他恶魔

虽然隔离是关键,但进程和线程常常需要协作和共享资源。这正是操作系统工作变得真正具有挑战性的地方。共享带来了并发错误的风险,其中最臭名昭著的是死锁和优先级反转。

​​死锁​​是终极僵局。想象一个用户空间线程 PuP_uPu​ 和一个内核线程 PkP_kPk​ 需要协调。PuP_uPu​ 持有用户空间缓冲区(RuserbufR_{userbuf}Ruserbuf​)的锁,并进行一个需要内核令牌(RsyscallR_{syscall}Rsyscall​)的系统调用。与此同时,为该调用服务的内核线程 PkP_kPk​ 持有内核令牌(RsyscallR_{syscall}Rsyscall​),但需要访问用户缓冲区中的数据,这需要锁 RuserbufR_{userbuf}Ruserbuf​。现在我们陷入了致命的拥抱:PuP_uPu​ 在等待 PkP_kPk​ 释放令牌,而 PkP_kPk​ 在等待 PuP_uPu​ 释放锁。两者都无法继续。这种循环依赖关系可以在​​资源分配图​​中被形式化地可视化,其中循环 Pu→Rsyscall→Pk→Ruserbuf→PuP_u \rightarrow R_{syscall} \rightarrow P_k \rightarrow R_{userbuf} \rightarrow P_uPu​→Rsyscall​→Pk​→Ruserbuf​→Pu​ 使死锁无可否认。解决方案是打破死锁的四个基本条件之一。例如,操作系统可以强制执行一条规则,即内核在这种情况下绝不能​​持有并等待​​;它必须在尝试获取用户的锁之前释放自己的令牌,从而打破循环。

一个更微妙的恶魔是​​优先级反转​​。这个臭名昭著的错误曾困扰过火星探路者任务。考虑三个任务:高(HHH)、中(MMM)、低(LLL)三个优先级。假设LLL获取了一个共享资源(一个互斥锁)。然后需要相同资源的HHH变为就绪状态并抢占了LLL,但被迫阻塞,等待LLL释放锁。现在,关键部分来了:与共享资源无关的任务MMM变为就绪状态。由于MMM的优先级高于LLL,它抢占了LLL。结果是,高优先级任务HHH被困住,等待低优先级任务LLL,而LLL又被中优先级任务MMM阻止运行。优先级实际上被“反转”了。解决方案与问题本身一样令人烦恼但优雅:​​优先级继承​​。当HHH在LLL持有的资源上阻塞时,操作系统临时将LLL的优先级提升到与HHH相同。现在,MMM无法再抢占LLL。任务LLL迅速完成其临界区,释放资源,其优先级恢复正常,最终HHH得以运行。这个简单的协议限制了阻塞时间,并恢复了系统的秩序。

架构哲学:一座城堡还是一个堡垒村?

鉴于所有这些原理和机制,一个操作系统应该如何构建?两种主要哲学占主导地位:宏内核和微内核。

​​宏内核​​是传统方法。整个操作系统——调度、内存管理、文件系统、设备驱动程序、网络协议栈——都是一个在特权内核模式下运行的单一、庞大的程序。它就像一座巨大的城堡。组件之间的通信速度与简单的函数调用一样快。这种设计因其性能而备受推崇。

相比之下,​​微内核​​是一种极简主义和模块化的哲学。内核本身被精简到其绝对核心:通常只有用于调度、进程间通信(IPC)和基本内存管理的机制。所有其他服务——设备驱动程序、文件系统、网络协议栈——都作为独立的用户空间进程实现,称为​​服务器​​。它就像一个由许多小型、独立堡垒组成的村庄,而不是一座大城堡。其主要优点是提高了可靠性(设备驱动程序服务器的崩溃不会导致整个系统瘫痪)和安全性(驱动程序以较少特权运行)。

然而,这种模块化是有代价的。曾经在宏内核内部的函数调用,现在变成了客户端进程、微内核和服务器进程之间较慢的、需要上下文切换的IPC。此外,还有内存开销。虽然微内核本身很小,但每个服务器进程都需要自己的地址空间、栈和其他资源。定量分析表明,微小的微内核及其数十个服务器进程的内存占用总和,很容易超过提供相同功能的单一、集成的宏内核的内存占用。这些架构之间的选择是性能与模块化之间的基本设计权衡,这一选择已被操作系统设计者争论了几十年。

应用与跨学科联系

在我们完成了对操作系统基本原理和机制的探索之旅后,人们可能会留下这样一种印象:它是一个精妙绝伦但又抽象的机器。我们已经看到它如何调度任务、管理内存以及与硬件对话。但操作系统的构建并非为了在真空中供人欣赏;它是为了与世界互动而生。当它的原理被应用时,它们才真正焕发生机,而且应用方式常常是如此无缝,以至于我们视之为理所当然。操作系统设计的真正美妙之处不仅在于其内部逻辑,还在于它所促成的复杂现实。

在本章中,我们将探讨这种动态的相互作用。我们将看到操作系统如何扮演一位魔术大师、一位不知疲倦的指挥家和一位警惕的守护者,将其核心原则应用于解决横跨广阔学科领域的现实世界问题。从制作音乐到对抗恶意软件,从与新型硬件对话到构建安全的数字社会,操作系统是那股无形的力量,为原始计算的混乱带来秩序、安全和功能。让我们开始这段世界的巡礼,在这里,我们学到的抽象原理变得具体、强大,有时甚至美丽。

幻象的艺术:打造虚拟世界

操作系统最深刻的角色之一是创造和维护幻象——强大、一致的虚构,使硬件杂乱、有限且复杂的现实变得易于管理和安全。

也许最基本的幻象是为每个程序提供一个私有的、广阔的、线性的内存空间。实际上,物理内存是共享的、混乱的帧的集合。操作系统维持着虚拟地址空间这个美丽的虚构。但当虚构破灭时会发生什么?当一个程序试图访问一块属于它自己“拥有”但实际上不在物理RAM中的内存时,就会发生页错误。这不是一个错误;而是一次召唤。操作系统的页错误处理程序是幻象的修复大师。它 meticulously 地在磁盘上找到数据,可能会换出另一个页面(如果页面被修改过,会小心地将其内容写回磁盘),将所需数据加载到一个新可用的物理帧中,并完美地更新其映射表。至关重要的是,它必须在确保所有处理器核心上的转译后备缓冲器(TLB)——硬件用于地址转换的快速存取内存——都被更新的同时完成这一切。整个精巧、高风险的编排都是为了维护一个关键的不变量:进程使用的每个虚拟地址都有真实的东西作为后盾,无论是在RAM中还是在磁盘上。正是这种对细节的执着、微观的关注,才使我们的大型多任务应用程序成为可能。

这种幻象的力量不仅限于内存。考虑一下,在你的新ARM架构处理器笔记本上运行一个为Intel x86处理器编写的应用程序。它就是能正常工作。这种魔力是由操作系统与容器运行时协同编排的。当你拉取一个多架构容器镜像时,操作系统识别出自身的特性——比如linux/arm64——并从镜像清单中选择相应的版本。不需要任何模拟。但如果你坚持要运行linux/amd64版本呢?操作系统会施展另一个戏法。它调用一个解释器,比如QEMU用户模式,来即时地将外来的x86指令翻译成本地的ARM指令。然而,真正巧妙的部分在于它在用户空间代码和内核空间服务之间保持的区别。虽然应用程序自身的计算指令被费力地模拟,带来了性能损失,但当应用程序需要做诸如读取文件之类的事情时,它会进行一次系统调用。操作系统拦截这个调用并以全速本地处理它。这就是为什么一个跨架构应用程序的计算可能需要三倍的时间,但其文件I/O时间保持不变。操作系统在提供x86环境幻象的同时,巧妙地在可能的情况下利用其底层硬件的本地能力。

终极幻象是完全隔离。在容器和云的世界里,我们在同一硬件上运行着无数互不信任的程序。操作系统如何才能在它们之间建立起不可逾越的墙壁?答案在于超越简单的权限,走向一个更深层次的架构原则:基于能力的安全。想象一个操作系统,其中每个进程都有自己私有的文件系统命名空间,甚至无法命名,更不用说访问其世界之外的任何东西。为了共享,一个进程必须明确地创建并传递一个“不可伪造的能力”——一个特殊的令牌——给另一个进程。接收进程然后可以在其选择的位置挂载共享文件。只有当能力令牌和文件自身的内在访问控制列表都允许时,访问才被授予。这种“权限交集”模型确保了权限永远不会被放大;你无法获得比明确委托的更多的权限。这就是“构造即隔离”的原则,一个强大的设计模式,使系统默认安全,而不是通过一堆防御补丁。

指挥棒:驯服时间与硬件

除了创造虚拟空间,操作系统还必须指挥时间中事件的流动,确保硬件组件的嘈杂声能和谐共鸣。在有严格截止期限的系统中,这一点尤为明显。

以一个数字音频工作站为例。一次音频丢失——一个微小的静音间隙——就可能毁掉一次完美的录音。当音频设备需要数据,而操作系统未能及时提供时,就会发生这种情况。原因是延迟,其来源多种多样:硬件中断的抖动、调度音频处理线程的延迟等等。为了解决这个问题,操作系统扮演了一位严厉的指挥家。它使用一个实时的、抢占式的调度器,无论发生什么,都会立即运行最关键的任务。它以手术般的精度分配优先级:将最终数据送入设备的内核中断处理程序,被赋予比生成音频数据的用户空间线程更高的优先级。一个比另一个更紧急。最后,它不是通过猜测来计算必要的缓冲区大小,而是通过将整个链条中的最坏情况延迟相加,并确保预渲染了足够的音频来应对这段延迟。正是这种对调度、优先级划分和缓冲的精心编排,使操作系统将一台通用计算机转变为一件高保真度的乐器。

当处理CPU与外部设备之间的直接通信时,指挥家的角色变得更加微妙。处理器和I/O设备对内存有各自的看法,没有共同的协议,它们很容易相互误解。CPU为了优化性能,可能会重排其内存写入。一个程序可能先将数据包写入内存,然后向网卡上的一个特殊“门铃”寄存器写入,告诉它“开始!”。但如果CPU重排了这些操作,门铃写入可能在数据包完全在内存中可见之前到达网卡,导致网卡发送垃圾数据。为了防止这种情况,操作系统必须提供并使用明确的内存屏障。在数据写入和门铃写入之间插入一个写内存屏障,就像一个命令:“在继续之前,确保所有先前的写入对设备都可见。”这强制建立了一种“先行发生(happens-before)”关系,创造了一条硬件和软件都必须遵守的对话规则。正是操作系统,通过这些微妙但关键的原语,充当了外交官,确保了CPU世界和设备世界之间的完美沟通。

随着硬件的演进,操作系统必须学习新的指挥方式。持久内存(PMem)——像RAM一样快但能在断电后保留内容的内存——的出现带来了新的挑战。当程序写入PMem时,数据首先落在CPU的易失性缓存中。突然断电会抹去它。为了保证持久性,数据必须从缓存刷新到内存控制器。操作系统通过“写屏障”抽象来暴露这个能力。为了以最高效率实现这一点,避免每次小写入都产生沉重的系统调用开销,操作系统可以采用一种优美的技术:vDSO(虚拟动态共享对象)。它将一小段经内核认可的代码直接映射到应用程序的地址空间。这段代码可以执行特殊的处理器指令来刷新缓存行,然后发出一个栅栏(fence)以确保刷新完成,所有这些都无需离开用户空间。这是一个极其优雅的解决方案,为应用程序提供了一个安全、快速、直接的桥梁,让它们能够说出持久硬件的新语言。

数字卫士:在对抗性世界中强制执行秩序

在一个互联的世界里,操作系统不仅仅是一个管理者,更是一个守护者。它站在前线,负责执行规则、确保公平,并在一个默认不受信任的环境中抵御威胁。

这种守护作用可以从像手机通知系统这样简单的事情中看出。是什么阻止一个有bug或恶意的应用程序用通知风暴淹没你的屏幕,耗尽你的电池,并阻止其他应用发出声音?答案是操作系统强制执行的资源控制。一个设计良好的通知服务不是一个自由市场。它是一个由操作系统内核中介的系统。通过使用诸如每个应用程序的令牌桶之类的机制,内核可以强制执行严格的速率限制。一个应用被给予一个预算——比如,短时间内可以发三条通知,平均每十秒一条。任何超出的尝试都会在内核边界被立即拒绝。通过为每个应用程序提供自己的队列,操作系统还防止了一个坏行为者造成“队头阻塞”,即其垃圾消息延迟了行为良好应用发出的合法通知的传递。这就是操作系统作为公正廉明的仲裁者的体现。

当与活跃的对手打交道时,风险更高。考虑一个木马程序:一个伪装成有用工具但暗中试图窃取你数据的程序。一个常见的防御措施是强制所有敏感操作,如网络访问,通过一个受信任的“代理”进程。但一个聪明的木马会试图找到一个后门通道——一种绕过代理的方法。它可能尝试直接创建一个原始网络套接字,或者与另一个有网络访问权限的进程通信。仅仅请求用户许可或使用用户空间库是不够的;这些可以被绕过或通过社交工程手段欺骗。唯一可靠的解决方案是使代理成为一个不可绕过的扼制点。这要求操作系统在内核级别实现强制访问控制(MAC)。使用像Linux安全模块(LSM)这样的框架,操作系统可以创建一个策略,默认规定:“'untrusted_app'域中的任何进程都不能创建任何网络或进程间通信(IPC)通道。”只有代理进程被授予此权限。这个策略在启动时加载,并且不能被用户更改。这是“完全中介”原则的一个强有力的例子,内核成为每个敏感操作的最终、防篡改的守卫。

操作系统还必须不仅在空间上,而且跨越时间和故障来保护数据。在一个加密文件系统上,我们如何确保在崩溃后数据保持一致性?加密提供了机密性,但没有提供完整性。攻击者仍然可以对磁盘上有效加密的数据块进行重排序或重放。解决方案是操作系统和密码学原理的美妙结合。操作系统使用预写日志(write-ahead log),或称日志(journal),其中更新作为一批次写入。只有在存在最终的“提交”记录时,该批次才被认为是有效的;如果在批次中途发生崩溃,整个批次都将被丢弃。为了保护这个日志,每个记录都用一个消息认证码(MAC)进行标记,该MAC与前一个记录的MAC进行加密链接。这创建了一个不可破坏的链条。任何删除、重排序或篡改记录的尝试都会使其MAC失效,这反过来又会从那一点起破坏整个链条。操作系统的原子提交协议和密码学的完整性链条的结合,创建了一个能够抵御随机故障和智能对手的系统。

守护者的工作永无止境,因为威胁的格局总在变化。随着图形处理单元(GPU)变得异常强大,它们也成为了恶意软件的新藏身之处。一个恶意程序可以将其邪恶计算卸载到GPU上。从操作系统的角度看,CPU线程可能看起来是空闲的,不消耗任何资源。与此同时,GPU正在高速运转,通过直接内存访问(DMA)扫描其有权访问的内存,并准备数据以供窃取。操作系统对此一无所知。这暴露了一个关键的缺口:操作系统的可见性和控制必须延伸到所有重要的计算资源。解决方案是让操作系统进化,将GPU视为一个“一等”实体。这意味着将GPU调度和记账整合到内核中,使用I/O内存管理单元(IOMMU)为每个GPU作业强制执行细粒度的内存权限,并赋予操作系统抢占长时间运行的GPU任务的权力。操作系统必须不断扩展其守护领域,以覆盖硬件开辟的新前沿。

结论

正如我们所见,操作系统的设计远非一项枯燥的学术活动。它是一门充满活力的、活生生的学科,坐落在抽象原理与混乱现实的交汇点。操作系统是伟大的综合者,将逻辑、硬件和策略编织在一起,创造出我们所依赖的无缝、强大且可信赖的数字体验。它创造出无限空间的幻象,驯服了时间无情的本质,并在一个充满复杂威胁的世界中担当守护者。它的美不在于任何单一的算法,而在于其基本思想的优雅统一及其深远的应用。下一次当你聆听无瑕的数字音频,在容器中运行程序,或者仅仅是相信你的机器会保护你的数据安全时,请花点时间欣赏一下由操作系统——我们计算世界中无名的英雄——在表层之下精心编排的那场无声而复杂的舞蹈。