Lec 2 命名系统
[toc]
阅读参考书§2.2,§3.1,§4.4
思考题
- 在计算机系统中,名称的作用是什么? 从高层次讲,ta能让我们做什么?
- 使用名称的好处有哪些?
- 命名方案的组成部分有哪些?
- 设计命名方案时,我们需要考虑什么问题?
计算机系统中的命名

计算机系统在其构建、配置和运行过程中以多种方式使用名称。在由子系统构建系统时,理想情况下使用子系统时,不必了解其内部的工作原理,名称用于实现模块化,同时在某些情况下,模块化还必须隐藏名称。
名称的用途。 共享体现了名称的一个基本用途:作为通信和组织工具。因为两次使用相同的名称可以引用同一个对象, 使用名称作为中介来解耦对象的概念称为间接寻址(indirection)。决定名称与对象之间的对应关系称为绑定(binding)。更改绑定是一种简单的替换对象的方法。模块本质上也是对象,因此命名是模块化的基石。
命名模型
为了理解名称如何与特定对象关联,建立一个命名模型是很有帮助的。系统设计者创建一个命名方案(naming scheme),该方案包含三个要素:
- 命名空间:有一组符号组成的字母表,并且包含一套语法规则,用于规定哪些名称合法。
- 名称映射算法(name-mapping algorithms):也称解析器(resolver)将命名空间中的某些名称与一个值的集合(universe of values)中的某些值关联起来。名称映射算法通常受额外参数“上下文(context)”的控制。某些命名方案只有一个上下文,这样的命名方案称为全局命名空间(universal name space)。全局命名空间的特点是:无论是谁使用它,同一个名称在整个命名方案中始终具有相同的含义。
- 另一种特殊的唯一性规则是唯一标识符命名空间(unique identifier name space),其中的名称永远不会被重复使用,并且一旦绑定后,该绑定关系就会永久保持。这样的名称称为“稳定绑定(stable binding)”
- 值的集合(universe of values):值可以是一个对象,也可以是另一个名称,该名称可能来自同一命名空间,也可能来自不同的命名空间
名称到值的映射被称为绑定(binding),当这种映射存在时,我们就说名称被绑定到该值。图 2.10 说明了这一点。

