《终极 OS 之梦》

本文是 Oleg Kiselyov 在 1995 年发布的一篇演讲,虽然已经很旧了,但里面的一些东西至今仍有很多思考价值,因此我将其翻译首发在知乎。但是我感觉在论坛里发一发也有价值,因为我觉得 Emacser 对他文章中设想的系统会更有共鸣,因为我们常开玩笑说 Emacs 自己就是一个 OS, 而且是比传统 OS 更统合,更先进的 OS.

闲话休提,走你


摘要

这是一场梦,梦由现实的碎片拼凑而成,有时会以奇异的组合方式重新排列。这个梦已经酝酿了十多年,滥觞于对许多主流现代操作系统的不满。

一个显而易见的事实是:用户在系统终端上所做的事情不过请求、读取、修改排列在表格或可滚动的列表中的文本信息。然而,用户往往需要使用完全不同而且毫无关联的命令进行完全相同的修改。如从列表中删除某项(行)

  • 删除文本文件中的一行

  • 删除文件

  • 杀死进程(即从活动进程列表中移除之)

  • 取消打印任务

此外,尽管操作系统中充斥着各种各样的表:

  • 关于文件的层次数据库

  • 电话号码表(黄页)

  • 关于二进制对象归档(程序库)的哈希表

  • 关于 IP 路由、当前进程、用户以及代码修订版本的,相对扁平的数据库

操作系统的核心服务中却明显缺乏常见的数据库功能:如用哈希键将一条记录 inserting 进入「表」中,使用简单或组合的键来 retrieving 一条记录或其字段,以及表间的 linking

当用户在某些事物中查询「foo」一词,然后删除/关闭/停止查询出来的结果。无论这些结果是一段文字、一个网络连接、一个订阅的新闻组还是一个进程,用户都只需要大致相同的鼠标点击或按键顺序,且操作系统会以同样的精神来理解和解释。本文正是试图想象这样的操作系统会是什么样的,它又将如何工作。

必须强调的是,本文写于 1995 年春。文中讨论的一些观点,如:包含所有信息的数据库(注册表)、桌面作为主页等,在后来都成了老掉牙的想法。然而,直到那年秋天,网景(Netscape)公司才公开提出了活动桌面(Active Desktop)的概念。在 1995 年,微软(Microsoft)甚至还不认为自己是一家互联网公司。此外,本文是在 BeBox(用一个自定义的数据库作为文件系统)发布之前写的。

导言

人们看待操作系统的角度通常有两种:一种是管理计算资源;另一种则是隐藏硬件特性,同时为用户提供友好的界面。后者似乎是操作系统的主要职责,毕竟中央处理器并不关心正在运行的是什么系统,也不关心它当前要处理的代码是系统级还是用户级的。然而,除 MacOS、MagicCap 和 Newton OS 外,操作系统一直向用户提供大量不同的界面、命令和操作,而这些操作的目的基本上都是填写某种列表或表格。此外,操作系统的每个主要组件——文件系统、网络服务、用户管理、终端管理等等,都在实现和管理着自己独有的简单数据库。

由此看来,数据库服务和文本/列表编辑显然是操作系统中的一种核心活动,应当在非常基础的层面上进行支持。论文中附带了一些图片,展示了如何实现这种统合,以及如何使用这种统合的具体案例,以下是预览:

MacOS 已经把 TextEdit 提升为标准的系统(工具箱)服务——这正有力地证明了:操作系统的任务不只是管理文件和进程。此外,虽然删除一段文本、一个文件、一个目录,甚至是一个文件服务器连接都可以通过相同的操作完成:选中并拖拽进入回收站。但这一理念仍有改进的余地:例如,进程列表在概念上和文件列表并无太大区别,我们可以想象 Finder 所管理(即排列、获取信息、复制和丢弃)的文件和文件不一定是普通的文件和文件夹,它们可以是进程、打开的 TCP 连接、新闻组、活动的和待处理的打印任务,以及待办任务等。

