try ai
科普
编辑
分享
反馈
  • 两级目录系统

两级目录系统

SciencePedia玻尔百科
核心要点
  • 两级目录系统通过在单一根目录下为每个用户提供一个私有目录来简化文件管理,通过隔离提供固有的安全性。
  • inode 引用计数等核心机制实现了高效的文件共享,而预写日志确保了原子操作和崩溃一致性。
  • 该系统的设计通过原子内核操作解决了 TOCTOU 竞争条件等安全挑战,并为策略执行提供了清晰的框架。
  • 尽管该模型简单,但它能够扩展和适应,构成了云存储分片和现代移动操作系统中应用沙盒的概念基础。

引言

在任何多用户计算环境中,从早期的微型计算机到现代的云,一个根本性的挑战始终存在:如何以简单、安全、高效的方式组织数字文件。两级目录系统作为这个问题的经典而优雅的答案应运而生,它提出的结构就像给每个用户一个私人的文件柜一样直观。虽然存在更复杂的层次化系统,但两级模型的持久生命力揭示了深邃的工程智慧,证明了简单性往往是稳健性和安全性的终极源泉。本文将深入探讨这一基础概念的持久力量。

接下来的章节将首先揭示使两级目录系统得以运作的核心原理和机制,探讨从文件命名和共享到性能、安全性和崩溃一致性之间的权衡等方方面面。然后,我们将探索其现代应用和跨学科联系,发现这些核心思想如何被用于解决资源管理、云规模架构以及我们日常使用的移动设备安全等问题。

原理与机制

简单的魅力:每个用户一个文件柜

想象一个巨大的图书馆,里面装的不是书籍,而是数字文件。你会如何为成千上万的不同用户组织它?一个优美、简单而强大的想法是给每个人一个专属的文件柜。这就是​​两级目录系统​​的精髓。在顶层,我们有一个单一的“主房间”——​​根目录​​——它本身不包含文件,而是包含所有用户的列表。列表中的每个条目都指向特定用户的“文件柜”,即他们的个人​​用户目录​​。在该用户目录内部,且仅在该目录内部,存放着该用户的所有文件。文件的路径,例如 /Ada/program.c,就变成了一个简单的两步指令:进入主房间,找到标有“Ada”的文件柜,然后在该文件柜内找到名为“program.c”的文件夹。

这种结构不仅仅是整洁;其主要优点在于其深刻的简单性。在工程世界里,简单性并非幼稚的标志,而是优雅和稳健的体现。考虑为一个资源受限的嵌入式设备(如汽车或医疗仪器中的控制器)设计文件系统。这类设备可能对代码大小(ROM)和工作内存(RAM)有严格的预算,并且需要支持少量用户,比如说最多 32 个账户。

我们是否应该构建一个复杂的、带有平衡树索引以实现闪电般快速查找的层次化系统,就像在高端服务器中看到的那样?乍听之下,这似乎更优越。B 树提供了对数级的搜索时间 O(log⁡U)O(\log U)O(logU),这总是优于线性扫描的 O(U)O(U)O(U)。但这是一个渐近思维的陷阱。当用户数量 UUU 仅为 32 时,log⁡2(32)=5\log_2(32) = 5log2​(32)=5 次操作与 32 次操作之间的差异完全可以忽略不计,尤其是与从物理存储读取信息所需的时间相比。复杂的 B 树代码会消耗宝贵的 ROM,其对多个内存缓冲区的需求会给紧张的 RAM 预算带来压力。简单的两级系统,仅通过扫描一个短列表来查找用户目录,其构建和运行成本要低得多。它的实现几乎只是一个循环,需要最少的代码和一个小内存缓冲区。在这种情况下,简单性不仅仅是一种美学选择,而是一种更优越的工程解决方案。

文件的生命周期:命名、身份与共享

当我们看到像 /Ada/program.c 这样的路径时,我们看到的是一个名称,而不是文件本身。在大多数现代文件系统中,文件的真正身份是一个称为 ​​inode​​(索引节点)的实体。可以将 inode 想象成一个文件的主记录卡。它包含了所有关键的元数据:文件所有者、文件大小、最后修改时间,以及至关重要的,其数据所在的物理存储块列表。目录仅仅是一个查找表,一个将人类可读的名称映射到 inode 编号的键值对列表。

名称与身份的分离是一个功能非凡的概念,它为共享这个基本问题提供了一个优美的解决方案。假设用户 Ada 有一个文件 research.dat,她想与用户 Charles 共享。为 Charles 创建一个副本是浪费的,并且会导致版本混乱。优雅的解决方案是让 Ada 和 Charles 都有一个指向同一个文件、同一个 inode 的名称。