解析失败、反向查找与常见的名称映射算法
调用 resolve 时,可能会有以下情况:
- 找不到名称:返回一个特殊值或抛出异常来通知调用者。
- 一个名称映射多值: 返回一个值的列表。在这种情况下,
unbind可能需要额外的参数来指定要解绑的是哪个值。 - 提供反向查找(reverse lookup):即给定值,让名称映射算法返回绑定到该值的名称或名称列表
实际应用中,常见的名称映射算法包括:
- 表查找(Table lookup)
- 递归查找(Recursive lookup)
- 多重查找(Multiple lookup)
最常见的上下文实现方式是一个 {名称, 值} 对的表。在这种情况下,名称映射算法就是在该表中查找名称。虽然这个表的底层可能采用哈希表、B-树等复杂数据结构,但基本原理是相同的
路径名、命名网络和递归解析
路径名可以被看作一种名称,它显式地包含了解析时应使用的上下文引用。以下是一些路径名的示例:
- ginger.pedantic.edu.
- /usr/bin/emacs
- Macintosh HD:projects:CSE 496:problem set 1
- Chapter 2, section 2, part 3, first paragraph
- Paragraph 1 of part 3 of section 2 of chapter 2
| 最不重要的组件 | 显式上下文引用 |
|---|---|
| ginger | pedantic.edu. |
| emacs | /usr/bin |
| problem set 1 | Macintosh HD:projects:CSE 496 |
| first paragraph | Chapter 2, section 2, part 3 |
| Paragraph 1 | part 3 of section 2 of chapter 2 |
递归解析的特点是显式上下文引用本身也是一个路径名,它必须进一步解析。因此,解析过程会重复进行,直到路径名中最初最重要(即最前面)的组件变为最不重要的组件。通常,解析器会使用以下两种默认上下文引用之一:
- 根(root)上下文引用:解析器内置一个特殊的上下文引用,称为根(root),它是一个通用命名空间的示例。如果一个路径名可以通过递归解析最终到达根上下文,则它被称为绝对路径名。
- 另一个默认上下文的路径名:解析器可能会使用另一个上下文来解析路径名。但为了避免循环引用,该上下文路径名必须是一个绝对路径名。如果一个路径名的解析方式是先在另一个上下文中查找其最重要的组件,则该路径名被称为相对路径名。在 Unix 文件系统中,例如,如果当前工作目录(working directory)是
/usr/Alice,那么相对路径名plans/Monday将解析为与绝对路径名/usr/Alice/plans/Monday相同的文件。
如果一个名称解析器既可以解析相对路径名,也可以解析绝对路径名,那么通常会使用某种语法标记来区分二者。例如,在 /usr/bin/emacs 中,路径名前缀的 / 表示这是一个绝对路径。另一种方式是解析器尝试两种方案,按照某种顺序进行解析,并使用第一个成功的方案,这是一种简单的多重名称查找。
命名网络
路径名也可以被视为标识对象的一种方式,而这些对象组织在一个称为“命名网络”(naming network)的结构中。在命名网络中,上下文本身被视为对象,并且任何上下文都可以包含对其他对象(包括其他上下文)的名称绑定。
解析器需要选择一个上下文作为根(root)(通常会将该上下文的底层名称硬编码到解析器中),然后解析所有绝对路径名的方法是:从根上下文开始,依次查找路径名中的每个组件,直到找到该路径名所指向的对象。对于相对路径名,解析器会从环境变量中找到一个默认上下文,该变量包含默认上下文的绝对路径名。
由于在命名网络中可能存在多条路径指向同一个对象,因此同一个对象或上下文可能有多个不同的路径名。这些不同的路径名被称为同义名(synonyms)或 别名(aliases)。另一方面,由于根提供了一个通用命名空间,因此如果多个对象使用相同的绝对路径名,则它们一定指向相同的导出对象(exporting object)
共享路径名的挑战
共享命名网络中的名称可能会遇到一个问题:不同用户可能会使用不同的起点来表达路径名。因此,当一个用户向另一个用户传递路径名时,可能无法直接使用该路径名,而需要进行转换。
解决此问题的一个标准方法是:要求用户仅共享绝对路径名,这样所有路径名都以根(root)开头,确保路径名具有全球唯一性。
在计算机操作系统中,文件系统通常采用命名网络的结构,其中目录(directory) 充当上下文。大多数文件系统会对命名网络的结构施加一些实现上的限制,例如要求上下文组织成以根(root)为树根的命名层次结构(naming hierarchy)。然而,一个严格的命名层次结构限制过多,因此在实际系统中很少使用。即使某些系统表面上是层次结构化的,通常也会提供某种方式来添加跨层次链接(cross-hierarchy links)。最简单的链接是同义名(synonym),即一个对象可以在多个上下文中绑定。
有些系统还支持一种更复杂的链接,称为间接名称(indirect name)。间接名称是一种特殊的绑定,它将一个名称绑定到同一命名空间中的另一个名称,而不是直接绑定到对象。由于许多系统设计者都意识到间接名称的实用性,因此这种机制被赋予了许多不同的名称,例如:
- 符号链接(symbolic link)
- 软链接(soft link)
- 别名(alias)
- 快捷方式(shortcut)
在 Unix 文件系统(详见 2.5 节)中,路径名结构包括:命名层次结构、链接以及被称为软链接(soft link)的间接名称。
路径名的结构限制
由于路径名具有内部结构,因此支持路径名的命名方案通常会制定允许路径名的构造规则。例如:
- 路径名可能有最大长度限制。
- 某些符号可能仅限于结构分隔符的用途(例如
/在 Unix 系统中的作用)。
多重查找: 通过分层上下文搜索
上下文分配规则是一种粗糙的工具。例如,一个包含库程序的目录可能需要被不同用户共享,而单一的上下文分配规则无法满足所有需求。这种不灵活性导致了 第三种更复杂的名称解析方案——多重查找(multiple lookup)。
比较名称
名称比较的含义需要仔细思考,因为调用者可能关心以下三个不同的问题:
- 这两个名称是否相同?
- 这两个名称是否绑定到相同的值?
- 如果这些值实际上是存储容器(如内存单元或磁盘扇区)的标识符,那么这些存储容器的内容是否相同?
例如,在LISP 语言中,有三种比较操作符:
eq—— 比较两个名称的绑定(即它们是否指向相同的对象)。equ—— 比较两个名称的值(即它们绑定的对象是否相等)。equals—— 递归比较整个数据结构的内容是否相同。
命名的好处
使用名称有助于实现模块化,这是因为名称提供了通信和组织的机制,并且具备许多其他属性。多重查找 的核心思想是放弃单一默认上下文的概念,而是通过 系统地尝试多个不同的上下文 来解析名称。由于同一个名称可能在多个上下文中都有绑定,因此需要一种机制来决定最终采用哪个解析结果。
一个常见的方案是 搜索路径(search path),它本质上是一个上下文列表,按照一定顺序排列。名称解析器会依次尝试列表中的每个上下文:
- 先尝试第一个上下文,如果查找失败(即“未找到”),则尝试下一个上下文。
- 如果名称在多个上下文中都有绑定,则返回 列表中最靠前的 绑定结果。
搜索路径通常用于编程系统中的库管理,编译器/加载器 依次在多个已知的公共或私有库中查找需要使用的库函数,比如 sqrt,直到找到第一个匹配项