从本质上说,操作系统不过是许多数据库的管理器。事实上,无论是文件系统、进程表、路由表,还是已知 AppleShare 服务器的列表、版本控制系统(Projector)数据和 Think C 项目,这些东西都是数据库。为什么我们不用一个设计良好的分布式数据库,来统一这些数量众多的「自定义」数据库呢?

传统的数据库通常是在文件系统之上实现的。然而,文件系统本身就是一个数据库。Mac 的 HFS 和 Novell 的文件系统甚至也使用 B-树数据结构,以及一些其他「真正的」数据库使用的高级索引方案。在数据库中查询「1994 年一月的销售额」和依次点击文件夹「1994 年」,「一月」,「销售额」其实是两种密切相关的活动。为什么不能用「真正的」数据库完全取代文件系统?现代数据库具有存储图像文件、声音文件、电影文件和各种大小的文件对象的所有功能,并提供了灵活的链接和查询记录的接口,我不禁要问:「我们还需要『文件』做什么?」

在统合用户接口及其底层数据库后,我们甚至可以整合目前认为「无法关联」的部分。例如:文档不仅可以由文本块组成,还可以由文件夹和应用程序本身组成。类比在 Word 文档中存储指向图片的链接,我们也可以在文档中嵌入菜单、应用程序、远程服务器、预编译头文件,或者是 mailto: 链接、formanchor 这样的 HTML 标签。通过这种方法,我们可以把系统的桌面改造成浏览器*主页(homepage)*的类似物。

一切不过是编辑

显然,我目前所编辑的文档是由文本行的列表构成的,要删除其中一行,我需要按 PF4(假设我的编辑器是 Emacs);当系统执行「列出一个目录的内容」时也会产生某种表格或列表,而在这样的表格中删除一行却需要使用另一条命令:rm;当我们用 pstopProcessWatcher 查看当前正在运行的进程时,我们最终同样会得到一个表,而要在这样的表中「删除一行」,我们不得不又使用另一个不同的命令:kill process_id(使用该命令甚至还要将进程名称作为其参数);当我们要从打印任务队列「删除一行」,又要使用另一条命令 lprm print_job_id

删除一行文本、删除一个文件、杀死一个进程、删除掉一条路由或 ARP 条目——都不过是「删除表中的一行」。这种相似绝非巧合,无论在实现还是表示上,这种统一性都非常深刻。事实上,管理对象集合的基本方法只有几种:无非是通过某种列表或树来管理。而「向用户展示一个集合并让他们对其操作」的方法也只有那么几种。人们在终端上工作的唯一方式就是浏览和编辑——不过是移动鼠标,打字和按下 PgDn 键。这些界面上的不同,不代表这些用户或系统活动存在本质上的不同。而是由于「不同的子系统和服务是由不同的人编写,并由更多的人修改」最终造成的不同,是一种演变而来的结果。

Macintosh 绝对是这一方面的佼佼者——MacOS 中的许多类似功能都是通过完全相同的操作完成的(例如,通过「拖拽到废纸篓」删除,通过双击打开)。如果安装了 Drag&Drop Manager,这种操作则更上一层楼。而 CDE 或 proc 文件系统的开发也似乎表明 UNIX 在朝着统合用户界面的方向发展。鉴于 UNIX 的宗旨是「万物皆文件」,proc 文件系统这样的概念早该实现了,但我们还是不禁要问:「为什么进程就不一样呢?」(以及为什么实现和普及这一理念需要如此长的时间)。然而,这种统合也并不彻底:虽然用户可以打开 /proc/1024 这一文件来获取 ID 为 1024 的进程的信息(不出意外,也可以查看该进程的所有者,以及其创建时间),但用户却不能通过 rm /proc/1024 杀死这一进程,也不能通过 ls /proc/1024/open_files 来查看这个进程所打开的所有文件。为什么不可以?