这可以通过​​硬链接​​来实现。当 Charles “链接”到 Ada 的文件时,系统只是在 Charles 的目录中创建一个新条目 /Charles/shared_research.dat,它指向与 /Ada/research.dat 完全相同的 inode 编号。现在这个文件有了两个名字。但系统如何知道何时删除文件呢?如果 Ada 删除了她的版本怎么办?

答案在于一个简单而巧妙的机制:​​inode 引用计数​​。每个 inode 都会维护一个指向它的目录条目(硬链接)的数量。当 /Ada/research.dat 被创建时,其 inode 的引用计数为 1。当 Charles 创建他的硬链接时,计数增加到 2。如果 Ada 后来删除了她的文件,系统只是从她的目录中移除 /Ada/research.dat 这个名称,并将引用计数减少到 1。由于计数不为零,inode 及其数据块被保留。文件继续存在,现在只能通过 Charles 访问。只有当指向该文件的最后一个链接被删除,并且引用计数变为零时,系统才会回收 inode 并释放存储块。这是一种极其去中心化且稳健的方式来管理共享对象的生命周期。

另一种选择是​​符号链接​​,它的工作方式不同。它会创建一个新文件,其内容就是目标文件的路径名,就像桌面上的快捷方式一样。它不影响目标的引用计数。这使得符号链接很脆弱:如果 Ada 重命名或删除她的原始文件,Charles 的链接就会变成一个指向无处的“悬空指针”。而硬链接通过指向文件的真实身份(inode),则不会受此类问题的影响。

简单的代价:性能与瓶颈

打开一个文件 /Ada/program.c 的过程涉及一系列步骤:首先,系统必须访问根目录以找到 Ada 用户目录的位置。然后,它必须访问 Ada 的用户目录以找到 program.c 的 inode。最后,它必须访问 inode 本身。这些步骤中的每一步都可能需要从相对较慢的存储设备(如硬盘或闪存)中读取一个数据块。

我们可以对这个操作的成本进行建模。假设在快速内存缓存中已经找到根目录、用户目录或 inode 的概率分别为 prp_rpr​、pup_upu​ 和 pip_ipi​。一次缓存命中花费 0 次磁盘 I/O 操作,而一次未命中则花费 1 次。得益于期望的线性性质这一奇妙特性,总的期望 I/O 操作次数就是每个步骤的期望 I/O 之和,而无需考虑缓存事件之间的任何依赖关系。根目录的期望 I/O 是 1⋅(1−pr)+0⋅pr=1−pr1 \cdot (1-p_r) + 0 \cdot p_r = 1-p_r1⋅(1−pr​)+0⋅pr​=1−pr​。将这三个步骤相加,得到总的期望 I/O:

E[I/O]=(1−pr)+(1−pu)+(1−pi)=3−pr−pu−piE[\text{I/O}] = (1 - p_r) + (1 - p_u) + (1 - p_i) = 3 - p_r - p_u - p_iE[I/O]=(1−pr​)+(1−pu​)+(1−pi​)=3−pr​−pu​−pi​

这个简单的方程优美地揭示了性能完全取决于缓存。如果所有东西都在缓存中(pr=pu=pi=1p_r=p_u=p_i=1pr​=pu​=pi​=1),成本为零。在实际系统中,根目录被访问得如此频繁,以至于其缓存命中率 prp_rpr​ 通常非常接近 1,从而有效地将公式简化为 2−pu−pi2 - p_u - p_i2−pu​−pi​。

然而,性能不仅关乎速度,也关乎公平性。在两级系统中,某些资源是集中式的。当许多用户试图同时创建文件时,他们可能都需要获取一个单一的、系统范围的锁来更新全局空闲空间位图。这个锁就成了一个​​竞争​​点。系统是否给予了每个用户公平获取该锁的机会?

为了回答这个问题,我们可以求助于一个量化指标,如 ​​Jain 公平性指数​​。对于一组获得锁的用户 {x1,x2,…,xn}\{x_1, x_2, \dots, x_n\}{x1​,x2​,…,xn​},该指数计算如下:

J=(∑i=1nxi)2n∑i=1nxi2J = \frac{\left(\sum_{i=1}^{n} x_i\right)^2}{n \sum_{i=1}^{n} x_i^2}J=n∑i=1n​xi2​(∑i=1n​xi​)2​