- 检索性:客户端通过名称可以检索特定的资源,而不需要了解其具体的存储或处理位置。
- 共享性:服务器可以通过名称向多个客户端共享同一资源。名称作为唯一标识符,使得服务器可以有效地管理和分发资源,支持多用户访问和共享。
- 易记性和用户友好性:使用用户友好的名称或ID(如域名kaws.com)比使用数字地址(如IP地址18.25.4.171)更容易记住和识别。
- 定位信息的指定:一些名称不仅提供了识别资源的功能,还可以包含位置信息。
- 隐藏性(hiding):服务器端的代码可以通过文件名(如guac_data.txt)来访问文件,而无需关心文件在内存中的具体布局或存储方式
- 间接性(indirection):服务器可以在不通知用户的情况下,更改guac_data.txt文件的内存布局或存储方式。使用文件名作为间接
命名方案设计
好的命名方案可以让我们实现上述的性质。
可能的名称集合
可能的值集合
一个查找算法,能够将名称转换为值(名称解析)
名称管理:管理和维护名称及其对应的资源
比如文件系统还有命名空间:定义可用名称的范围和结构
元数据与名称重载
对象的名称以及与其关联的上下文引用,都是一种称为元数据(metadata)的信息——即关于对象的有用信息,但这些信息无法在对象内部找到(或者即便存在,也可能不容易找到)。例如,图书馆的书目记录就是一组元数据,包括书名、作者、出版社、出版日期、入馆时间以及书架位置等,并且都以标准格式存储。
在计算机系统中,与对象关联的常见元数据包括:用户友好的名称、唯一标识符、对象类型(如可执行程序、文本文档、视频流等)、创建时间、上次修改时间、最近一次备份时间、备份副本的位置、对象所有者的名称、创建该对象的程序、用于验证完整性的密码学校验和(称为见证值,witness,参见在线的 Sidebar 7.1),访问权限列表(即谁可以读取或更新该对象的名单),以及对象的物理存储位置等。元数据的一个常见特点是:元数据是关于对象的信息,可以被修改,而无需修改对象本身
计算机文件系统通常都会管理一些特定的文件元数据,比如文件的物理位置、大小和访问权限。但通常不会为用户提供额外的元数据存储,唯一的用户自定义元数据往往只有文件名。正因为如此,文件名经常被过载(overloaded)为包含一些元数据。而这些元数据与名称作为引用的作用几乎没有关系。例如,某些文件名必须遵循特定的语法规则,以便嵌入特定的元数据信息。最常见的例子是文件扩展名(如 .txt、.doc、.jpg),用于表示文件的类型。下图有更多的例子