上文已经提到,进程列表和文件列表在概念上并无太多区别,那么我们就可以设想:MacOS 在 Processes 文件夹中放入所有代表进程的「文件」。然后用户通过标准的 Finder 操作 ,如 View-byGetInfoTrashDuplicate 等来操作进程。USENET 新闻的层级结构也类似于文件系统(事实上,NNTP 服务器正是用文件系统的层次结构来存储和管理新闻),新闻阅读器 Nuntinus 在「按名称查看」模式下,会将新闻层次结构显示为由「文件夹」和「文件」组成的目录树。可叹的是,Nuntinus 不得不模仿 Finder 的许多功能来管理这些新闻组文件夹。此外,对于打印机、网络管理器、FTP 工具或新闻阅读器等应用程序而言,如果我们能告诉 Finder:「这里有一个『文件』列表,像你平常管理文件一样管理它,只需要在你要丢掉某些东西时告诉我就行了。」开发这些应用程序会变得更简单。

「在文件夹视图中重新排列图标」和「在文档中重新排列段落」本质上是相同的活动。如果我们将其统合为一体,就可以大大减少开销和代码重复。此外,这还能使普通文档包含文件夹、图标和应用程序等对象:它们自动成了超文档(hyper-document)

纯文本的光辉与黯淡

UNIX 系统使用大量纯文本文件指定系统配置,试举几例:/etc/hostssendmail.cfsyslog.confinetd.conf/etc/uucp/Systems。其他系统上的 INI 文件也是纯 ASCII 文件,就连 MacOS 也存在 System Folder:Hosts 这样的孤例。但请注意,虽然显示在屏幕上的符号是 ASCII 字符,但磁盘上存储的信息不必也采用 ASCII 编码。那为何 ASCII 配置文件仍比比皆是?原因也很简单:用户可以用 ex 或 edlin,甚至更好的文本编辑器修改纯文本文件。甚至可以在没有编辑器时用 cat 命令查看和创建文本文件。对于操作系统而言,读取「纯文本文件 /etc/hosts 的其中一块文本」和读取「Next 的结构化 netinfo 数据库的 /machines 子树」相比并没有什么不同。ASCII 格式脱颖而出的原因只是:即便在裸系统上也有处理文本文件的工具。如果操作系统仍把文件系统和数据库作为分割的两者对待,那么像 netinfo、NIS、CVS 这样的软件就必须提供「将其内部数据库表或记录集转换成纯文本」以及反过来转换的工具。

但其实不必如此,如果我们把数据库引擎加入系统的核心服务中,并提供浏览和修改数据库记录的便捷工具,编辑系统配置文件的这一棘手问题就迎刃而解了。MacOS 的设计非常接近这个理想:它用 ResEdit 作为通用数据库编辑器。大部分(如果不是全部)的系统配置都可以通过切换按钮开关、重新输入字符串或者调整颜色修改之。用户无需学习特定配置文件的语法,操作系统也无须把 CPU 时间浪费在解析文本文件和报告(可能的)解析错误上。不幸的是 MacOS 系统捆绑了 SimpleText,却没有捆绑 ResEdit 和配套的系统*资源(Resource)*模板。这就是为什么 System Folder:Hosts 是纯文本的原因……

「应用程序不过是一组代码与使用常见名称的配置资源的组合。」这样的想法实在绝妙。在某些应用程序(例如 LaserWriter Utility)中,用户甚至可以通过添加/删除对应的资源来添加或删除菜单项及其对应的功能,而毋须重新编译或重新链接代码。我曾痛苦地学到一条建议:不要把 PowerPC 原生的代码放进 Resource Fork(而是应该将其放入 Data Fork)。每个应用程序都应该有一个由资源管理器管理的数据库,以及一个由代码片段管理器管理的数据库。

万物皆数据库

