第 23 章...
Unix 文件系统 (The Unix Filesystem)
- 什么是文件?
- 文件的类型
- 目录与子目录
- 特殊文件
- 用于硬件的特殊文件
- 用于终端的特殊文件: tty
- 用于伪设备的特殊文件
- 命名管道: mkfifo
- Proc 文件
- 树形文件系统;文件系统层次标准
- 根目录;子目录
- 挂载文件系统: mount, umount
- 根目录巡礼
- /usr 目录巡礼
- 为什么存放程序的目录不止一个?
- 主目录
- 虚拟文件系统
- 练习
在下面的三章里,我们将讨论 Unix 文件系统,也就是 操作系统中为你和你的程序服务的那一部分:它负责存储 并组织你系统上的所有数据。本章先讲基本概念, 随后在第 24 章讨论目录的具体用法, 在第 25 章讨论文件的具体用法。
本章的目标是回答三个关键问题。第一,什么是 Unix 文件?不难想象,文件可以是存放在磁盘上的数据仓库。 但你会看到,它的含义远不止于此。第二个问题关乎组织 方式。一台 Unix 系统上常常有几十万个文件,如何才能把 这么多条目组织得井井有条、易于理解?最后,一个统一 的系统怎么可能透明地支持许多不同类型的数据存储设备?
我们的讨论从一个简单的问题开始,它的答案却复杂得 令人吃惊:什么是文件?
什么是文件?(What Is a File?)
在还没有计算机的旧时代,“文件”一词指的是一摞纸张。 通常,文件放在硬纸板文件夹里,再加以整理,存进档案 柜。如今,大多数数据都已计算机化,文件的定义也随之 改变。从最简单的意义上说,文件就是被起了名字的一堆 数据(*)。多数情况下,文件存放在数字媒体上:硬盘、 CD、DVD、软盘、U 盘、存储卡等等。
* 脚注
一个 Unix 文件实际上可以有不止一个名字。我们将在 第 25 章讨论链接时讲到这一点。
在 Unix 内部,文件的定义要宽泛得多。文件(FILE)是 任何有名字、可以从中读取数据的来源;也是任何有名字、 可以往里写入数据的去向。因此,当你使用 Unix 或 Linux 时,“文件”不仅指磁盘文件这类数据仓库,还指任何物理 设备。例如,键盘可以作为文件来访问(一个输入来源), 显示器同样如此(一个输出去向)。还有一些文件毫无实体 形态,却能接受输入或产生输出,以提供特定的服务。
以如此通用的方式定义文件,其重要性怎么强调都不为过: 这意味着 Unix 程序可以用简单的过程从任何输入来源读取 数据,并向任何输出去向写入数据。例如,大多数 Unix 程序被设计成从标准输入读取、向标准输出写入(见第 18 章和第 19 章)。从程序员的角度看,I/O(输入/输出)很 容易实现,因为无论数据实际来自何处、去往何处,读写 数据都能以简单而标准的方式完成。从用户的角度看,则 拥有极大的灵活性,因为他可以在运行程序的当下指定输入 来源和输出去向。
可以想见,Unix 文件系统——或任何文件系统——的内部 细节是复杂的。本章我们只讲基本概念,等你读完,就会 发现 Unix 文件系统是一种令人着迷的精美之物:这种美 只存在于一切都说得通的复杂系统之中。
文件的类型(Types of Files)
Unix 文件有很多种类型,但都归入三大类:普通文件、 目录和伪文件。在伪文件之中,最常见的又有三种:特殊 文件、命名管道和 Proc 文件。本节我们先快速浏览一番 这些最重要的文件类型,后面的各节再分别详细讨论每一 种文件。
普通文件(ORDINARY FILE)或规则文件(REGULAR FILE) 就是大多数人听到“文件”一词时所想到的东西。普通文件 存有数据,驻留在某类存储设备上,例如硬盘、CD、DVD、 U 盘、存储卡或软盘。因此,普通文件是你平时打交道最 多的文件类型。例如,当你用文本编辑器写一个 Shell 脚本时,这个 Shell 脚本和编辑器程序本身都存放在普通 文件里。
如我们在第 19 章所讨论的,粗略地说,普通文件分为两 类:文本文件和二进制文件。文本文件包含一行行可打印 字符组成的数据(字母、数字、标点符号、空格、制表 符),每行末尾有一个换行字符。文本文件用来存放文本 数据:纯数据、Shell 脚本、源程序、配置文件、HTML 文件等等。
二进制文件包含非文本数据,这类数据只有被执行或被某个 程序解释时才有意义。常见的例子有可执行程序、目标文 件、图像、音乐文件、视频文件、文字处理文档、电子表 格、数据库等等。例如,文本编辑器程序本身是一个二进制 文件,而你所编辑的那个文件是文本文件。
由于你遇到的文件几乎都是普通文件,掌握基本的文件操作 技能至关重要。具体来说,你必须学会如何创建、复制、 移动、重命名和删除这类文件。我们将在第 25 章详细 讨论这些主题。
第二种文件类型是目录(DIRECTORY)。与普通文件一样, 目录也驻留在某类存储设备上。然而目录不存放常规数据, 它们用于组织并访问其他文件。从概念上说,目录“包含” 其他文件。例如,你可能有一个名为 vacation 的 目录,里面存放着你即将前往 Syldavia 旅行所需的全部 文件。
目录还可以包含其他目录。这让你能够把文件组织成一个 层次化的系统。你在本章后面会看到,整个 Unix 文件系统 就是按一棵巨大的层次树组织的,目录套着目录,目录里再 套目录。在这棵树中属于你的那部分里,你可以随心所欲地 创建和删除目录。这样,你就能按自己的意愿组织文件,并 随需求的变化而调整。
(你还记得吗,在第 9 章讨论 Info 系统时,我们谈到过 树。严格地说,树(TREE)是一种数据结构,由节点、叶和 分支组成的集合,其组织方式保证任意两个节点之间至多有 一条分支。)
你有时会看到用“文件夹(FOLDER)”来代替“目录”一词, 尤其在图形用户界面工具中。这种说法来自 Windows 和 Macintosh 世界,因为这两个系统都用文件夹来组织文件。 Windows 的文件夹很像 Unix 的目录,但没有那么强大。 Macintosh 的文件夹则就是 Unix 的目录,因为 Mac 的操作系统 OS X 是运行在 Unix 之上的(见 第 2 章)。
最后一种文件类型是伪文件(PSEUDO FILE)。与普通文件 和目录不同,伪文件不用于存放数据。因此,伪文件本身 不占任何空间,尽管它们被视为文件系统的一部分,并且 同样被组织在目录之中。伪文件的用途是提供一种服务, 而访问这种服务的方式与访问常规文件的方式相同。多数 情况下,伪文件用来访问由内核——操作系统的核心部分 ——提供的服务(见第 2 章)。
最重要的一类伪文件是特殊文件(SPECIAL FILE),有时 称为设备文件(DEVICE FILE)。特殊文件是物理设备的 内部表示。例如,你的键盘、显示器、打印机、磁盘驱动 器,实际上你计算机里的或网络上的每一台设备——都可以 作为特殊文件来访问。
下一种伪文件是命名管道(NAMED PIPE)。命名管道是我们 在第 15 章讨论过的管道机制的延伸,因此它能把一个程序 的输出接到另一个程序的输入。
最后,Proc 文件让你可以访问内核内部的信息。在少数 特定情形下,你甚至能用 Proc 文件修改内核里的数据。 (显然,这只有非常内行的人才该做。)最初创建这些文件 是为了提供进程运行时信息,因此得名“proc”。
名称的由来
File(文件)
当你看到或听到“file”这个词时,必须依上下文判断它的 含义。有时它指任何类型的文件;有时它只指包含数据的 文件,也就是普通文件。
例如,假设你读到这句话:“ls 程序会列出目录中 所有文件的名字。”此处“file”指任何类型的文件:普通 文件、目录、特殊文件、命名管道或虚拟文件。这五种文件 都可能出现在目录里,因而都能被 ls 程序列出。
然而,假如你读到的是:“要用 cmp 程序把一个文件 与另一个文件比较。”此处“file”指的是普通文件,因为只有 这种文件包含可比较的数据。
目录与子目录(Directories and Subdirectories)
我们用目录把文件组织成一个层次化的树状系统。为此, 我们把文件收集成一个个组,每个组存放在自己的目录里。 由于目录本身也是文件,一个目录就能包含其他目录,层次 由此形成。
举个例子。你是西海岸一所声望很高的大学的学生,本学期 选了三门课——历史、文学和冲浪——每门课都写了不少 论文。为了把这一切作业组织好,你建了一个叫 essays 的目录(暂时别管具体怎么做)。在这个 目录里,你又建了三个目录:history、 literature 和 surfing,用来存放论文。 每篇论文都存在一个文本文件里,文件名字起得很贴切。 Figure 23-1 展示了这整个结构的样子。注意,这张图 看上去像一棵倒过来的树。
|
||
|
Figure 23-1. 用目录组织文件的例子 为了组织文件,我们使用父目录和子目录, 构成一棵层次树。在这个例子中,父目录 essays 包含三个子目录——history、 literature 和 surfing——每个子目录 里都含有普通文件。细节见正文。 |
||
父目录(PARENT DIRECTORY)是包含其他目录的目录。子 目录(SUBDIRECTORY)或子级目录(CHILD DIRECTORY)是位于 另一个目录之内的目录。在 Figure 23-1 中, essays 是父目录,它包含三个子目录: history、literature 和 surfing。
人们常常像目录真的装着其他文件那样谈论它们。例如,我们 可能说 essays 里有三个子目录,literature 里有四个普通文件。你甚至会想,如果能往 literature 目录里面瞧一瞧,就会真的看到那四个文件。实际上,所有 文件都是作为彼此独立的实体存放的。目录并不持有文件本身, 它只包含 Unix 用来定位文件所需的信息。
不过,你无需操心这些细节,因为 Unix 会自动维护整个 文件系统的内部运作。你要做的只是学会使用相应的程序, Unix 就会替你完成一切:创建目录、删除目录、移动目录、 列出目录内容等等。我们将在第 24 章介绍这些程序。
特殊文件(Special Files)
特殊文件是表示物理设备的伪文件。Unix 把所有特殊文件 存放在 /dev(device,设备)目录里。(名字开头 的那个斜杠,我们稍后在本章再讲。)要显示你系统上特殊 文件的名字,可以像下面这样使用 ls 程序 (第 24 章):
ls /dev | less
你会看到许多名字,其中绝大多数你用不上。这是因为特殊 文件大多由系统程序使用,而不是由用户使用。不过,仍有 几个特殊文件值得一知。我把这些文件列在 Figure 23-2 中,并将分三组来讨论:硬件、终端和 伪设备。
Figure 23-2: 最值得关注的特殊文件
特殊文件是用来表示设备的伪文件,主要由系统程序 使用。虽然你很少会直接使用特殊文件,但有几个了解 一下颇有好处。细节见正文。
| 硬件 | |
|---|---|
| /dev/fd0 | 软盘 |
| /dev/hda | IDE 硬盘 |
| /dev/hda1 | IDE 硬盘:第 1 个分区 |
| /dev/sda | SCSI/SATA 硬盘 |
| /dev/sda1 | SCSI/SATA 硬盘:第 1 个分区 |
| /dev/sda1 | USB 闪存卡(见正文) |
| /dev/lp0 | 打印机 |
| /dev/usb/lp0 | USB 打印机 |
| 终端 | |
|---|---|
| /dev/tty | 当前终端 |
| /dev/tty1 | 控制台 / 虚拟控制台 |
| /dev/pts/0 | 伪终端 |
| /dev/ttyp0 | 伪终端 |
| 伪设备 | |
|---|---|
| /dev/null | 用来丢弃输出;读取时不返回任何内容(eof) |
| /dev/zero | 用来丢弃输出;读取时返回空字符(若干个 0) |
| /dev/random | 随机数生成器 |
| /dev/urandom | 随机数生成器 |
如果你想弄明白其他特殊文件的名字,有一份官方的总清单 可以帮你。要找到这份清单,请在网上搜索 “LANANA Linux Device List”。(LANANA 是 “Linux Assigned Names and Numbers Authority” 的缩写。)顾名思义,这份清单专门 针对 Linux。不过,大多数重要的特殊文件在所有 Unix 系统上名字都相似,所以即使你不用 Linux,这份清单也 很有用。
用于硬件的特殊文件(Special Files for Hardware)
连接到计算机的所有设备都可以通过特殊文件访问。我们先 从最直接的设备说起,也就是表示实际硬件的那些设备。如 你在 Figure 23-2 中所见,文件 /dev/fd0 表示一个软盘驱动器。设备名末尾的数字指某个具体的设备。 这里 /dev/fd0 指第一个软盘驱动器。(计算机程序 员常常从 0 开始计数。)如果有第二个软驱,它就是 /dev/fd1,依此类推。同样,/dev/lp0 对应 第一台打印机。
硬盘的处理方式稍有不同。第一块 IDE 硬盘记作 /dev/hda,第二块是 /dev/hdb,依此类推。 硬盘被划分成一个或多个分区(PARTITION),各个分区作用 如同彼此独立的设备。第一块硬盘的第一个分区记作 /dev/hda1;如果有第二个分区,就是 /dev/hda2。SCSI 和 SATA 硬盘另有自己的命名方 式。第一块 SCSI 或 SATA 盘是 /dev/sda,第二块 是 /dev/sdb,依此类推。同样,分区要编号,所以 第一块 SCSI 或 SATA 盘上的第一个分区就是 /dev/sda1。
SCSI 这套命名有时也用于其他类型的设备。一个常见的例子 是 USB 闪存,它被当作可移动的 SCSI 磁盘来对待。因此, 闪存对应的特殊文件会叫作 /dev/sda1 或类似的 名字。
用于终端的特殊文件:tty(Special Files for Terminals: tty)
在 Figure 23-2 中你会注意到,表示终端的特殊文件 有好几种。原因如下。从前,终端是连接到主机计算机上的 独立物理设备(见第 3 章)。这样的终端由名为 /dev/tty1、/dev/tty2 等的特殊文件来表示。 (如我在第 7 章所解释的,TTY 这个缩写是 terminal(终端) 的同义词,因为最早的 Unix 终端是 Teletype 电传打印机, 人们把它们称作 TTY。)
对于那些表现得像硬件设备的终端,如今仍然沿用 /dev/tty 这套命名规则。具体来说,当你以单用户 模式运行 Unix 时就是如此。此时你的键盘和显示器(控制 台)就像一个内置的文本终端,表示这个终端的特殊文件是 /dev/tty1。同样,当你在桌面环境里使用虚拟控制 台时(见第 6 章),它的作用也如同真正的终端。Linux 默认支持六个这样的控制台,分别由 /dev/tty1 到 /dev/tty6 这些特殊文件表示。
然而,当你用图形用户界面在窗口里运行终端仿真程序时, 情况就完全不同了。由于并不存在实际的终端,Unix 会创建 所谓伪终端(PSEUDO TERMINAL)或 PTY 来模拟一个终端。 当你在图形用户界面里打开一个终端窗口(第 6 章),以及 当你连接到远程 Unix 主机(第 3 章)时,用的就是 PTY。 这两种情况下,PTY 都充当你的终端。
恰好有两套不同的机制用于创建伪终端,所以你会看到两类 名字。如果你所用的 Unix 采用第一套机制,表示伪终端的 特殊文件会叫 /dev/ttyp0、/dev/ttyp1 这 样;如果用另一套机制,名字就是 /dev/pts/1、 /dev/pts/2 等等。这两类名字在 Figure 23-2 中都能看到。
任何时候,你都可以用 tty 程序显示你终端的名字。 例如,假设你正在 3 号虚拟终端上工作,你输入:
tty
输出为:
/dev/tty3
为了使用方便,特殊文件 /dev/tty 表示你当前正在 使用的那个终端。例如,如果你正在使用 3 号虚拟控制台, 那么 /dev/tty 与 /dev/tty3 是等价的。
下面举个例子,说明你怎么用上特殊文件。你在第 25 章会 看到,用 cp 程序可以复制一个文件。第 11 章我们 谈过口令文件 /etc/passwd。假设你想把口令文件复 制一份,副本取名为 myfile,命令是:
cp /etc/passwd myfile
这合情合理。再来看看下面这条命令:
cp /etc/passwd /dev/tty
这条命令把口令文件复制到你的终端,效果就是把文件内容 显示在你的显示器上。不妨试一试——然后花点时间想想方才 发生了什么。
用于伪设备的特殊文件(Special Files for Pseudo-Devices)
我们要讨论的最后一类特殊文件是伪设备(PSEUDO-DEVICE)。 伪文件可以充当输入来源或输出去向,却不对应任何实际设备, 无论真实设备还是仿真设备。最有用的两个伪设备是空文件 (NULL FILE)和零文件(ZERO FILE)。空文件是 /dev/null,零文件是 /dev/zero。写往这两个 设备的输出统统被丢弃。因此,有人戏称这些文件为 “位桶(bit bucket)”。
我们在第 15 章详细讨论过空文件。这里举该章的一个例子。 假设你有一个叫 update 的程序,它会读取并修改 大量数据文件。干活的时候,update 会显示有关 进展的统计信息。如果你不想看这些统计信息,可以把标准 输出重定向到两个位桶中的任意一个:
update > /dev/null
update > /dev/zero
如果你想动手试验,这里有个现成的小例子,你现在就能试。 输入命令:
cat /etc/passwd
你会看到口令文件的内容。现在,把 cat 命令的输出 重定向到空文件或零文件,注意输出消失了:
cat /etc/passwd > /dev/null
cat /etc/passwd > /dev/zero
就输出而言,两个位桶的作用相同。唯一的差别在于把它们用 作输入时会发生什么。当程序从 /dev/null 读取时, 无论请求多少字节的输入,结果永远是一个 eof 信号 (见第 7 章)。换句话说,从 /dev/null 读取不会 返回任何东西。
当程序从 /dev/zero 读取时,该文件会生成所请求数 量的字节,只不过这些字节的值全都是 0(数字零)。 在 Unix 中,这个值被视为空字符(NULL CHARACTER),或更 简单地称为 NULL(空值)。尽管听起来奇怪,一个源源不断 提供空字符的来源是有用的。例如,出于安全考虑,常常需要 抹掉某个文件甚至整块磁盘的内容。这种情况下,只要从 /dev/zero 复制所需字节数的数据到输出去向,就能 用空字符覆盖原有数据。
(术语有点让人犯迷糊:空文件什么都不返回,零文件却返回 空字符。生活就是这样。)
下面这个例子用 dd 创建一个全新的文件,里面塞满 空字符。(dd 程序是一个功能强大的 I/O 工具,我 不打算详细讲解。若想进一步了解,请查看联机手册。)
dd if=/dev/zero of=temp bs=100 count=1
在这个例子中,if 是输入文件;of 是输出 文件;bs 是块大小;count 是块的个数。 也就是说,我们把一个 100 字节的数据块从 /dev/zero 复制到一个叫 temp 的文件里。
如果你想拿这个例子做实验,就运行 dd 命令,然后用 hexdump 或 od 程序(第 21 章)显示 temp 的内容。看完之后,可以用 rm 程序 (第 25 章)删除 temp。
最后两个伪设备 /dev/random 和 /dev/urandom 用来生成随机数。因此,每当一个程序 需要随机数时,它只要从这两个文件之一读取即可。
也许你正是那种私人生活中几乎用不到随机数的奇特人物。若 真如此,你可能会纳闷它们为什么重要。答案是:数学家和科 学家用这类数来为包含偶然性的自然过程建立模型。这样使用 时,一个取之不尽、便于调用的随机数来源是无价的。(如果 你对这类东西感兴趣,不妨去读一点关于“随机过程”的材料。) 为数据加密生成密钥的程序也要用到随机数。
— 提示 —
Unix 和 Linux 提供两个不同的特殊文件来生成随机数: /dev/random 和 /dev/urandom。两者的区别 很微妙,但当要求完全随机时,这区别就很要紧。
计算机化的随机数生成器收集“环境噪声”,存入一个“熵池 (entropy pool)”。熵池中的比特随后被用来生成随机数。 如果熵池耗尽了,/dev/random 文件就会停下来,等待 收集到更多噪声。这保证了创建加密密钥等关键操作具有完全 的随机性。不过,如果需要等待熵池重新充满,有时会出 现延迟。
另一方面,/dev/urandom 文件即使熵池已经很低也 从不停止生成数字(u 代表 “unlimited”,无限)。 它改为重复使用一部分旧的比特。理论上,用低熵随机数加密 的数据更容易被攻击一点点。实践中这没什么影响,因为实际 上没人知道如何利用如此微小的理论缺陷(*)。即便如此, 如果你疑心很重,用 random 而不是 urandom 也许能让你睡得更安稳。若不然,用 urandom 就完全 够用,而且它永不会让你等待。
* 脚注
至少在非涉密的文献里是这样。
命名管道:mkfifo(Named Pipes: mkfifo)
命名管道是一种伪文件,用来创建一种特殊的管道设施。就这 样,命名管道成为我们在第 15 章讨论过的常规管道设施的 延伸。在我演示它如何工作之前,先快速看一眼常规管道。 下面这条命令从系统口令文件中提取所有含有 “bash” 字符的 行,然后把数据用管道送给 wc 程序(第 18 章) 统计行数:
grep bash /etc/passwd | wc -l
我们这样使用管道时,管道并没有一个具体的名字:它是自动 创建的,也只在两个进程运行期间存在。因此我们称之为匿名 管道(ANONYMOUS PIPE)。
命名管道与匿名管道的相似之处在于,两者都是把一个进程的 输出接到另一个进程的输入。然而有两点重要差别。第一,你 必须显式地创建命名管道。第二,两个进程结束时命名管道并 不随之消失:它一直存在,直到被删除。所以,一旦创建了命 名管道,你就可以反复使用它。
你常会看到命名管道被称作 FIFO(读作“fie-foe”),它是 “first-in, first-out”(先进先出)的缩写。这是一个计算机 科学术语,用来描述这样一种数据结构:其中元素取出的顺序 与放入的顺序相同。更正式地说,这样的数据结构称为队列 (QUEUE)。(*)
* 脚注
与之相对的是栈(见第 8 章和第 24 章),那是一种按 LIFO (“后入先出”)方式存储和取出元素的数据结构。
要创建命名管道,你用 mkfifo(make FIFO,创建 FIFO)程序。语法是:
mkfifo [-m mode] pipe
其中 mode 是与 chmod 程序一起使用的文件 模式;pipe 是你想创建的管道的名字。(文件模式我们 将在第 25 章讲 chmod 时讨论;眼下你可以先忽略 -m 选项。)
大多数时候,命名管道由程序员用来便利两个进程之间的数据 交换,这种操作称为进程间通信(INTERPROCESS COMMUNICATION, IPC)。在那种情形下,程序会按需创建、使用然后再删除命名 管道。借助 mkfifo 程序,你也可以在命令行上手工 创建一个命名管道。这种事不常做,但我还是给你举个例子, 好让你自己动手实验,体会其中道理。
要做这个实验,你需要打开两个终端窗口,或者使用两个虚拟 控制台。然后在第一个终端窗口里输入下面这条命令。该命令 用 mkfifo 创建一个名为 fifotest 的命名 管道:
mkfifo fifotest
现在,我们往管道里送些输入。在同一个窗口里输入下面这条 命令,用 grep 从系统口令文件中挑出含有 “bash” 的行,并 把输出重定向到 fifotest:
grep bash /etc/passwd > fifotest
接着移到第二个终端窗口或虚拟控制台。输入下面这条命令, 从命名管道读取数据并统计行数。你一敲下命令,wc 程序就会从命名管道读取数据并显示它的输出。
wc -l < fifotest
命名管道用完之后,可以用 rm(remove,删除)程序 把它删掉:
rm fifotest
显然,这是个刻意编造的例子。毕竟刚才我们用一行命令就 做到了完全相同的事:
cat /etc/passwd | wc -l
不过,既然你已经明白命名管道如何工作,不妨想想:对一个 工作需要进程间通信的程序员来说,它们可能有多么宝贵。他要 做的只是让程序创建一个命名管道,然后就可以按需多次用它 把数据从一个进程传给另一个进程。活干完,程序再把管道删 掉。简单、容易、可靠,而且无需为了暂存数据而创建中间 文件。
Proc 文件(Proc Files)
Proc 文件是伪文件,它提供了一种简便的办法,可以直接从 内核查看许多类型的系统信息,而不必动用复杂的程序去搜寻 数据。最初的 proc 文件系统是为了提取有关进程(*)的信息 而开发的,因此得名 “proc”。
* 脚注
如我在第 6 章所说,进程的概念是 Unix 的根基之一。的确, 在 Unix 系统内部,每个对象要么由文件表示,要么由进程 表示。简单地说,文件存放数据或提供对资源的访问;进程则 是正在执行的程序。
更精确的进程定义(同样出自第 6 章)是:一个被装入内存、 就绪待运行的程序,连同该程序的数据以及用于跟踪这个程序 所需的信息。
所有 Proc 文件都存放在 /proc 目录中。在这个目录 里,你会发现系统上每个进程都有一个对应的子目录。这些子 目录的名字就是各个进程的进程 ID。(正如我们将在第 26 章 讨论的,每个进程都有一个唯一的标识号,称为“进程 ID”。) 例如,假设此刻你的系统上有一个进程是 #1952,那么关于它 的信息就能在 /proc/1952 目录里的伪文件中找到。
/proc 目录的想法取自 Plan 9 操作系统(*),那是 一个从 1980 年代中期一直做到 2002 年的研究项目。Plan 9 项目由贝尔实验室创立,发起者正是创造 Unix、C 和 C++ 的 那批人。Plan 9 的一个基本概念是:所有系统接口都应被视为 文件系统的一部分。具体说来,关于进程的信息就放在 /proc 目录里。尽管 Plan 9 并未获得成功,但把许 多类型的信息当作文件来访问这一想法极具吸引力,终究被 Unix 和 Linux 社区广泛采纳。
* 脚注
Plan 9 这个名字来自一部极其粗劣的科幻电影 Plan 9 From Outer Space(《外太空第 9 号计划》), 该片公认是史上最烂的电影。一群技艺精湛、富有远见的计算机 科学家,为什么要用这样一部电影来命名一个重要项目?我只能 说,这是极客式的笑话。
然而 Linux 不仅采纳了 /proc,还把它大大扩展了。 现代 Linux 系统用这个目录存放许多其他伪文件,从而可以 访问种类繁多的内核数据。事实上,如果你是超级用户,甚至 可以通过往某个 Proc 文件写入来修改某些 Linux 内核的 值。(别去试。)Figure 23-3 列出了 Linux 中最值得 关注的 Proc 文件。你可以像查看普通文本文件内容那样查看 这些文件里的信息。例如,要显示有关你处理器的信息,用下面 两条命令中的任一条:
cat /proc/cpuinfo
less /proc/cpuinfo
Figure 23-3: 最值得关注的 Linux Proc 文件
Proc 文件是用来访问内核信息的伪文件,主要由系统程序 使用。虽然你很少会直接使用 Proc 文件,但有几个了解 一下颇有好处。细节见正文。
| Proc 文件 | ……的信息 |
|---|---|
| /proc/xxx/ | 进程 #xxx |
| /proc/cmdline | 内核选项 |
| /proc/cpuinfo | 处理器 |
| /proc/devices | 设备 |
| /proc/diskstats | 逻辑磁盘设备 |
| /proc/filesystems | 文件系统 |
| /proc/meminfo | 内存管理 |
| /proc/modules | 内核模块 |
| /proc/mounts | 已挂载的设备、挂载点 |
| /proc/partitions | 磁盘分区 |
| /proc/scsi | SCSI 与 RAID 设备 |
| /proc/swaps | 交换分区 |
| /proc/uptime | 时间(秒):内核已运行多久、处于空闲模式多久 |
| /proc/version | 内核、发行版以及 gcc 编译器(用于构建内核)的版本 |
通常情况下,除非你是系统管理员,否则你永远不必去看 Proc 文件。大体而言,Proc 文件只被那些需要从内核获取高度技术 性信息的程序使用。例如,在第 26 章我们将讨论 ps (process status,进程状态)程序,它会显示你系统上各进程 的信息。ps 程序正是通过读取相应的 Proc 文件来 收集它所需的数据的。
有一个格外引人入胜的 Proc 文件,我没有列在 Figure 23-3 里,那就是 /proc/kcore。这个文件 表示你计算机的实际物理内存。你可以用带 -l 选项的 ls 程序显示它的大小(见第 24 章):
ls -l /proc/kcore
这个文件看上去会大得吓人:实际上,它的大小与你计算机全 部内存(RAM)的容量相同。但要记住,这是一个伪文件:它并 不真的占用空间。
— 提示 —
Linux 用户:我鼓励你去探索一下系统里的 Proc 文件。看看 这些文件的内部,能让你对自己的系统如何配置、机制如何运作 增长不少见识。为了不出乱子,请确保你不是超级用户, 哪怕你只是看一看。
树形文件系统;文件系统层次标准
(The Tree-Structured Filesystem;
The Filesystem Hierarchy Standard)
在接下来几节里,我们要谈谈 Unix 文件系统以及它的组织 方式。讨论中我以标准的 Linux 文件系统为例。细节在不同 的 Unix 系统之间会有差别,所以你的系统可能与这里写的 有些不同。但基本概念,包括大多数目录名字,都是一样的。
一台典型的 Unix 系统含有远超十万个文件(*),存放在目录 和子目录中。所有这些文件都被组织进一个文件系统 (FILESYSTEM),其中的目录以一棵树的结构组织,树根是一个 称为主目录的目录,即“根目录”。文件系统的职责是存储和 组织数据,并向用户和程序提供对这些数据的访问。 Figure 23-4 给出了文件系统组织方式的图示。根目录 ——直接或间接地——是系统中所有其他目录的父目录。
* 脚注
不,我没有夸大其词。有些基本的 Unix 系统自带超过二十万 个文件。要估算你系统上文件和目录的数量,请以超级用户身份 运行下面这条命令:
ls -R / | wc -l
|
||
|
Figure 23-4. 标准的 Linux 文件系统 Unix 和 Linux 系统含有数量极其庞大的文件——至少 十万到二十万——它们被组织成一棵由目录和子目录构成 的树。这个例子展示的是标准 Linux 文件系统的骨架。 你看到的这个示例遵循文件系统层次标准(本章后面有 说明)。如你所见,根(主)目录包含 16 个子目录, 每个子目录又有自己的子目录、子子目录,依此类推。 图中还列出了 /usr 目录中常见的 7 个子目录。 |
||
第一次查看 Unix 文件系统的组织方式,你或许会有些发怵。 毕竟那些名字古怪而神秘,几乎看不出什么道理。然而,如同 Unix 世界的大部分内容一样,一旦你明白了这些模式以及它们 如何运作,Unix 文件系统就很好懂了。本章后面我们会把 Figure 23-4 里的子目录逐个过一遍,届时我会解释它们 的用途以及名字的由来。
不过在开始之前,我想花点时间说说 Unix 文件系统为什么会 组织成这个样子。如我们在第 2 章所讨论的,第一个 Unix 系统是 1970 年代初在贝尔实验室开发的。Figure 23-5 展示了最初那个 Unix 系统的结构,它本来就是按层次树设计 的。别担心那些名字,后面都会说得明白。我只想让你注意一 点:最初的文件系统与如今的文件系统(Figure 23-4) 的某个子集极其相似。
|
||
|
Figure 23-5. 最初的 Unix 文件系统 Unix 文件系统一开始就用目录和子目录来组织文件。 这张图展示的是 1970 年代初最初那个 Unix 系统所采用 的树形结构。注意,这棵树看上去差不多就是如今文件 系统(Figure 23-4)的一个子集。 |
||
随着 Unix 逐年演进,文件系统的组织方式也跟着变化,以反 映各路 Unix 开发者的需求与偏好。虽然基本格式没变,细节 却因 Unix 的版本而异。这造成了相当的混乱,尤其是当用户 在 System V Unix 和 BSD 之间来回迁移的时候(见 第 2 章)。到了 1990 年代,各 Linux 发行版的创造者开始 引入自己的变体,混乱于是加剧。
1993 年 8 月,一群 Linux 用户成立了一个小组织,制定标准 的 Linux 目录结构。第一份这样的标准于 1994 年 2 月发布。 1995 年初,随着 BSD 社区成员加入这项工作,该组织把目标 进一步扩大。从那以后,他们致力于能为所有 Unix 系统—— 而不只是 Linux——制定标准的文件系统组织方式。这套新的 体系被称为文件系统层次标准(FILESYSTEM HIERARCHY STANDARD,FHS)。当然,由于并不存在 Unix 警察,这个标准 是自愿采用的。尽管如此,许多 Unix 和 Linux 开发者还是 选择采纳了 FHS 的大部分内容。
尽管许多 Unix 系统在某些方面与 FHS 不同,但 FHS 是一个 深思熟虑的方案,确实抓住了现代 Unix 和 Linux 文件系统 组织方式的精髓。理解了 FHS,你会发现与任何其他 Unix 系统打交道都很容易。因此,在我们讨论 Unix 文件系统的 细节时,我将以 FHS 为样板。如果你想看看 FHS 的基本模样, 请看 Figure 23-4。
— 提示 —
如你在 Figure 23-4 中所见,除根目录以外,所有目录 都位于另一个目录之内。所以严格来说,除根目录以外,所有 目录都是子目录。
然而日常说法里,我们通常只说目录。例如,我们可能提到 “/bin 目录”。只有当我们想强调某个目录位于另一个 目录之内时,才使用“子目录”这个词,例如“/bin 是 根目录的一个子目录。”
根目录;子目录(The Root Directory; Subdirectories)
从一开始,Unix 文件系统就被组织成一棵树。我们在第 9 章 把树当作抽象数据结构讨论过,当时我解释过,一棵树的主要 节点称为根(详见第 9 章)。因此,我们把 Unix 文件系统的 主目录称为根目录(ROOT DIRECTORY)。
由于根目录如此重要,它的名字常常必须作为命令的一部分写 出来。每次都要输入 “root” 这几个字母实在太麻烦。于是, 根目录用一个单独的 /(斜杠)来表示。这里举个简单 例子。要列出某个指定目录中的文件,你用 ls 程序 (第 24 章):输入 ls,后面跟上目录名即可。列出 根目录中所有文件的命令是:
ls /
当你要指明某个位于根目录之内的目录或文件时,就写一个 /,后面跟上名字。例如,根目录里有一个名为 bin 的子目录。要列出这个目录中的所有文件,你用 这条命令:
ls /bin
严格地说,它的意思是“位于 /(根)目录之内、名为 bin 的那个目录”。
要表示某个目录或文件位于另一个目录之内,就用 / 把名字隔开。例如,在 /bin 目录里,你会找到存放 ls 程序本身的文件。这个文件的正式名字是 /bin/ls。同样,在 /etc 目录里,你会找到 Unix 的口令文件 passwd(见第 11 章),它正式的 名字是 /etc/passwd。
谈论这类名字时,我们把 / 字符读作 “slash”(斜 杠)。因此,/bin/ls 这个名字读作 “slash-bin- slash-L-S”。
— 提示 —
在你习惯这套术语之前,/ 字符的用法可能让人发 晕。这是因为 / 有两个彼此毫不相干的含义。
在文件名的开头,/ 代表根目录;在文件名中间, / 充当分隔符。(花点时间想想看。)
名称的由来
root
在第 4 章我解释过,要成为超级用户,你要用 root 这个用户 ID 登录。现在你明白这个名字从何而来了:超级 用户的用户 ID 取自根目录之名,而根目录是文件系统中首要 的目录。
挂载文件系统:mount, umount(Mounting a Filesystem: mount, umount)
在 Unix 文件系统里,几十万个文件被组织成一棵巨大的树, 树的基部就是根目录。多数情况下,这些文件并不存放在同一 个物理设备上,而是存放在若干不同的设备上,其中包括多个 磁盘分区。(如我前面解释的,每个磁盘分区都被视为一个独立 的设备。)
每个存储设备都有自己的本地文件系统,其中的目录和子目录 按标准的 Unix 方式组织成一棵树。然而,在能够访问某个本 地文件系统之前,必须把它的树接到主树上。做法是把较小文 件系统的根目录连接到主文件系统中某个指定的目录上。我们 这样把一个较小的文件系统接上来,就叫挂载(MOUNT)它。 主树中用来承接该文件系统的那个目录称为挂载点(MOUNT POINT)。最后,当我们断开一个文件系统时,就叫卸载 (UNMOUNT)它。
Unix 系统每次启动时,都会作为启动过程的一部分自动挂载 若干本地文件系统。因此,等到系统运行起来、可以使用时, 主文件系统早已被其他几个文件系统扩充过了。
有时你可能需要手工挂载一个设备。为此你使用 mount 程序;要卸载一个设备,你用 umount。作为一种总体 上的预防措施,只有超级用户才被允许挂载文件系统。不过为 了方便,有些系统被配置成允许普通用户挂载某些预先设定的 设备,例如 CD 或 DVD。
这里举一条挂载命令的例子。在这个例子里,我们挂载设备 /dev/fd0 上找到的软盘驱动器文件系统,把它在主树 上的 /media/floppy 位置接上:
mount /dev/fd0 /media/floppy
这条命令的效果是,用户就能通过 /media/floppy 目录访问软盘上的文件。
挂载与卸载是需要超级用户身份的系统管理工作,所以我不深 究其细节。请查阅联机手册(man mount)。不过 作为普通用户,你可以列出系统上当前挂载的全部文件系统, 只要单独输入一条 mount 命令:
mount
粗略地说,存储设备分两类。固定媒体(FIXED MEDIA)如硬 盘,永久连接在计算机上。可移动媒体(REMOVABLE MEDIA)则 可以在系统运行时更换:CD、DVD、软盘、磁带、U 盘、存储卡 等等。在系统层面,这一区分很重要,因为一旦某个文件系统 有可能真的凭空消失,Unix 就必须确保对它做妥善管理。例 如,在允许你弹出 CD 之前,Unix 必须确认所有待完成的输出 操作都已完成。
因此,文件系统层次标准规定了挂载文件系统应使用的特定目 录。对于没有挂载在其他位置的固定媒体(例如额外的硬盘), 该目录是 /mnt;对于可移动媒体,该目录是 /media。
名称的由来
Mount, Unmount(挂载,卸载)
在 Unix 早期(大约 1970 年),磁盘驱动器是又大又贵的 设备,以今天的标准看,它们能存的数据相当少(撑死四十 兆字节)。与如今作为完整整体的现代硬盘不同,当年的磁盘 驱动器使用可更换的“磁盘组”,每一组都有自己的文件系统。
每当用户需要更换磁盘组时,系统管理员必须在物理上卸下当 前那个磁盘组,再装上新的一组。这就是直到今天我们还说 “挂载”和“卸载”文件系统的原因。当我们使用 mount 程序时,所做的正是“把磁盘组装进驱动器”这一动作的软件 版本。
根目录巡礼(A Tour of the Root Directory)
要对你计算机上的文件系统建立基本理解,最快的办法就是看 看根目录,把里面所有子目录检查一遍。这些目录构成了整个 系统的骨干,因此有时被称为顶级目录(TOP-LEVEL DIRECTORIES)。
Figure 23-6 总结了文件系统层次标准(FHS,本章前面 讨论过)所规定的根目录标准内容。尽管细节因 Unix 系统而 异,但所有现代文件系统都可视为 FHS 的变体。所以,一旦 你理解了 FHS,就能看懂你碰巧遇到的任何 Unix 文件系统。
Figure 23-6: 根目录的内容
Unix 文件系统的骨架是由顶级目录,也就是根目录的 子目录搭起来的。下面是文件系统层次标准(FHS)规定的 全部顶级目录的清单。由于某些 Unix 和 Linux 系统并不 严格遵循 FHS,你看到的可能与这里有所不同。即便如此, 你仍可把这份清单当作起点,用以理解你自己的系统。
| 目录 | 内容 |
|---|---|
| / | 根目录 |
| /bin | 必备程序 |
| /boot | 引导系统时所需的文件 |
| /dev | 设备文件 |
| /etc | 配置文件 |
| /home | 用户的主目录 |
| /lib | 必备的共享库、内核模块 |
| /lost+found | 由 fsck 找回的受损文件 |
| /media | 可移动媒体的挂载点 |
| /mnt | 未挂载在别处的固定媒体的挂载点 |
| /opt | 第三方应用程序(“可选软件”) |
| /proc | Proc 文件 |
| /root | 根用户(超级用户)的主目录 |
| /sbin | 由超级用户运行的必备系统管理程序 |
| /srv | 本机所提供服务的数据 |
| /tmp | 临时文件 |
| /usr | 存放静态数据的第二文件系统 |
| /var | 存放变动数据的第二文件系统 |
我们这里的目标是从根目录开始,把顶级目录的清单逐个走 一遍。每讨论一个目录,你都可以用 ls 程序 (第 24 章)在自己的系统上查看它。例如,要显示 /bin 目录的内容,可用下面两条命令之一:
ls /bin
ls -l /bin
你用不带选项的 ls 时,只会看到文件名字。如果用 上 -l(long,详细)选项,就会看到更多细节。要是 输出太多、刷得太快,你可以把它用管道送给 less (第 21 章),一屏一屏地看:
ls -l / | less
ls -l /bin | less
根目录
根目录是整个文件系统的基部。在许多 Unix
系统上,根目录里只有子目录。也有某些系统
里你能找到一个普通文件,那就是内核。(见
下面关于 /boot 的讨论。)
/bin
这个目录存放最重要的系统程序,
也就是管理员在单用户模式下维护系统所需的
基本工具(见第 6 章)。这些工具都是可执行
程序,正如本章前面所讨论的,它们是二进制
文件。所以叫 bin:存放二进制文件的地方。
更简单地说,你可以把这个目录当作装程序的
存储箱。这个目录里的一些程序普通用户也会
用到。
/boot
这是系统存放引导(boot)过程所需
全部文件的地方(见第 2 章)。内核必须位于这
个目录或根目录之中。内核很好认:输入下面两
条命令,找一个名字古怪的超大文件即可:
ls -l /boot | less
ls -l / | less
如果你升级过系统,你会发现内核不止一个版本。多数情况 下,在用的是最新的那一个,看名字就能认出来。(版本号 是名字的一部分。)
/dev
在这个目录里,你会找到所有的特殊
文件。大多数特殊文件表示物理设备,少数表示伪设备。
(见本章前面的讨论。)这个目录里还有一个叫
MAKEDEV 的程序,用来创建新的特殊文件。
/etc
这个目录包含配置文件。如我们在
第 6 章所讨论的,配置文件是一种文本文件,在程序
启动时被处理,其中含有影响该程序运行的命令或
信息。就精神实质而言,配置文件与第 14 章讨论过的
rc 文件相似。
/home
当你的 Unix 账户被创建时(见
第 4 章),你在得到用户 ID 的同时也得到一个“主
目录”。你的主目录就是存放你所有个人文件和目录的
地方。主目录的名字与你的用户 ID 相同。所以,如果你
幸运地拥有用户 ID harley,你的主目录就是
/home/harley。主目录的事本章后面还要详细讲。
除一个例外,所有主目录都位于 /home 之中。
这个例外就是超级用户的主目录,它是 /root
(见下文)。
/lib
程序运行时常常要调用库
(LIBRARIES),也就是现成的数据与代码模块。Unix
提供了大量库,使程序能够访问操作系统提供的服务。
这个目录包含运行 /bin 和 /sbin 中
程序所必需的库和内核模块。
/lost+found
如果 Unix 没有正常关机,那些只写
了一部分的文件就可能受损。下一次 Unix 启动时,会运行
一个叫 fsck(filesystem check,文件系统检查)的
专门程序来检查文件系统并修复问题。如果发现损坏的文件,
fsck 会把它们抢救出来,移到 /lost+found
目录。系统管理员随后可以查看这些恢复的文件,并妥善处置。
/media
可移动媒体的挂载点,例如 CD、DVD、
软盘、U 盘、存储卡等等。(见本章前面关于挂载文件
系统的讨论。)
/mnt
未挂载在别处的固定媒体的挂载点,
例如额外的硬盘。曾几何时,这是唯一的挂载目录,所以你
常会看到人们把可移动媒体挂在这里。除非你想让自己的人
生最终被揭穿成一场彻头彻尾的骗局,否则别学这些人:可
移动媒体属于 /media 目录。
/opt
这个目录是第三方应用程序安装自己的
地方。(名字 /opt 代表 “optional software”,
可选软件。)在 /opt 里,每个应用程序都有一个
子目录,可以按照自己的意愿组织。这给了应用开发者一个
指定的安装位置,而不必操心某个特定文件系统可能如何
组织。/opt 中的子目录以公司名或应用名命名。
为了井然有序,LANANA(本章前面讨论过的 Linux Assigned
Names and Numbers Authority)维护着官方的名字清单。想
看这些清单,请在网上搜索 “LSB Provider Names” 或
“LSB Package Names”。(LSB 代表 “Linux Standard
Base”。)
/root
这是超级用户的主目录,也就是用户 ID
为 root 的那位的主目录。其他所有主目录都在
/home 里(见上文)。
/sbin
名字 /sbin 代表 “system
binaries”(系统二进制程序)。这个目录存放用于
系统管理的程序。一般规则是,这个目录里的程序必须以
超级用户身份运行。
/srv
这个目录为与本机提供的服务相关的
数据而预留(因此得名 /srv)。可能把数据存在这里
的典型服务有 cgi、Web、ftp、cvs、rsync 等等。
/tmp
这个目录用来做临时存储。任何人都
可以在这个目录里存放文件。不过,/tmp 的内容最终
会被自动清除。因此,程序通常只用这个目录存放那些短时
需要的文件。
/usr
这个目录是一个第二文件系统的根,
它自己含有若干重要的子目录。/usr 的用途是存放
静态数据(STATIC DATA),即不经系统管理员干预就不会改变
的数据。静态数据就其本性而言不随时间改变。这使得
/usr 可以独立驻留在自己的设备上,甚至可以是一个
只读设备,比如 CD-ROM。从前,/usr 是存放用户主
目录的地方。如今 /usr 只用于静态数据,而会变化的
主目录则存放在 /home 中(见上文)。
/var
这个目录的名字意为
“variable”(可变)。与 /usr 一样,这个目录是
一个第二文件系统的根,其中含有自己的重要子目录。差别
在于:/usr 存放静态数据,而 /var 存放变动
数据(VARIABLE DATA),即就其本性而言会随时间变化的数据,
例如日志文件(记录系统上正在发生什么)、打印文件、电子
邮件等等。与 /usr 一样,/var 文件系统常常
驻留在自己的设备上。以这种方式把静态数据与变动数据分离
开来,系统就更容易管理。例如,系统管理员可以建立一套备
份机制,让变动文件比静态文件保存得更独立(也更频繁)。
名称的由来
dev, etc, lib, mnt, opt, src, srv, tmp, usr, var
Unix 有个传统:文件系统的顶级目录用三个字母的名字。理由 是这样的名字短、好敲。然而说起来,这些名字念起来有些拧 巴。因此,每个三字母名字都有一个大家认可的读法。
dev: "dev"
etc: "et-cetera" or "et-see"
lib: "libe" (to rhyme with "vibe")
mnt: "mount"
opt: "opt"
src: "source"
srv: "serv"
tmp: "temp"
usr: "user"
var: "var" (to rhyme with "jar")
一般规则是:如果你在谈论与 Unix 有关的事情,碰上一个缺 了一两个字母的名字,念出来时就把缺的字母补上。例如, /etc/passwd 文件的名字念作 “slash et-cetera slash password”。文件 /usr/lib/X11 念作 “slash user slash libe slash x-eleven”。
你有时会听人说 etc 代表 “extended tool chest” (扩展工具箱),或者说 usr 的意思是 “Unix system resources”(Unix 系统资源),云云。这些说法都不真实。 这些名字全都是缩写,不是首字母缩略词。
/usr 目录巡礼(A Tour of the /usr Directory)
如本章前面所讨论的,/usr 和 /var 两个目录 是各自独立文件系统的挂载点,这些文件系统被整合进主文件 系统。/usr 文件系统存放静态数据,/var 存 放变动数据。这两个目录装的都是系统数据,与之相对,用户 数据存放在 /home 目录中。
此外,这两个目录都包含若干标准子目录。不过 /var 文件系统主要是给系统管理员用的,对普通用户没那么重要。 /usr 文件系统就有趣得多了:它包含对普通用户和程 序员都有用的文件。因此,我要带你把 /usr 简短地 逛一圈,给你看看文件系统层次标准所描述的最重要的子目录。 作为参考,Figure 23-7 汇总了这些目录。如前所述,你 可能会发现标准布局与你的系统之间存在一些差异。
Figure 23-7: /usr 目录的内容
/usr 目录是一个第二文件系统的挂载点,其中存放用 户和程序员感兴趣的静态数据。按文件系统层次标准,下面是 你在这个目录里会找到的最重要的子目录。细节见正文。
| 目录 | 内容 |
|---|---|
| /usr/bin | 非必备程序(大多数用户程序) |
| /usr/include | C 程序的头文件 |
| /usr/lib | 非必备的共享库 |
| /usr/local | 本地安装的程序 |
| /usr/sbin | 由超级用户运行的非必备系统管理程序 |
| /usr/share | 共享的系统数据 |
| /usr/src | 源代码(仅供参考) |
/usr/bin
与根目录里同名的那个目录
(/bin)一样,这个目录包含可执行程序。
这里的程序比 /bin 多得多。事实上,
/usr/bin 就是系统上大多数可执行程序的家。
例如,在我的一台 Linux 系统上,/bin 只有
100 个程序,而 /usr/bin 有 2084 个程序。
/usr/games
这是我在整个 Unix 文件系统里最
喜欢的目录(也许除了 /home/harley)。顾名思义,
这个目录里有游戏,还有各种消遣节目和教育程序。从前,
/usr/games 里满是各种有趣好玩的程序。我最喜欢的
游戏是 adventure,最喜欢的消遣是 fortune。
在互联网上四处看看,你仍然能找到这些程序——还有更多
——并且可以下载装到自己的系统上。(顺手找找
“Hunt the Wumpus”。)如今可惜大多数游戏都不见了,实在
是令人痛惜。有些 Unix 系统只剩几个游戏,有些干脆一个
也没有。这几乎就像有太多人忘记了 Unix 本该是好玩
的。
/usr/include
这里是 C 和 C++ 程序员所用
包含文件的存放区。包含文件(INCLUDE FILE)有时称为头
文件(HEADER FILE),其中的源代码任何程序员都可按需取
用。典型的包含文件里有子例程、数据结构、变量、常量等
等的定义。我们之所以用“包含文件”这个名字,是因为在 C
语言里用 #include 语句把这类文件纳入程序。而
“头文件”这个名字则是指这类文件通常被包含在程序最开头
(头部)的位置。包含文件的名字带有 .h 后缀,例
如 ioctl.h 和 stdio.h。
/usr/lib
与它的同类 /lib 一样,这个
目录存放库:程序用来访问操作系统所提供服务的现成数据
与代码模块。
/usr/local
这个目录由系统管理员按需使用,
以服务本地用户。本地的程序和文档就存放在这里。这个目
录的一种典型用法是建一个子目录 /usr/local/bin,
用来存放不属于主系统的程序。把软件放在这里,可以确保
系统升级时它不会被覆盖。
/usr/sbin
与 /sbin 一样,这个目录
包含管理员使用的系统程序。从概念上说,/usr/sbin
之于 /sbin,就如同 /usr/bin 之于
/bin(见上文对 /usr/bin 的讨论)。
/usr/share
有大量存放静态数据的文件——文档、
字体、图标等等——需要在用户和程序之间共享。
/usr/share 目录有许多子目录用来存放这类文件。
例如,第 20 章我们谈过词典文件,在许多系统上它可以
在 /usr/share/dict/words 找到。最值得一看的共享
文件是那些包含第 9 章讨论过的联机文档的文件:Unix
手册存放在 /usr/share/man,Info 系统存放在
/usr/share/info。
/usr/src
名字 src 代表 “source code”
(源代码)。在这个目录里,你会找到存放系统源代码的子
目录。/usr/src 中的源代码仅供参考。在许多 Linux
系统上,你可以在 /usr/src/linux 找到内核的源代
码。
/usr/X11
这个目录存放 X Window(图形用户
界面支持系统,见第 5 章)所使用的海量文件和目录。有些
系统上,这个目录叫 /usr/X11R7,或者(如果系统
很旧)/usr/X11R6。
为什么存放程序的目录不止一个?
(Why Is There More Than
One Directory for Programs?)
如我们所讨论的,有两个不同的目录用来存放通用的可执行 程序:/bin 和 /usr/bin。你也许正在纳闷, Unix 为什么要有两个这样的目录?为什么不干脆把所有程序 存放在一个目录里?答案是:这两个 bin 目录是一笔 历史遗产。
1970 年代初,最早几个版本的 Unix 是在贝尔实验室一台 PDP 11/45 小型机上开发的(见第 2 章)。Unix 的开发人员用的 那台 PDP 11/45 有两个数据存储设备。主设备是一个定 头磁盘,常被称作磁鼓(drum)。磁鼓相对较快,因为读写头 不必随磁盘转动而移动。但它的数据存储量有限,不到 3 兆 字节。
次设备是一块叫 RP03 的常规磁盘。RP03 上的读写头可以在 一条磁道与另一条磁道之间来回移动,这让它能存多得多的 数据,最多 40 兆字节。不过,由于读写头要动,这块磁盘 比磁鼓慢得多。
为了在一台计算机上容纳多个存储设备,Unix 的开发人员采 用了一种设计:每个设备都有自己的文件系统。主设备(磁 鼓)存放所谓根文件系统;次设备(磁盘)存放所谓 usr 文件系统。
理想情况下,把整个 Unix 系统都放在磁鼓上当然最好,因为 它比磁盘快得多。可就是没那么大的地方。于是 Unix 的开 发人员把所有文件分成两组。第一组是启动过程以及运行基 础操作系统所必需的文件,这些文件存放在磁鼓上的根文件 系统里。其余文件则存放在磁盘上的 usr 文件系统 中。
启动时,Unix 从磁鼓引导。这就让操作系统能立即访问根文 件中那些必备的文件。Unix 运行起来之后,再挂载 usr 文件系统,从而可以访问其余文件。
这两个文件系统中各有一个 bin 目录用来存放可执行 程序:根文件系统有 /bin,usr 文件系统有 /usr/bin。在启动过程中,也就是 usr 文件系 统挂载之前,Unix 只能访问根文件系统那块相对狭小的存储 区。因此必备程序存放在 /bin 中,其他程序存放在 /usr/bin 中。同样,库文件也分成两个目录:/lib 和 /usr/lib;临时文件则放在 /tmp 和 /usr/tmp 中。所有情形都是一个道理:根文件系统只 放最重要的文件,即引导和排除故障所必需的文件,其余一切 都放进 usr 文件系统。
如今存储设备又快又便宜,还能存海量数据。除少数情况外, 已经没有令人信服的理由非要把 Unix 的核心拆分到存放于多 个设备的多个文件系统里。的确,有些 Unix 系统把所有通用 二进制文件放在一个巨大的目录里。不过,仍有许多 Unix 系 统使用各自独立的文件系统,合成一棵大树。这样设计的原因, 我们将在本章后面讨论虚拟文件系统时再讲。
一般规则是,现代 Unix 系统区分三类软件:任何人都可能用 到的通用程序;只有超级用户才用的系统管理程序;以及需要 许多文件和目录的大型第三方应用程序。如本章前面所讨论 的,这三类程序各自存放在自己的目录里。作为参考, Figure 23-8 汇总了你可能找到 Unix 程序文件的各个 位置。
Figure 23-8: 存放程序文件的目录
Unix 文件系统为程序文件设置了不少不同的存放位置。通用 程序存放在名为 bin(“binary files”,二进制文件) 的目录中;系统管理程序存放在名为 sbin(“system binaries”,系统二进制程序)的目录中;大型第三方应用存放 在名为 opt(“optional software”,可选软件)的目 录中。程序还被进一步分为必备与非必备两类。必备程序是启 动系统或执行关键系统管理所必需的程序,其余的都是非必备 程序。
你在这里看到的细节以文件系统层次标准为依据,你的系统 可能略有不同。
| 通用程序 | |
|---|---|
| /bin | 必备程序 |
| /usr/bin | 非必备程序 |
| /usr/local/bin | 本地安装的程序 |
| 系统管理程序 | |
|---|---|
| /sbin | 由超级用户运行的必备系统管理程序 |
| /usr/sbin | 由超级用户运行的非必备系统管理程序 |
| /usr/local/sbin | 本地安装的系统程序 |
| 第三方应用程序 | |
|---|---|
| /opt/xxx | 应用 xxx 的静态数据;包括程序 |
| /var/opt/xxx | 应用 xxx 的变动数据 |
主目录(Home Directories)
系统目录里有这么多塞满重要文件的地方,显然我们需要一套 有序的制度来管住用户把个人文件存在哪里。当然,像你我这 样聪明的人,如果允许我们把个人程序存进 /bin 目 录、把个人数据文件存进 /etc,也未必会把事情搞 糟。但总的来说,我们不能让芸芸众生随心所欲地把文件乱放 在任何地方——我们需要条理。
解决办法是给每个用户一个属于自己的主目录(HOME DIRECTORY),一个他可以在里面为所欲为的目录。当你的 Unix 账户被创建时(见第 4 章),系统就为你创建了一个主目录。 你主目录的名字记在口令文件里(第 11 章),你一登录,系统 就自动把你放进这个目录。(“在”一个目录里是什么主意,等 你读完第 24 章就会明白得更透彻。)在你的主目录里,你可以 随心所欲地存放文件、创建其他子目录。的确,许多人自己就 有一套庞大精妙的树形结构,全都挂在自己的主目录之下。
文件系统层次标准建议主目录建在 /home 目录里。在 小型系统上,主目录的名字就是用户 ID,例如 /home/harley、/home/linda 等等。在用户 ID 众多的大型系统上,可能多加一层子目录,把主目录按类 别组织起来。例如,在一所大学里,主目录可能放在名为 undergrad、grad、professors 和 staff 的子目录里;在一家房地产公司,你可能会看到 agents、managers 和 admin。意思你 懂了。
唯一一个主目录不在 /home 之下的用户 ID 就是超级用户(root)。因为管理员必须随时都能控制 系统,超级用户的主目录必须始终可用,即使在系统引导时或 系统在单用户模式下运行时也不例外(见第 6 章)。许多系统 上,/home 目录位于一个第二文件系统之中,而它在 被挂载之前是不可用的。相反,/root 目录始终是根 文件系统的一部分,因而始终可用。
每次你登录时,环境变量 HOME 都会被设成你主目录 的名字。因此,显示主目录名字的一个办法,就是用 echo 程序显示 HOME 变量的值:
echo $HOME
(echo 程序只是显示它参数的值。关于它和环境变量 的讨论在第 12 章。)
作为捷径,符号 ~(波浪号)可以作为你主目录的缩写。 例如,你可以用下面这条命令显示主目录的名字:
echo ~
不管它叫什么名字,主目录要紧的地方在于:它是你的,由你 做主。你该做的头一件事,就是建一个 bin 子目录, 用来存放你自己的程序和 Shell 脚本。然后你可以把这个目录 的名字——例如 /home/harley/bin——放进你的搜索 路径。
(搜索路径是存放在 PATH 环境变量里的一串目录。每 当你输入一个并非 Shell 内置的程序名字时,Unix 就会到搜 索路径指定的那些目录里去找该执行的程序。详见第 13 章。)
Figure 23-9 展示了一个以 /home/harley 主目录 为基础的典型目录结构。这个主目录有三个子目录: bin、essays 和 games。essays 目录自己又有三个子目录:history、 literature 和 surfing。所有这些目录都含有 文件,只是图中没有画出来。你在第 24 章会看到,创建和删除 子目录很容易。因此,随着需求变化,扩大或修剪你的目录树都 是轻而易举的事。
|
|
Figure 23-9. 典型的以主目录为根的树形结构 每个用户 ID 都被分配一个主目录。按文件系统层次 标准,主目录应放在 /home 目录里,不过你也会 见到各种变体(见正文)。 在你的主目录里,你可以根据自己的需要创建和删除 子目录。这个例子展示了一个典型的主目录,它有三个 子目录,其中一个自己又有三个子目录。这六个子目录 都含有文件,只是图中没有画出。 |
/home 目录是文件系统层次标准的一部分,在 Linux 系统上被广泛使用。但如果你用的是另一类 Unix,你的主目录 也许在别处。经典的设置——用了很多年——是把主目录放在 /usr 目录里。例如,用户 ID 为 harley 的主 目录就是 /usr/harley。还有的系统用 /u、 /user(带 “e”)、/var/home 或 /export/home。作为参考,下面是你可能在不同系统 上看到的主目录位置示例:
/usr/harley
/u/harley
/user/harley
/var/home/harley
/export/home/harley
在大型系统上,尤其是文件存放在网络上的系统,主目录的确 切位置可能复杂得多;很大程度取决于系统管理员决定如何组织 文件系统。例如,我在一台计算机上有账户,我的主目录是一个 五层深的子子子子子目录:
/usr/local/psa/home/vhosts/harley
— 提示 —
在任何系统上,你都可以用下面两条命令中的任意一条查出 主目录的位置:
echo $HOME
echo ~
虚拟文件系统(The Virtual File System)
在这一章里,你我一起覆盖了不少材料。如果你在意这类东 西,这些细节多少会有些意思。然而,这片森林的内容远不止 树上的叶子。Unix 文件系统是由几位极其聪明的人创造的, 多年来又经许许多多经验丰富的程序员和系统管理员之手不断 增强。最终的产品不仅实用,而且优美。
本节我想帮你体会这套系统的不凡之处,不只是它的有用,还 有它的美。为此,我要解释:存放在各种不同存储设备上的多 个文件系统,是如何被合并成一个巨大的树形结构的。
本章前面我解释过,每个存储设备都有自己的本地文件系统, 其中目录和子目录按标准 Unix 方式组织成一棵树。在你能够 访问这样一个文件系统之前,它必须先连到主文件系统上,这 个过程我们称为挂载。用技术术语说,我们是通过把一个文件 系统的根目录连接到一个挂载点——主文件系统内的某个目录 ——来挂载它的。
我希望你注意到,我们谈论这些概念时,“文件系统”一词有 两种不同用法。别被绕糊涂。第一种是“Unix 文件系统”,那 个包罗万象的巨大结构,整个系统里每个文件、每个目录都在 其中。第二种是较小的、独立的“设备文件系统”,驻留在各 个存储设备上。Unix 文件系统正是由这些较小的文件系统连接 成一个巨大结构而形成的。
要为了解释这一切如何运作,我得从头说起,先回答一个问题: 系统启动时会发生什么?当你打开计算机时,一连串复杂的事件 被启动起来,这就是引导过程(第 2 章有描述)。加电自检之后, 一个叫做引导加载程序(boot loader)的特殊程序接管控制权, 并从引导设备(BOOT DEVICE)读取数据,以便把操作系统装入 内存。多数情况下,引导设备是本地硬盘上的一个分区;但它也 可以是一个网络设备、一张 CD、一个 U 盘等等。
引导设备上的数据之中,藏着那个最初的 Unix 文件系统,即 根文件系统(ROOT FILESYSTEM)。根文件系统是自动挂载的, 它保存启动 Unix 所需的全部程序和数据文件,也保存一旦出 乱子系统管理员会用到的工具。因此,根文件系统至少包含下 面这些目录(本章前面都讨论过):
/bin /boot /dev /etc /lib /root /sbin /tmp
一旦根文件系统挂载完毕、内核启动起来,其他设备文件系统 就会被自动挂载。有关这些文件系统的信息保存在一个配置文 件 /etc/fstab(*)里,系统管理员可以修改它。(名字 代表 “file system table”,文件系统表。)要查看你系统上 的这个文件,用命令:
less /etc/fstab
* 脚注
在 Solaris 上,这个文件叫 /etc/vfstab。
根文件系统总是存放在引导设备上。不过还有三个文件系统可 能驻留在各自的设备上:usr、var 和 home。如果这些文件系统有自己独立的设备,就把它们 接到相应的子目录上,从而连入 Unix 文件系统。usr 文件系统挂载在 /usr;var 文件系统挂载在 /var;依此类推。这一切都是自动完成的,所以等到你 看到登录提示符时,所有文件系统都已挂载,Unix 文件系统已 就绪运行。
每种设备都使用适合该类设备的文件系统。硬盘上的一个分区 使用适合硬盘的文件系统;CD-ROM 使用适合 CD-ROM 的文件系 统,如此等等。可以想见,读写数据所涉及的细节会因设备类 型不同而差别很大,也会因文件系统是本地(在你计算机上) 还是远程(在网络上)而不同。最后,有些文件系统——例如用 于 Proc 文件的 procfs——使用伪文件,而伪文件并不 驻留在存储设备上。作为参考,Figure 23-10 列出了你 最可能遇到的那些文件系统。
Figure 23-10: 最常见的文件系统
作为参考,这里列出你使用 Unix 或 Linux 系统时最常遇到 的文件系统。基于磁盘的文件系统在硬盘、CD、DVD 或其他设 备上存储数据;网络文件系统支持通过网络共享资源;专用文 件系统提供对系统资源(例如伪文件)的访问。
| 基于磁盘的文件系统 | |
|---|---|
| ext3 | 第三代扩展文件系统(Linux) |
| ext4 | 第四代扩展文件系统(Linux) |
| FAT32 | 32 位文件分配表文件系统(Microsoft Windows) |
| HFS+ | 分层文件系统(Macintosh) |
| ISO 9660 | ISO 9660 标准文件系统(CD-ROM) |
| NTFS | NT 文件系统(Microsoft Windows) |
| UDF | 通用磁盘格式文件系统(可擦写 CD 与 DVD) |
| UFS2 | Unix 文件系统(BSD) |
| ZFS | 泽字节文件系统(Solaris) |
| 网络文件系统 | |
|---|---|
| NFS | 网络文件系统(使用广泛) |
| SMB | 服务器消息块(Windows 网络) |
| 专用文件系统 | |
|---|---|
| devpts | 伪终端(PTY)的设备接口 |
| procfs | Proc 文件系统 |
| sysfs | 系统数据文件系统(设备与驱动程序) |
| tmpfs | 临时存储文件系统 |
各种文件系统之间差异如此之大,这就引出一个重要问题。 想想下面两条 cp(复制)命令:
cp /media/cd/essays/freddy-the-pig /home/harley/essays
cp /proc/cpuinfo /home/harley/
我们在第 25 章会讲 cp。眼下我只想让你体会到的 是:第一条命令把 CD 上某个目录里的文件复制到硬盘某个分 区的目录里;第二条命令把伪文件(由内核生成)里的信息复 制到硬盘某个分区的一个文件里。两种情况下,你都只是输入 一条简单命令,那么细节由谁来操心?
细节由一个特殊的设施负责,叫做虚拟文件系统(VIRTUAL FILE SYSTEM,VFS)。VFS 是一个 API(应用程序接口),在你 的程序与各个文件系统之间充当中间人。每当一个程序需要一 次 I/O 操作时,它就向虚拟文件系统发出请求。VFS 找到合适 的文件系统,通过指示设备驱动程序执行 I/O 来与之通信。就 这样,VFS 让你和你的程序能够面对单一、统一的树形结构 (Unix 文件系统)工作,尽管实际上数据来自种种彼此不同的 独立文件系统。
在我们的第一个例子里,数据必须从 CD 读出。cp 程 序发出一个读请求,由虚拟文件系统处理。VFS 把它自己的请 求发给 CD 文件系统,CD 文件系统再向 CD 设备驱动程序发出 相应的命令,由驱动程序读取数据。这样,你不必知道任何细 节,你的程序也不必。在你看来,Unix 文件系统就跟你想象的 那样存在,并且完全按你希望的方式工作。
你看出它的美了吗?在每一次文件操作的一端,虚拟文件系统 用你的语言与你交谈;在另一端,VFS 用各个设备文件系统自己 的语言与它们交谈。结果就是,你和你的程序能够与任何一个 文件系统打交道,而无须直接与它们通信。
再想一个问题。每当开发出一种新的文件系统(比如为新设备 开发的),它怎样才能与 Unix 协同工作?答案在概念上很简 单。新设备的所有开发者要做的,只是教会这个新文件系统说 “VFS 语”。这样它就能加入 Unix 的世界,并且天衣无缝地融 入其中。
妙处就在这里:无论你是什么时候学的 Unix——35 年前还是 35 分钟前——Unix 文件系统看上去一样,用起来也一样。而 且,随着岁月推移,新设备、更好的文件系统不断出现,它们 都会平滑而轻松地融入你的世界。这就是为什么那个在学生还 戴爱情珠、上街反战的时代设计出来的操作系统,在学生已经 戴着手机、上街反战的今天依然运作良好。
那么未来呢?我们无法预知在即将到来的岁月里会出现什么样 稀奇古怪的新设备和信息来源。毕竟,说到技术,谁也不敢许 诺什么。然而我能向你承诺的是:无论出现什么新技 术,它都能与 Unix 一起工作。我还可以承诺:多年以后,你 会把 Unix 教给你的孩子(*)。
* 脚注
所以把这本书留好。
练习(Exercises)
复习问题 #1:
什么是 Unix 文件?
文件的三种主要类型是什么?分别描述每一种。
复习问题 #2:
说明文本文件与二进制文件的区别。各举三个例子。
复习问题 #3:
什么是文件系统层次标准(FHS)?按 FHS,简要说明下列 各项的内容:
1. 根目录(/)
2. 下列顶级目录:
/bin /boot /dev /etc /home /lib /sbin /tmp /usr /var复习问题 #4:
在 FHS 中,哪些目录存放通用程序?
哪些目录存放系统管理程序?
复习问题 #5:
什么是主目录?按 FHS,主目录在哪里找到?
唯一一个主目录在别处的用户 ID 是谁?为什么?
假设你的用户 ID 是 weedly,你是一所大型大学的 本科生。给出你的主目录两个可能的名字。
应用你的知识 #1:
下面这条命令会列出根目录的所有子目录:
ls -F / | grep '/'
用这条命令看看你系统上顶级目录的名字。把你看到的与 文件系统层次标准的基本布局比较一下。有哪些不同?
应用你的知识 #2:
如我们将在第 24 章讨论的,你可以用 cd 从一个目录 换到另一个目录,用 ls 列出目录的内容。例如,要切 到 /bin 目录并列出其内容,你可以用:
cd /bin; ls
探索你的系统,找出下列文件存放在哪里:
• 用户的主目录
• 通用程序(Unix 工具)
• 系统管理程序
• 特殊文件
• 配置文件
• 手册页
• 内核
• 引导系统时所需的文件
提示:你会发现 whereis 程序很有用(见第 25 章)。
应用你的知识 #3:
输入下面这条命令:
cp /dev/tty tempfile
输入几行文本,然后按 ^D。
你刚才做了什么?它是怎么起作用的?
提示:为了收拾自己留下的摊子,你应当输入下面这条命令, 删除名为 tempfile 的文件:
rm tempfile
进一步思考 #1:
Unix 用非常通用的方式定义“文件”。给出这种制度的三个 优点,再给出三个缺点。
进一步思考 #2:
至于各 Unix 系统在多大程度上必须遵循文件系统层次标准, 并没有统一的规定。有些系统相当贴近这个理想模型,有些 则差别很大。
如果要求所有 Unix 系统使用同一套基本的文件系统层次, 这是好事还是坏事? 讨论一下优点与缺点。
© 本书全部内容,2026 年版权所有,Harley Hahn
完整的商标与版权信息