该指数产生一个介于 1n\frac{1}{n}n1​(最坏情况,一个用户得到所有)和 111(完美公平,每个人得到相同数量)之间的值。它为我们提供了一个精确的数学工具,来评估我们的简单系统在并发使用的压力下是否表现得优雅。一个能保持高公平性指数的系统是成功地平衡了吞吐量和公平性的系统。

构筑堡垒:多用户世界中的安全与策略

两级系统的刚性结构提供了一个自然而强大的​​安全边界​​。每个用户的目录都是他们的私有领域。这种结构上的保证极大地简化了策略的执行。想象一个限制每个用户特定存储配额(比如 10 GB)的策略。在两级系统中,当一个进程试图写入文件时,系统知道它在一个特定用户的目录内。它可以检查该用户的配额一次,然后就完成了。

与之形成对比的是通用的层次化系统,其中不同用户的目录可以混合在一起。要在一个路径 /shared/projects/ada/results/final.dat 创建文件,系统可能需要在路径的每一级检查所有权并应用不同的策略。两级结构保证了在用户分支内所有权是固定的,这显著减少了所需的策略检查次数,使得系统既更快又不易出错。

但即使在这些坚固的边界内,也潜藏着微妙的危险。一个经典的漏洞是​​检查时-使用时 (Time Of Check to Time Of Use, TOCTOU)​​ 竞争条件。想象一个程序想要创建一个新的配置文件。它首先检查文件是否存在。如果不存在,它接着创建并写入文件。问题在于检查和使用之间的微小时间差。攻击者可以在那个极小的时间窗口内,创建一个同名的符号链接,指向一个关键的系统文件,比如 /etc/passwd。当良性程序执行其“创建”(使用)步骤时,它可能会在不知不觉中跟随这个恶意链接并覆盖密码文件。

对此唯一的防御是​​原子性​​。检查和使用必须成为由操作系统内核执行的单一、不可分割的操作。现代系统通过原子 create 调用提供此功能。通过使用特殊标志,程序员可以请求内核“创建此文件,但仅当它尚不存在时”。此外,另一个标志可以指示内核“在最后一步不跟随符号链接”。这些原子语义共同作用,完全关闭了 TOCTOU 窗口,将一个脆弱的两步舞变成了一个安全的单步跳跃。

处变不惊:一致性与恢复

如果在文件系统正忙时拔掉电源线会发生什么?这相当于数字世界的一场地震,一个稳健的系统必须被设计成能承受它。考虑一个简单的 rename 操作,将一个文件名从 old 改为 new。这至少涉及两个步骤:添加 new 目录条目和移除 old 目录条目。在这两个步骤之间发生崩溃可能会导致文件有两个名称,或者更糟,根本没有名称——使其永久丢失。

解决方案是一种称为​​预写日志 (Write-Ahead Logging, WAL)​​ 或日志记录的技术。在对文件系统的实际数据结构进行任何更改之前,系统首先将预定更改的描述写入一个特殊的日志,即​​日志 (journal)​​。它写入一条“添加条目‘new’”和一条“移除条目‘old’”的记录,然后是一条特殊的“提交”记录。只有在提交记录安全地写入磁盘后,系统才能自由地应用这些更改到目录本身。

这种方法的威力在恢复时显现出来。如果系统崩溃并重新启动,它会首先检查日志。如果它发现一个带有提交记录的事务,它就知道该操作是完全意图的,并可以安全地“重放”日志以确保所有更改都已完成。如果它发现一个不完整的事务(没有提交记录),它就知道该操作被中断了,于是直接丢弃它,使文件系统保持其原始、一致的状态。现在,rename 操作相对于崩溃是​​原子​​的。

这个原则可以扩展到巨大的操作。想象一下删除一个包含数千个文件的整个用户账户。简单地迭代和删除文件是非常危险的;中途崩溃将留下无数​​孤立的数据块​​——已被分配但不再可达的空间,永久地泄漏了。WAL 方法就是答案。系统开始一个单一的、巨大的事务。对于每个文件,它都会向日志写入一个“删除意图”记录,包括该文件使用的所有数据块的列表。只有在记录了所有文件的意图并写入一个单一的提交记录之后,删除才会继续。如果发生崩溃,恢复过程可以读取日志,并勤勉地完成每一个数据块的释放,确保完美的一致性。