这一点也不夸张:操作系统中到处都是集合、列表、表格以及其他东西组成的集合。从像进程控制块(Process Control Block, PCB)页表(page table)这样基础的数据结构,到 I/O 请求、打开的窗口的描述符和系统资源等都符合这一定义。其他这种集合的例子还包括:SCCS,RCS,termcap/printcap,网络数据库(DNS 服务、域名查询、/etc/hosts/etc/networks/etc/services、路由表),man 页的 whatis 数据库。人们在使用计算机系统时,几乎无时无刻都在执行各种数据库查询。每种不同的数据库都支持基本的增查删改功能,通常也会提供一些「高级」功能,如组合键或跨表链接。不过,几乎每种不同的系统设施都用不同的方法来实现这种常用功能。诚然,像管理进程控制块或虚拟页的功能确实需要量身定制和精心调整的实现。但在删除 SCCS 修订版本或 host 条目时,系统有一定的延迟也问题不大。此外,操作系统中许多「数据库」的实现都十分的直接和朴素:它们实现的是一种扁平的结构,只允许线性搜索。而用一个经过精心设计的通用数据库取代大量此类应急解决方案,有望显著提高性能(此外,这也很酷)。

有了通用数据库,操作系统或应用程序就能从许多杂事中解放出来:数据库接管了通配符资源查找、时间戳管理、权限检查等功能。这种通用性还提供了另一个明显的优势:一种链接不同记录和表格的能力。在目前的情况下,要实现这一点很麻烦。例如,对于通用数据库的管理员而言,「文件的两条记录之间的链接」和「User 表的一条记录、Processes 表中的一条记录与 Files 表和 Print jobs 表中的几条记录之间的链接」没什么不同。我们不需要多个 ID 来跟踪这些链接,甚至可以有多对多的链接。此外,性能也得到了提高:我们可以通过数据库查询更快地找到「属于用户 joe 的所有进程」,而不必使用 ps aux | grep joe 这样的命令。任何的数据库都能比像 netstat -a | grep finger 这样的傻瓜式搜索做出更好的查询选择——其实,许多脚本都不过是在做数据库查询的工作,而且做的效率也不高。Makefile 也会更容易生成和维护了。通过使用通用数据库,我们还能直接建立「被 #include 的文件」与「使用 #include 的文件」之间的链接;这样就不必在编译时为编译器传递 -I-L 参数,并试图猜测编译器究竟会从几个可能的 time.h 文件中选择哪一个。

文件系统可以利用数据库进行改进

文件系统中常见的层次结构并非实体逻辑组织的唯一形式。此外,符号链接等别名手段的广泛使用使文件系统看起来更像一个拙劣的网络数据库。因此本文提出一条建议:在具有广泛的索引功能的良好网络数据库(如 Adabas)上实现文件数据库。Adabas 数据库以其高效而闻名,具有自己的存储管理和透明多级索引,其日志功能远优于 UFS(或 HFS)。FileMaker 是另一个不错的候选项,在 FileMaker 数据库中编录 CD 或软盘内容的软件不计其数。若 FileMaker 善于表达单机文件系统的结构与内容,那么也可以将其用于联机文件系统。

文件系统的层次结构体现在目录(文件夹)的嵌套中。另一方面,目录也可以视为一种有名字的,满足一些条件的特定文件子集的「视图」。因此目录也可以视为一种数据库视图(view),即带有名字的数据库查询。一个文件可以同时出现在任意多个「目录」(视图)中。例如,一个「文件夹」可以显示所有标记为「销售报告」的文件,而另一个文件夹则包含五天内修改的文件。「搜索文件系统」与「创建和填充文件夹」也是相似的活动。由于保存的视图本身就是数据库对象,因此用户可以根据需要在视图中引用视图。不过,这并不强制要求形成层次结构:用户可以创建两个相互引用的视图,也可以根据问题需要创建最适合的任何其他视图网络。

每条数据库记录(项)都应该有一些必要属性:如时间戳、所有者、权限、类型。在此之上,用户(或应用程序)可以添加任意想要的内容。对于某些记录,其主体内容不过是缺乏细化的一堆二进制数据,而 image 类的记录(或者 image 表内的记录)则可能包含如图像宽度、高度、色彩深度、压缩方法的签名等额外属性。要列出宽度为 512 像素且为 8 位色深,使用私有色彩方案的图片,就像按日期查询文件夹中的文件一样简单。