没有任何重载的名称称为纯名称(pure name)。对于纯名称,唯一适用的操作是比较(compare)、解析(resolve)、绑定(bind)和解绑(unbind)。无法通过解析操作从纯名称中提取元数据。相比之下,重载名称可以用于两种不同的方式:
- 作为标识符,执行比较、解析、绑定和解绑操作。
- 作为元数据的来源,可通过解析提取额外信息。
名称的重载可能无害,但也可能会违反模块化设计和抽象原则,导致名称脆弱性(name fragility)。名称脆弱性发生在对象的物理位置发生变化时,如果名称中嵌入了位置信息,则名称也必须更改,即使对象的标识和内容都未发生改变。
用于定位对象的名称
在计算机系统中,地址 是物理位置或映射到物理位置的虚拟位置的名称。计算机系统由真实的物理对象构成,因此包含大量的地址示例,例如寄存器编号、物理和虚拟内存地址、处理器编号、磁盘扇区编号。由于许多物理设备的访问方式是几何的,地址通常选自紧凑的整数集合,使得地址的相邻性与物理相邻性对应,同时地址上的算术运算(如加 1 或减 1)具有物理上的意义。
由于地址往往包含位置信息,因此对象移动时,其地址(即名称)也必须更改,这会导致名称脆弱性。因此,系统设计者通常遵循“通过间接层解耦模块” 这一设计原则,即隐藏地址,以避免地址的暴露。例如:
- 在编写程序时,程序员可以使用
com1这样的端口名称,而不是4D7C这样的硬件地址。这样即使端口的物理地址改变,程序仍能正常运行。
生成唯一名称
- 一种简单的生成唯一名称的方案是发放连续整数或足够精细的时间戳值。
- 随机数。然而,这种方案的问题在于,有限状态机很难生成真正的随机性。
- 一种适用于已经存在的、具有二进制表示的对象,就是哈希。例如,安全哈希算法(SHA,参见在线侧栏 11.8 的介绍)的一种版本对于任何大小的输入都会生成 160 位长的输出。如果变换函数的质量足够高,那么两个不同的文件几乎肯定会获得不同的名称。
- 这种方案存在一个问题, 如果修改了一个对象,而该对象的名称是基于其原始内容构造的,那么一个问题随之而来:是否应该更改其名称?
预期用户与用户友好性
用户友好性 vs 唯一性: 命名的矛盾,这种矛盾通常引入额外的机器可读标识符解决。
大小写处理方式影响用户体验,主要有三种模式。 一种更好的方式是区分显示与比较:
区分大小写(case-sensitive):严格区分大小写(如 Unix 文件系统)
大小写保持:允许用户指定名称的大小写,但是显示时保持不变,但在比较时忽略大小写
大小写转换(case-coercing):存储时统一大小写,但影响人机工程设计
用户友好的命名不一定是字符字符串。在图形用户界面(GUI)中,图标的形状和位置实际上也可以充当名称
名称、值和绑定关系的寿命
悬空引用
当一个名称的寿命超过其绑定关系时,仍然尝试解析该名称的用户将遇到悬空引用(dangling reference),即:该名称要么解析为未找到,要么解析为无关的值。悬空引用在有限名称空间中特别令人担忧,因为名称必须被重复使用。如果某个对象错误地使用了旧名称,它可能会导致严重的错误,甚至对现在使用该名称的对象造成破坏。
丢失对象
悬空引用的反面问题是:当对象的所有名称绑定都消失时,该对象成为孤立对象(orphaned object)或丢失对象(lost object),因为再也没有人能通过名称访问它。
丢失对象的危害:
- 这些对象可能仍然占用存储空间,但无法被访问。
- 如果系统无法有效回收这些无法访问的对象,就会导致存储泄漏(storage leak)。
- 一个频繁遗失对象的系统最终会耗尽存储资源。
案例学习: DNS
Domain name system(DNS),域名管理系统,是客户端/服务端应用的一个优秀案例,同时也是一个成功的命名方案实现。 DNS的主要用途是实现域名和IP地址的映射。
value <- dns_resolve(domain_name)典型的DNS视线中,binding并非通过BIND或者UNBIND过程实现,而是通过文本编辑器或数据库管理工具来创建和管理绑定表。这种设计的一个后果是,DNS 绑定的更改不会立即生效,通常需要几个小时才能完成更新。
域名是路径名,通过点号来分割,并且最小的层级(最具体的部分)排在最前面
在DNS中,域名既可以是相对路径,也可以是绝对路径,绝对路径通常以点结尾(然而,在人类用户的交互界面中,绝对路径的尾部点通常会被省略)。 dns_resolve 采用了一种简单的多重查找策略,当出现一个相对路径名,DNS_RESOLVE首先会尝试补充一个默认上下文(由本地配置参数指定),比如出现相对名称ginger.com, 默认上下文是"pedantic.edu",然后DNS_RESOLVE会首先尝试用绝对路径"ginger.com.pedantic.edu."来解析,如果扩展后的名称无法解析,dns_resolve 会再次尝试,直接在提供的名称末尾加上一个点,并解析为绝对路径,即"ginger.com."
工作原理
DNS 名称解析有两种,集中目录服务模型和分布式目录模型。
对于分布式目录服务模型,对所有的域名服务器都是一样的: 服务器有一组records,每个都绑定了域名到互联网地址的映射。当客户端发送一个域名解析请求时,域名服务器会在自己负责的记录中查找,成功找到则返回其记录。如果找不到,则查阅一组转发记录,每条转发记录都将 DNS 命名空间中的某个层级区域绑定到另一个可以帮助解析该区域内域名的域名服务器。服务器从请求域名的最高层级部分开始搜索匹配的转发记录,并返回该记录。如果没有找到匹配项,DNS 将返回“没有该域名”的响应。
解析流程
假设客户端机器ginger.cse.pedantic.edu 视图解析 ginger.Scolarly.edu,其解析过程如下:
- 客户端的
dns_resolve发送解析请求至一个根域名服务器(客户端需要知道根域名服务器的地址,后将解释如何获取该地址) - 根域名服务器检查请求中的域名,从最高层级(即
.edu)开始匹配。发现.edu的转发记录(referral)后,返回edu有一个域名服务器,其域名为names.edu.,IP 地址为192.14.71.191”的响应。- 例如,names.edu. 可能负责
edu.及其子域的解析。但是,这种关联并非强制要求,也就是说,域名服务器的域名和它实际管理的域名不一定有直接关系。为了避免造成视觉干扰,下图并省略了。
- 例如,names.edu. 可能负责
- 客户端
dns_resolve接收到响应后,将相同的查询请求发送到192.14.71.191。 - 该域名服务器从最高层级开始匹配,发现
Scholarly.edu的转发记录,并返回:“Scholarly.edu的域名服务器为ns.iss.edu.,IP 地址为128.32.136.9”。 - 这一过程持续进行,直到最终找到
ginger.Scholarly.edu的名称记录,并返回其 IP 地址169.229.2.16。 dns_resolve将该 IP 地址返回给调用方,使其能够与目标服务器进行通信。