如果灾难严重到连日志都损坏了,像 ​​FSCK​​(文件系统一致性检查)这样的工具就派上用场了。它就像一个主审计员,从根目录开始,遍历每个链接来重建文件系统的映射。在这里,两级结构的简单性再次成为一个优点。FSCK 工具知道所有有效的用户目录必须位于根目录下深度为 1 的位置。任何在不同深度或从根目录无法访问的目录都会被立即识别为孤立目录,使得验证和修复比在任意深的层次结构中简单得多。

演进的简单性:适应真实世界

我们描绘了一个简单、安全且稳健的系统。但它能扩展吗?两级模型,凭借其对用户目录的线性扫描,对于普通用户来说工作得很好。但对于那些可能存储数百万个文件的“高级用户”——比如数据科学家或图形艺术家——呢?对他们来说,线性扫描会慢得令人难以忍受。

正是在这里,这个简单的设计揭示了其最后、也是最深刻的优雅之处:它可以适应。我们不需要放弃两级结构。相反,我们可以实施一种​​混合策略​​。对于 99.9% 的拥有适量文件的用户,我们坚持使用简单高效的基于列表的目录。但当系统检测到某个用户的目录增长超过了某个阈值——即 B+ 树查找比列表扫描更划算的那个点——它可以自动且透明地将该用户的目录​​提升​​为高性能的 B+ 树索引。

这种​​自适应策略​​让我们两全其美。整个系统保留了两级模型的概念简单性和安全优势。常见情况保持快速和轻量级。复杂性只在需要的地方引入,以外科手术般的方式应用于处理异常情况。系统在不断演进,平衡着简单性与性能之间的权衡,这不是一个静态的、一次性的选择,而是一种对其所承受需求的动态响应。这才是真正成熟和优美的工程标志。

应用与跨学科联系

我们已经看到,两级目录系统是解决多用户文件组织问题的一个非常简单而优雅的方案。它为每个人在庞大的机器中提供了一个私有的数字“家园”。但是,科学和工程中一个基本思想的真正美妙之处,不仅在于其最初的简单性,还在于它解决其他问题、连接其他领域以及以惊人方式演进的能力。让我们超越基本原理,去发现这个看似普通的概唸如何成为从日常资源管理到全球云架构等方方面面的基石。

资源管理的艺术:量入为出

想象你是数字公寓楼的房东。你给每个租户一定的空间——一个存储配额。现在,你如何执行这个规定?你可能会想,只需把他们所有文件的大小加起来就行了。但文件系统比那更聪明,也更诚实。它必须为代表用户花费的每一个字节负责。

让我们问一个非常实际的问题:如果一个用户的配额是 QQQ 字节,他们实际能存储的最大文件数是多少?事实证明,答案取决于文件系统的底层架构。每个文件不仅是其数据;它还附带一些簿记信息。有文件的元数据,即它的“身份证”,我们称之为 inode。这会占用空间。还有用户目录中的条目,即列出文件名的“项目”,这也会占用空间。即使是文件的数据也不是完美高效地存储的;它被打包成固定大小的块,所以一个很小的文件可能仍然会占用一整个块。一个真正稳健的系统甚至必须考虑到正在进行中的操作,为已经开始但尚未完成的文件创建预留空间。当你把所有这些部分——数据块、inode 块、目录条目大小——放在一起时,你可以推导出一个用户可以拥有的最大文件数的精确公式。这是一个绝佳的例子,说明了高层策略(配额)如何与机器的底层机制紧密相连。

当我们构建我们现在认为理所当然的功能,比如“回收站”时,也需要同样细致的关注。这看起来很简单:当用户“删除”一个文件时,不要真正删除它,只是将它移动到一个特殊的 .recycle 文件夹中。但这个简单的想法却是一个充满设计难题的雷区。如果两个同名文件被删除怎么办?如何防止回收站中的命名冲突?更微妙的是,用户的配额呢?如果一个“已删除”的文件仍然占用空间,它必须继续计入用户的配额。但如果用户已经超出配额了呢?删除操作应该失败吗?那会非常令人困惑!一个真正优雅的解决方案利用了文件系统的基本工具。通过使用原子 rename 操作来移动文件,我们可以在一个单一的、不可分割的步骤中完成“删除”。我们可以使用文件的 inode 编号为其生成一个唯一的回收名称,这保证了唯一性。并且通过确保 rename 操作本身不需要分配新空间(也许通过为回收站目录预分配一些空间),即使该用户超出配额,删除操作也能成功,同时正确地核算文件的字节数。这就像一个完美执行的魔术,由操作系统简单而可靠的原语构建而成。

孤独的堡垒:安全与共享