在我们的体系中,即便是裸操作系统也必须包含一些基本的数据库浏览器,以便查看和处理这些「文件」。基本浏览器可以像 Paradox 中的一个基本的、没有任何额外功能的记录浏览器,以列或者「名称=值」对的方式显示记录中的所有字段。当然,和 Paradox 一样,操作系统应当有能力为这些记录生成更好看的视图或报告。不过最基本的浏览功能仍是必要的,而且在紧急情况下非常有用(如 catSimpleText)。

把通用数据库用作文件系统也不会让用户感到无从下手,因为几乎所有旧的计算机技能仍然适用——例如,用户可以通过路径来指定文件。事实上,文件路径只是一种用于定位文件的,由键组成的序列,其含义超越了文件系统层次结构的范畴。以网络举例,假设有一 URL 为 http://somehost/foo/bar.html 。对该 URL 最直接的解释是:位于主机 somehostDocumentRoot 目录下的 foo 目录中的文件 bar.html。然而,如果 foo 是 CGI 脚本,则 bar.html 就只是传递给该脚本的参数。脚本可以将其解释为文件名,也可解释为任意脚本所期望的内容。由此可知:URL 斜杠之间的所有内容并非都是目录名,它们只是只是用来指定相关对象的键。这种观点也适用于文件系统:人们仍可以通过「输入斜杠分隔的文件属性」来查找文件。不过,我们采用数据库的方案使得用户可以在几乎任意的地方使用通配符,也可以用文件修改日期、大小等属性作为「目录名」进行查询。因此,运行 find 命令和列出一个「目录」不过是完全一样的行为。

许多工业级 DBMS(如 Oracle、Informix)都支持透明访问远程数据库和数据复制等功能。因此可以说分布式 DBMS 包含了 NFS,它能处理鉴权、数据传输、本地缓存、数据一致性等问题。

模拟「hello world」会话

将数据库引擎加入操作系统的核心部分无疑会改变整个操作系统的架构。但用户意识到这个新操作系统是多么令人熟悉时可能会感到惊讶:无处不在的「桌面」仍然存在,只不过现在它意味着一个特定的数据库视图(view),可以包含对其他数据库对象的引用,如进程表、用户可配置的系统数据表(同时带有属性 SystemUserConfigurable 的记录)、最近使用过的对象表等等。假设我找到了一个 C 语言编译器(嗯,我希望那时能有更好的语言,比如 Dylan),它可以是在桌面上,或者在桌面的一个子视图里,也可以是通过「带有 applicationC 属性」的查询查到的记录。当我启动「C 项目管理器」后,它会像往常一样提醒用户打开或创建一个项目。我创建了一个新项目和一个新源代码文件,该文件被自动标记为和项目同名,且带有 Ctext 等属性。不过,我也不必思考程序把新创建的「文件」放哪儿去了:项目本身就是我的「文件夹」。我也可以用更多的属性来标记这些源代码,从上下文或其他相关数据库对象中可以推导出一些属性。输入新记录的过程应与使用个人信息管理系统类似——允许用户使用以前的记录或其他已有记录为模板来创建新记录,智能的个人信息管理系统还能自行插入一些字段(如时间戳),在用户输入名称和数字时进行自动补全。所有文件属性,无论是系统自带的属性还是自定义的属性,都可用于搜索。

为了写出正确的显示「hello world」的 C 代码,首先我需要 include 标准 I/O 的声明头文件。在源代码编辑器里,我可以选择一个叫做「插入数据库对象」的工具,用它查询并插入一个符合「数据,C 头文件,系统所有,在注释中包含字符串 standard io」属性的文件。或者,我只是简单地输入「printf() 的 C 函数声明」,然后依赖编辑器补全其余的查询参数。另外,我也可以先完成 main() 函数的主体,然后点击 printf() 告诉编辑器查找包含该函数的声明的头文件。无论编译器插入了什么,我都可以通过单击该对象来查看该对象的实际内容。

这种功能可能明天就会出现。此外,消除我们所熟知的「文件」概念并不意味着要打破所有曾经的习惯、抛弃所有学过的技能、删除所有已有的应用程序。仅仅意味着以更自然的方式使用计算机。

13 个赞

大家也都需要登陆才能看知乎上的内容吗?我点了该连接后,提示需要验证(登陆)才行:

