软件系统中概念的抽象是组织思维加速可靠计算设计与构建的思维结构,是计算思维的基本原理。2021年图灵奖是对编程语言和编译器抽象的认可,揭示了更高层次抽象的进步,这些先进的“系统抽象”来处理大型系统。然而,硬币有两个面。抽象的好处是显而易见的,但很少有人提及抽象的原因,更不用说抽象的泄漏和缺失。或许,全面了解系统抽象,才能真正了解整个系统。从日常文件开始,到进程和操作系统,再到其他系统抽象,抛砖引玉。文件的抽象文件是最常见的数字对象,是字符串的容器,具有名称和大小。文件本身是一组存储在磁盘内存中的固定大小的记录抽象。它们是以索引表为根的树结构,用户看不到那些记录、树和索引表。“文件管理器”是管理文件的软件子系统,提供六种基本操作。CREATE创建一个新文件,DELETE删除一个文件。OPEN将所有文件组件记录的副本收集到主内存缓冲区中的线性文件映像中。CLOSE将缓冲区复制回其组件记录,并删除缓冲区。直接读取文件写入内存结构会比较慢,buffer变成了文件管理器管理的另一种对象。READ操作将打开的缓冲区的内容复制到调用者的长度为l的内存区域,从地址s开始,表示为(s,l)。WRITE操作将数据从调用者的内存区域(s,l)复制到缓冲区,替换缓冲区之前的内容。这里,typef表示文件能力,typeb缓冲区能力。CREATE命令创建一个文件,该文件可用作DELETE和OPEN命令的参数。OPEN命令创建了一个缓冲区函数,可供CLOSE、READ和WRITE命令使用。隐藏在文件管理器中的表用于将文件句柄(fh)映射到磁盘,将缓冲区句柄(bh)映射到主内存缓冲区。这当然是文件系统接口的简化视图。一个真实的文件系统可能有更多的操作,在磁盘上使用更复杂的树结构,并允许增量读取和写入缓冲区。进程抽象也许操作系统中最基本的抽象是“进程”,即正在执行的程序。发明进程的抽象是为了解决早期操作系统的可靠性问题。1960年至1965年间,操作系统设计人员着手构建功能强大的计算机系统,可以在大型用户网络中廉价地分配计算能力。这些系统旨在集成分时共享、虚拟内存、输入输出流、共享文件系统、目录系统和编程接口等一系列功能。这些功能最大限度地共享信息,最大限度地缩短开发时间,并在许多用户之间分配CPU和内存资源。当时,大型程序的主要抽象是“模块和接口”,需要将复杂的系统分解成简单的模块,通过它们的接口交换信息。例如,操作系统可以将模块用于CPU调度、内存管理、输入和输出、文件、目录和编程接口。然而,无论设计人员如何精心设计模块功能和接口,当模块连接在一起并受到用户工作量的影响时,系统总是会崩溃,调试起来非常困难。问题是,模块是一种控制结构,它指示CPU一次执行一项任务。然而,操作系统必须为许多用户管理许多计算。很难通过一个模块及其界面同时可视化许多用户的工作。一个大系统不仅仅是一个拥有更多用户的小系统。多个用户正在实现私有内存、共享文件并竞争有限的CPU、设备和内存资源。这将涉及竞争条件、死锁、繁忙等待和内存级别。数据循环、文件访问、用户通过创建新的自主服务扩展系统、预测吞吐量和响应时间。由此产生了一种新的思维方式,即并发控制。流程抽象成为一种优雅的解决方案。一个进程不仅仅是一个正在执行的程序,而是一个自治代理,它根据请求为其他进程执行服务。进程是需要CPU时间和内存空间、与其他进程同步、创建和访问文件、搜索目录、响应事件以及与其他进程组合以形成动态的所谓计算结构的实体。流程抽象导致了另一个重要的抽象——非终止计算,其中服务流程被设计为一个无限循环(whiletrue)。完成请求后,服务流回到“原点”,等待下一个传入的请求。隐藏在后台的守护进程执行有益的管理功能,例如回收内存或将修改后的内存内容写回磁盘。从持续运行的计算系统的角度设计,操作系统被视为一个合作社会,主要是不终止的进程,而不是堆积如山的模块。相比之下,现在的大部分编程应用都变成了独立启动和终止的程序,从输入开始,到输出结束。在这种情况下,非终止程序的进程看起来像一个无限循环错误。操作系统的抽象操作系统可以被描述为一个“抽象机器”,它的指令集就是在接口中提供的操作,隐藏了内部数据结构以跟踪所有对象。文件管理器就是一个例子。在操作系统或网络中,功能可以抽象为一系列层。每个级别都可以包含在较低级别定义的抽象,但不能使用来自较高级别抽象的任何信息。操作系统的第一个分层实例是由EdsgerDijkstra在1965年左右设计的,抽象为以下逻辑层次:networknamingI/ostream:file,pipe,deviceDirectories:directoryVirtualmachines:虚拟机userinterface:Shell(用户界面)Services:service1-5是一个微内核,程序运行在内核态,可以访问所有内存。6-10级是用户内核,程序运行在用户态,只能访问自己进程的私有内存。每个级别都是一个抽象机器,管理该级别的对象类。第10层是内核外部用户服务的集合,例如内核GUI、应用程序和性能分析工具。每个用户服务都有自己的系统抽象。对象和操作由较低级别的对象和操作组成。实际上,一层抽象机制是嵌套的,供上一层使用。抽象机的用户界面由所有嵌套机器的联合组成。这种嵌套隐藏了较低级别的细节并使许多人感到困惑。在这个抽象层中,程序必须设计成只调用下层,不向上调用,避免循环等待和自引用代码循环,使系统能够在一层上进行证明和测试。例如,假设文件管理器使用文件作为目录的容器,这似乎需要从文件管理器(第6级)向上调用目录管理器(第7级),请求创建目录文件管理器可以填充。为避免上行调用,我们将创建目录和用文件填充它的责任转移到shell(第9级)。shell可以调用Level7创建目录,然后调用Level6将文件加载到目录中。这种思想的重新定位简化了代码并消除了由循环引起的任何问题。分层的系统抽象非常强大,不存在因为简化和过度约束而导致的功能缺失,但这些约束确实引入了更多的复杂性。毫无疑问,分层系统会导致更小的内核。较小的内核速度更快,更易于测试和验证。今天,唯一可证明安全的操作系统是通过抽象层构建的。系统抽象系统抽象对于构建具有大量进程、用户、设备和网络连接的大型复杂系统至关重要。计算系统的每个主要领域都有自己的特征抽象。例如,互联网有寻址主机的IP协议、克服噪声传输的TCP协议、域名、url、网页、标记语言等等。云服务具有通用的无限命名空间、存储文件的不可伪造指针、数据中心、防止数据丢失的冗余等等。数据库系统有记录、字段、表、投影、连接、查询、原子事务、持久存储、文件对存储的永久承诺,等等。计算系统复杂性的一个主要来源是大量的数字对象。系统抽象以两种方式简化了这种复杂性。首先,将所有相同类型的对象组合成一个类,并为它们设计一个管理器。管理器为允许进程对这些对象执行的操作提供接口。其次,类管理器为对象分配唯一名称并验证每个访问权限。必须保护包含这些名称和访问代码的指针不被更改。在操作系统和云存储中,通过一个叫做“capability”的更底层的抽象,由type、access、handle等字段组成。Type字段表示指向的对象类型,access字段是一个多位代码。指定可以对此对象执行类操作的子集。handle字段是对象的唯一代码,它将对象与所有其他相同类型的对象区分开来。只要“功能”保留在内核空间中,它们就会受到保护,因为没有用户进程可以更改内核空间中的任何内容。当它们在外部交付时,它们会通过加密校验和进行增强,使接收者能够确认它们自创建以来没有被更改。事实上,“能力”已经成为实现面向对象编程语言的一个原则。小结系统抽象是用于设计和研究大规模复杂信息系统的工具之一。软件的惊人进步通常是通过系统抽象实现的。但对大规模程序抽象的分析性解释很少,对系统抽象的原因也缺乏介绍。如果你没有这些高深的抽象概念,不了解这些抽象的得失,可能会很遗憾。