两级系统的默认状态是隔离。每个用户的目录都是他们的私有领域,一座“孤独的堡垒”。这种隔离不仅是一个安全特性,也是一个性能优化的机会。

当您运行的程序需要打开您自己目录中的文件时,比如说 /home/you/report.txt,传统系统会从根目录 (/) 开始搜索。它首先找到 home,然后是 you,最后是 report.txt。但如果一个程序知道它将完全在你的目录内工作,它可以向操作系统请求一个特殊的句柄,一个直接指向 /home/you 的文件描述符。从那时起,它可以相对于该句柄打开像 report.txt 这样的文件,完全绕过了对 home 和 you 的查找。这种被称为 openat 的简单技巧,可以带来显著的、可衡量的吞吐量提升,特别是当上层目录不在系统的高速缓存中时。命名空间的逻辑结构直接促成了物理性能的提升。

当然,有时我们需要打破这种隔离来共享东西。但我们希望以可控和精确的方式进行。想象一下,你需要创建一个共享的“投递箱”,学生可以在其中提交作业,但你有一套严格的规则:学生可以存放文件,但他们不应该能看到其他人提交了什么,也不应该能删除或修改任何其他人的提交。当然,教师必须能读取所有内容。这是一个经典的安全策略问题。它可以 masterful 地通过经典 UNIX 权限工具的组合来解决。通过在投递箱目录上设置特殊标志——setgid 位以确保所有新文件都属于 instructors 组,以及 sticky bit(粘滞位)以防止非所有者删除文件——并仔细设计目录的权限以允许写入和遍历但不允许列出其内容,我们可以完美地实现这个复杂的策略。这证明了一个设计良好的访问控制模型的强大和微妙之处,让我们可以在我们的孤独堡垒上戳出精心控制的洞。

这种强隔离的另一面是,违反它的尝试本身就值得怀疑。如果一个用户的进程不断尝试访问其他用户目录中的文件并失败,这是一个强烈的信号,表明有些不对劲——也许是一个好奇的用户或一个恶意脚本在四处窥探。我们可以将文件系统的审计日志变成一个简单而有效的入侵检测系统。通过跟踪失败查找的速率 (FFF) 和更重要的,速率限制响应的速率 (RRR)——即系统在过多违规后开始限制进程——我们可以定义一个简单的异常分数,例如一个加权和 S=(F+wR)/NS = (F + wR) / NS=(F+wR)/N,其中 w>1w > 1w>1。这个分数让我们看到,两级目录的结构本身如何从正常操作的“噪声”中识别出异常行为的清晰“信号”。

超越层次结构:替代视图与数据完整性

像 /user/file 这样的层次化路径是思考命名空间的唯一方式吗?完全不是。操作系统中一些最有趣的想法来自于挑战这一假设。考虑一下​​能力 (capability)​​ 的概念。与其给进程一个起始目录并让它用路径字符串导航,不如我们给它一个不可伪造的令牌——一个能力——它直接命名并授权访问其用户目录,会怎么样?

有了这个令牌,进程可以将其呈现给内核并说:“我想对这个令牌指向的对象进行操作。” 内核看到有效的令牌后,就能确切地知道是哪个目录,并且该进程已获授权。无需从根目录解析路径;对 /user/ 的查找被完全消除了。这不仅是性能上的提升(每次文件访问都少了一次目录读取!),也是安全上的增强。进程甚至无法尝试命名一个它没有能力的客体。这个强大的概念表明,两级目录的逻辑分离可以通过不同的机制来实现,每种机制都对性能和安全有着深远的影响。

现实世界中的另一个关键挑战是数据完整性。你如何在一个用户正在活跃使用其目录时——也就是他们的整个数字生活——进行备份?如果你只是开始逐个复制文件,你可能会先复制 fileA,然后用户修改了它并创建了 fileB,然后你再复制 fileB。你的备份现在包含了 fileA 的旧版本和 fileB 的新版本,这是一个在任何单一时间点都从未存在过的状态。你的备份是不一致的。这就像试图在一个人们疯狂移动家具的房间里拍照。

解决方案惊人地优雅:​​写时复制 (Copy-on-Write, COW) 快照​​。在极短的瞬间,备份系统可以通过锁定目录来“暂停”它,快速扫描其中的所有文件,并在每个文件上放置一个特殊的“COW 标记”。然后它释放锁。整个暂停过程可能只持续一秒钟。从这时起,如果用户试图更改任何这些文件,文件系统会首先制作一份被更改数据块的副本,并将修改应用于新副本,而保留原始的、被标记的块不受影响,供备份过程从容读取。这保证了一个完美的、时间点一致的用户世界快照,而对他们工作的干扰只是一瞬间。