1 个赞

我这也会出现这个界面,不过一下子就会跳转到登录界面了。可能是要登录了才能看。

我最近也偶尔出现类似的情况,可能是知乎的问题

是不是全局代理了

我也挺想要这种操作系统的,不过很可惜按照os这种路径依赖非常严重的基础设施,未来几乎是很难改变的。特别是LLM相关的技术进一步提升,通过自然语言去控制机器就更是受到厂家的重视,传统的操作方式就更没有动力去改变了。

所以对于这类系统的未来,我是持悲观态度的。以后可能注定是小众的极客玩具了,plan9算是很接近文中的那类了,裸机上我也只能通过plan9的变种9front玩玩过过瘾,要不就是用应用软件层级的emacs或者pharo玩玩。

看起来这篇文章的目的是让人们少用文件文件系统多用数据库,这也是我一直想做的事,把org文件都放进数据库,像使用web应用一样使用org-mode,而不在使用文件系统管理这些文件。

而把二进制文件如图片也放进数据库,就感觉没有这个必要了,因为文本文件放进去可以更方便的检索和组织其中的内容,而不依赖文件的名字路径,文件名与内容是否相关影响不大。

而图片视频这类,如果文件名路径是乱的,然后你想写个程序自动检索其中的内容对其进行分类组织。这个估计要造个ai工具出来才能实现

1 个赞

坛友可以了解下 BaseX

以及对应的 XQuery 技术,事实上,同为文档数据库,JSON 文档数据库似乎比 XML 文档数据库流行得多,但 XML 还是有一定自己的好处(比如 XML 是原生类如富文本之类的结构,用 JSON 的话要用数组很麻烦地模拟)

而且 BaseX 的好处是提供了一个 GUI 界面管理和查询数据库,双击 jar 文件就能启动使用(不像 MongoDB 需要很复杂的部署过程)

1 个赞

这个我也想过. 不过我感觉自然的, 符合直觉的UI非常难以设计, 而文本界面恰好是个久经考验的, 不完美但也足够好的人机交互方式.

UI主要是每个人的风格都不同, 所以有些软件会提供主题切换或自定义主题的功能, 用文本界面主要还是为了简单, 就几个或十几个按钮, 只要排列要有规律,看起来不拥挤,不会勿点击(但也要容易点到), 就不会很难用。

还有一个好处就是方便为键盘操作优化,不依赖鼠标

Haiku就有类似的文件系统。 想想Haiku还真适合用来做NAS系统,只是不支持Docker,这个有点麻烦

在我后来翻译的另一篇文章 《Oberon:被埋没的珠宝》 里,Oberon 系统就是大量使用各种文本和超文本做界面的,节选一段如下

但是,Oberon 原始用户界面[9]的真正力量来自于:普遍使用了 文本作为统一的抽象概念 。无论是 Viewer 标题栏中显示的文件名、菜单栏中的命令、「列出目录」命令的输出,还是编辑器窗口的内容,系统提供的几乎所有操作都针对这些抽象文本的,而非针对被动的字符数组或数据文件。例如,如果有人愿意,可以用 Oberon 编译器编译 Viewer 的标题栏上的文本,也可以编译相关的菜单(虽然这么做都没什么意义)。与 UNIX 系统中传统的 管道(pipe) 解决方案相比,Oberon 的「将文本作为基本抽象」的方法是一项重大改进,不仅功能强大,而且直观简单。

我个人认为文章里面谈到的 Oberon 系统的优点其实和现在的 Emacs 很多地方一脉相承。

1 个赞

最近在探索 Calibredb.el 方案,Calibre 本身可以添加大量不同类型的文件,而 Calibredb.el 支持设置不同的虚拟库,理论上,可以把文档类型以外的图片,音频,视频等文件都添加到 Calibre 不同类别的库里。

而有了 Calibredb.el 这个方便的前端:

  1. 可以批量添加文件
  2. 可以快速的针对文件的 metadata 进行编辑,比如为文件添加标签等等,很方便
1 个赞