持有某个域名的名称记录(name record)或引用记录(referral record)的服务器称为该域名的权威域名服务器(authoritative name server)例如,ns3.cse.pedantic.edu. 是 ginger.cse.pedantic.edu. 以及所有以 cse.pedantic.edu. 结尾的域名的权威域名服务器;由于域名服务器不会存储自身域名的名称记录,所以域名服务器无法成为自身域名的权威域名服务器
上述是DNS运行的基本模型,以下是一些优化,使其相应更快、更可靠、更具扩展性:
- 初始请求不一定要发送到根域名服务器。dns_resolve(DNS解析客户端)可以把请求发送到任何已知的域名服务器
- 一些域名服务器是支持递归的。
- 域名服务器缓存。除了自身的权威记录,名称服务器还会缓存(cache)它从其他名称服务器获取的记录,以便加速后续查询。
- 缓存记录的有效期
域名采用层次结构(hierarchy),名称服务器的组织方式也与这一层次结构相匹配,这种方式不仅分担了名称解析(name resolution)的任务,同时也分散了管理域名分配的职责。这种分布式管理是分布式目录服务(distributed directory service)模式的核心优势之一。
其他介绍
为了让 dns_resolve 发送请求到名称服务器,它需要知道该名称服务器的互联网地址。原理上说是某个根服务器的地址,但实际上,它可以是任何现有名称服务器的地址。最常见的方法是,当计算机首次连接到网络时,它会执行名称发现广播,互联网服务提供商 (ISP) 通过响应该广播,为连接者分配一个互联网地址。 另外一种方法是网络管理员手动配置。
DNS 的一个缺陷在于,尽管它声称在响应中提供权威的名称解析,但它并不使用能够验证这些响应真实性的协议。因此,入侵者可以伪装成 DNS 服务器,并向名称解析请求发送恶意或误导性的响应。这种攻击不仅可能发生,而且(不幸的是)相对容易实现。
目前,解决此问题的主要方法是,DNS 用户应将所有 DNS 响应视为可能不可靠的提示,并独立进行验证(使用第 7 章和第 11 章中的术语,我们可以称之为“执行端到端认证”),以确认与之通信的系统的身份。另一种方法是让 DNS 服务器在与客户端通信时使用身份验证协议。然而,即使 DNS 响应能够得到身份认证,它仍然可能不准确。例如,DNS 缓存可能存储了过期信息,或者 DNS 管理员可能错误地配置了名称到地址的映射。
总结
对于DNS案例用自己的话回答下面问题:
DNS的目的是什么?What
它是如何工作的? 查找如何工作?例如要检索"www.mit.edu"的IP地址,DNS客户端会做什么? How
为什么设计成这样工作? Why
谁受DNS影响? Who
在DNS中,名称是什么?其值是什么?
DNS如何组织的?
缓存和递归如何提高DNS性能
DNS的哪些特性使其扩展到互联网的规模?
递归查询的好处是什么?与非递归查询相比,它是否减少了任何开销?如果是,具体体现在哪些方面?
DNS的分层设计有哪些好处?是否存在任何缺点?