扩展至云端:分布式世界中的目录

到目前为止,我们谈论的都是单台计算机。但是,当你在为数亿用户构建一个运行在上千台云服务器上的系统时,会发生什么?我们简单的两级目录思想还适用吗?令人惊讶的是,是的,但它转变成了一种新的、甚至更有趣的东西。

想象一下在一个大规模分布式键值存储 (KVS) 之上实现一个文件系统。表示文件的一种自然方式是使用键值对,其中键是元组 (user_id, filename)。突然之间,“用户目录”不再是磁盘上的一个文件;它成了共享相同 user_id 的所有键的一个概念性分组。这个 user_id 成为了天然的​​分片键​​。我们可以使用 user_id 的哈希值来决定我们数千台服务器(或称“分片”)中的哪一台将存储该用户的所有数据。这种共置(co-location)对于效率来说非常棒:列出用户的所有文件只需要查询单个服务器。

但这个简单的设计立即遇到了一个经典的分布式系统问题:​​负载偏斜​​。虽然哈希函数可能会均匀地分布用户,但如果用户本身不平等,它并不能均匀地分布负载。如果你的某个用户是一个“鲸鱼用户”——一个拥有数千万文件的大公司或病毒式内容创作者——而中位数用户只有几百个文件怎么办?这个热门用户的所有流量都会冲击单个服务器,使其过载,而其他服务器则闲置。基于用户的分片这种优雅的简单性在现实世界的重尾分布面前土崩瓦解。

挑战不止于此。随着你的服务增长,需要增加更多服务器,将分片数量从比如说 100100100 改为 125125125 时会发生什么?你必须重新平衡数据。如果你使用简单的 hash(user_id)(modS)hash(\text{user\_id}) \pmod{S}hash(user_id)(modS) 映射,改变分片数量 SSS 会强制进行大规模的重新洗牌。惊人比例的用户——在这种情况下,几乎是所有用户——的数据将被移动到新的服务器,造成网络流量风暴。这就是像​​一致性哈希​​这样更复杂的技术变得至关重要的地方。通过将用户映射到一个虚拟环上,一致性哈希确保了增加新服务器只需要移动一小部分、局部的数据——在这个例子中,只有 20%20\%20%。如何将“用户目录”映射到服务器的选择,对云服务的可扩展性和运营成本具有巨大的、可量化的后果。

现代世界的回响:从用户目录到应用沙盒

最后,让我们把这次旅程带回家,带到你很可能正在用来阅读本文的设备:你的智能手机。现代移动操作系统的架构是两级目录系统的直接思想继承者。想一想:你手机上的每个应用程序都存在于其自己的私有存储区域中,它自己的“目录”里。它可以自由地读写自己的文件,但无法看到或接触任何其他应用程序的数据。这被称为应用程序​​沙盒​​。

这与定义了两级目录的默认隔离原则完全相同。然而,“主体”已经改变。在经典系统中,主体是用户。在移动世界中,主体是应用程序。伴随这种转变,安全模型也发生了关键的演变。经典系统严重依赖​​自主访问控制 (DAC)​​,即文件的所有者(用户)可以决定与谁共享。而移动系统为了更高的安全性,则建立在​​强制访问控制 (MAC)​​ 之上。在 MAC 下,有一个全局的、不可覆盖的系统策略,严格强制执行沙盒。一个应用程序不能简单地“决定”共享其数据或访问另一个应用程序的数据;全局策略禁止这样做。

我们可以将这两个系统看作一个强大的、统一的抽象概念的特例。一次访问检查当且仅当满足两个条件时才会成功:(1) 主体持有对客体的权限(DAC部分),并且 (2) 全局系统策略允许该操作(MAC部分)。在经典的两级系统中,MAC 策略在很大程度上是宽容的,将控制权留给用户的自由裁量。在移动沙盒中,MAC 策略是高度限制性的,构成了沙盒的坚固壁垒。这显示了原始概念——为每个主体提供一个私有命名空间——的持久力量,它已经适应并演进,以保护我们所拥有的最个人化和最普遍的计算机。

从微型计算机上的一个简单组织工具,到云架构和移动安全中的一个基础概念,两级目录系统展示了一个真正伟大思想的深远而持久的影响。其身份、隔离和命名空间管理的原则,已经融入了现代计算的肌理之中。