小专题:高级文件系统实现者指南 CIO俱乐部

来源:百度文库 编辑:16楼社区 时间:2021/01/17 08:01:48
2006-3-29 11:15:20
伴随着 Linux 2.4 版本的发行,出现了大量的文件系统可能性,其中包括 ReiserFS、XFS、GFS 和其它文件系统。这些文件系统听起来的确都很酷,但是它们真正能做些什么呢,擅长在哪些方 面,以及在 Linux 产品环境下如何才能安全地使用它们呢?Daniel Robbins 通过向您展示如何在 Linux 2.4 的环境下建立这些新的高级文件系统来回答以上的问题。
·日志记录和 ReiserFS 的好处(第 1 部分)这一系列文章的目的是向您详实地介绍 Linux 的各种新的文件系统,包括 ReiserFS、XFS、JFS、GFS、ext3 和其它的文件系统。我要让您知道一些必要的实用知识,有了这些知识 您才能开始使用这些文件系统。我的目标是帮助您尽可能地避免潜在的隐患;这就是说,我们将仔细地了解一下文件系统的稳定性、性能问题(或好或差)、您应该知道的任何的负面应用程序交互作用、内核与补丁的最佳搭配以及更多内容。您可以把这一系列的文章看成是这些下一代文件系统的“内幕指南”。 这就是准备好的内容。但是要开始这一系列工作,我还有一篇文章要脱离这个主题,用来为接下来的行程做准备。我将会涉及两个对于 Linux 开发社区非常重要的主题 ― 日志和 ReiserFS 后的设计理念。日志是非常重要的,因为它是我们长期以来一直期待的技术,而现在终于出现了。在 ReiserFS、XFS、JFS、ext3 和 GFS 中都用到它。确切地理解日志是做什么的和为什么 Linux 需要它是非常重要的。即使您对日志已有所掌握,我还是希望我有关日志的介绍可以成为一个好的模型,以用来向其他人解释这项技术,或者作为一项惯例,以利于全世界的部门和组织开始向这些新的日志文件系统进行转变。这个过程通常是由“Linux guy/gal”开始的,就像您自己也会说服其他人应该这么做。 在这篇文章的后半部分,我们将看看 ReiserFS 后的设计理念。通过这么做,我们能够很好地掌握一个事实,那就是这些新的文件系统并不只是为了做同样的事比老的系统快一点。它们还允许我们用以前完全不可能的方法来处理事情。开发人员在阅读这一系列文章时应该牢记这一点。这些新的文件系统的能力 将很可能对您今后的 Linux 软件开发工程的代码编写产生影响。  理解日志:元数据 正如您所了解的那样,文件系统的存在允许您储存、检索和操作数据。为了实现这一目的,文件系统需要保持一个内在的数据结构使得您的数据有组织并且便于访问。这一内部的数据结构(确切地说就是“关于数据的数据”)被称为 元数据。就是这个元数据的结构为文件系统提供了其特定的身份和性能特征。  通常,我们并不直接和文件系统的元数据打交道。而是一个特别的 Linux 文件系统驱动程序为我们作相应的工作。Linux 文件系统驱动程序是专门用来操作复杂的元数据的。然而,为了使得文件系统驱动程序正常工作,有一个很重要的必要条件;它需要在某种合理的、一致的和没有干扰的状态下找到元数据。否则,文件系统驱动程序就不能理解和操作元数据,那么您也就不能存取文件了。 理解日志:fsck 这就引出了 fsck。当 Linux 系统启动时,fsck 启动并扫描系统的 /etc/fstab 文件中列出的所有本地文件系统。fsck 的工作就是确保要装载的文件系统的元数据是处于可使用的状态。大多数的时候是可使用的。当 Linux 关闭时,它仔细地把所有的缓冲区数据转送到磁盘,并确保文件系统被彻底卸载,以保证系统再次启动时能够使用。典型的就是,fsck 扫描那些将被装载的文件系统,确定它们已被彻底卸载,并做出合理的假设 ― 所有的元数据都没有问题。 然而,我们都知道不时地会有一些 意外发生,例如意想不到的电源故障或者系统挂起。当出现这些不幸的情况时,Linux 没有机会彻底卸载文件系统。当系统重新启动,fsck 开始扫描时,它会检测到这些没有彻底卸载的文件系统,并做出合理的假设 ― 文件系统可能没有为 Linux 文件系统驱动程序准备好。这就很有可能导致元数据在某种情况下陷入困境。  所以,为了弥补这种情况,fsck 将开始彻底的扫描并且全面地检查元数据,修正这一过程中找到的任何错误。一旦 fsck 完成这样的工作,文件系统就可以使用了。尽管意想不到的电源故障或者系统挂起可能造成最近修改的数据丢失,但是由于元数据现在是一致的,文件系统就可以被装载和投入使用了。 fsck 的问题 迄今为止,为确保文件系统的一致性,这种方法可能听起来并不是个坏主意,但是却不是最佳的解决方案。问题出自于这样一个事实 ― fsck 必须扫描文件系统 全部的元数据,以确保文件系统的一致性。对所有的元数据做彻底的一致性检查是一项极为费时的工作。通常至少要花上好几分钟才能完成。更糟糕的是,文件系统越大,完成这个彻底的扫描所花费的时间就 越长。这就是个大问题,因为当 fsck 做它自己事情的时候,您的 Linux 系统实际上就是被切断的,并且如果您有一个庞大数量的文件系统存储,您的系统可能就会花上半个小时或者更长的时间来执行 fsck。当然,在任务紧要的数据中心的环境里,也就是在系统正常运行极为重要的环境下,标准的 fsck 工作可能会造成破坏性的结果。幸运的是,有更好的解决方案。  日志 日志文件系统通过增加一个叫做日志的新的数据结构来解决这个 fsck 问题。这个日志是位于磁盘上的结构。在对元数据做任何改变以前,文件系统驱动程序会向日志中写入一个条目,这个条目描述了它将要做些什么。然后,它继续并修改元数据。通过这种方法,日志文件系统就拥有了近期元数据被修改的历史记录,当检查到没有彻底卸载的文件系统的一致性问题时,这个记录就唾手可得了。 可以这样来看待日志文件系统 ― 除了存储数据(您的素材)和元数据(关于素材的数据)以外,它们还有一个日志。您可以称它们为元元数据(关于素材数据的数据)。 运作中的日志 那么,fsck 如何处理日志文件系统呢?实际上,通常它什么都不做。它只是忽略文件系统并允许它被装载。在快速地恢复文件系统到达一致性状态的背后,真正起作用的在于 Linux 文件系统驱动程序中。当文件系统被装载时,Linux 文件系统驱动程序查看文件系统是否完好。如果由于某些原因出了问题,那么就需要对元数据进行修复,但不是执行对元数据的彻底扫描(就像 fsck 那样),而是查看日志。由于日志中包含了按时间顺序排列的近期的元数据修改记录,它就简单地查看 最近被修改的那部分元数据。因而,它能够在几秒钟时间内将文件系统恢复到一致性状态。并且与 fsck 所采用的传统方法不同,这个日志重放过程在大型的文件系统上并不需要花更多的时间。多亏了日志,数百 G 的文件系统元数据几乎能在瞬间恢复到一致性的状态。  ReiserFS 现在,我们来谈一谈 ReiserFS,它是我们将要研究的几个日志文件系统中的第一个。ReiserFS 3.6.x(作为 Linux 2.4 一部分的版本)是由 Hans Reiser 和他的在 Namesys 的开发组共同开发设计的。Hans 和他的组员们相信最好的文件系统是那些能够有助于创建独立的共享环境或者命名空间的文件系统,应用程序可以在其中更直接、有效和有力地相互作用。为了实现这一目标,文件系统就应该满足其使用者对性能和功能方面的需要。那样,使用者就能够继续直接地使用文件系统,而不必建造运行在文件系统之上(如数据库之类)的特殊目的层。  小文件的性能 那么,如何能使文件系统更加适应环境呢?Namesys 已经决定着眼于文件系统的一个方面,至少最初是 ― 小文件的性能。通常,像 ext2 和 ufs 这样的文件系统在这一方面做的并不是很好,经常迫使开发人员转向数据库或者特别组织的处理来获取他们所需要的某种性能。随着时间的推移,这种“围绕问题进行编码”的方法怂恿了代码的膨胀和许多不兼容的特殊目的 API,这并不是好事情。 这儿有一个 ext2 如何鼓励这种编程的例子。ext2 很擅长存储大量大小在 20k 以上的文件,但是对于存储 2,000 个 50 字节的文件来说,它就不是一种很理想的技术了。当 ext2 必须处理非常小的文件时,不只是性能显著地下降,而且存储效率也同样下降,因为 ext2 是按 1k 或者 4k 的块来分配空间的(可在文件系统创建时设定)。 现在,常规的明智做法会提示您 不应该在文件系统上储存这么多小的文件。而是应该存储在某种运行在文件系统之上的数据库里。作为对这种说法的回应,Hans Reiser 指出无论何时您需要在文件系统的顶上建立一层,那就意味着文件系统不满足您的需要。如果文件系统满足您的需要,那么您首先就要避免使用特殊目的的解决方案。这样就可以节省开发的时间,并消除代码膨胀。这些代码可能是在您手动处理自己的个人存储器或者缓冲机制时,或者与数据库的某个库交互作用过程时所产生的。  理论上是这样。但是在实际运用中,ReiserFS 的小文件性能会是如何的好呢?好得让人吃惊。实际上,当处理小于 1k 的文件时,ReiserFS 大概要比 ext2 快 8 到 15 倍!更妙的是,这些性能提高并不以其它文件类型的性能损失为代价。通常,ReiserFS 几乎在各个方面都优于 ext2,但是在处理小文件时才真正体现出了其闪光点。  ReiserFS 技术 那么 ReiserFS 是怎样提供如此出色的小文件性能的呢?ReiserFS 使用了特殊的优化 b* 平衡树(每个文件系统一个)来组织所有的文件系统数据。这为其自身提供了非常不错的性能改进,也能够减轻文件系统设计上的人为约束。例如,现在一个目录下可以容纳 100,000 个子目录。另一个使用 b* 树的好处就是 ReiserFS 能够像大多其它的下一代文件系统一样,根据需要动态地分配索引节,而不必在文件系统创建时建立固定的索引节。这有助于文件系统更灵活地适应其面临的各种存储需要,同时提供附加的空间有效率。  ReiserFS 有许多特征是特别针对提高小文件的性能的。和 ext2 不同,ReiserFS 并不固定地以 1k 或者 4k 的块分配存储空间,而是分配所需要的精确尺寸。而且 ReiserFS 也包括了以尾文件为中心的特殊优化 ― 尾文件是指那些比文件系统块小的文件及文件结尾部分。为了提高性能,ReiserFS 能够在 b* 树的叶子节点存储文件,而不是把数据存储在磁盘的其它地方再指向它。 这做了两件事。第一,它显著地提高了小文件的性能。由于文件数据和 stat_data(索引节)信息是紧挨着存储的,它们通常能被同一次磁盘 IO 操作所读取。第二,ReiserFS 能够压缩尾文件,节省大量磁盘空间。实际上,带有尾文件压缩功能(默认)的 ReiserFS 文件系统可以比同等的 ext2 文件系统多存储 6 个百分点的数据,这就其自身来说是令人惊叹的。 然而,由于在文件被修改时,尾文件压缩迫使 ReiserFS 重装数据,这就导致了性能上的轻微折损。鉴于这个原因,ReiserFS 尾文件压缩可以被关掉,允许系统管理员在速度与空间有效率上做出选择,或者牺牲一些存储能力来换取更高的速度。 ReiserFS 确实是一个非常出色的文件系统。在我的下一篇文章中,我将会指导您在 Linux 2.4 下完成 ReiserFS 安装的全过程。我们还将仔细地看一看性能调整,应用程序交互作用(和怎么围绕他们工作)以及使用的最佳内核等等。
·设置 ReiserFS 系统(第 2 部分)在本文中,我会向您展示如何让 ReiserFS 运行在 2.4 系列的内核下。我还会和您分享很多关于不同主题的技术信息,包括使用 ReiserFS 最好的 2.4 内核,性能注意事项等等。因为我首先 会谈到安装,所以我建议您先通读本文,然后再遵循安装指示。这样,您开始在自己的系统上运行 ReiserFS 的时候,脑子里就会有所有的技术注解,这样您就可以在各个步骤作必要的调整。
 
 
寻找好的内核
 
要在您的系统上使用 ReiserFS,您首先需要找到一个合适的内核。如果您已经熟悉 2.4 内核的发展,您就会知道这个过程比它听起来要复杂。本文完成时,最新的内核是 2.4.6-pre2;但是我建议您在自己的 ReiserFS 系统上还是使用 2.4.4(标准的 Linus 内核)或者 2.4.4-ac9(稍作改进的 Alan Cox 内核)。从我的测试看来,2.4.5 似乎很不稳定,所以我不推荐将这个内核作为产品使用;让我们希望 2.4.6 会比它好很多吧。
 
如果您想在自己的产品 ReiserFS 系统中使用除 2.4.4 或 2.4.4-ac9 以外的其它内核, 一定要作必要的检查以确保 ReiserFS(和内核大体上)是稳定的。当然,如果您是在一个测试服务器上安装 ReiserFS,只要不会丢失重要的数据,您就可以随意使用任一种内核。
 
总的来说,要注意内核稳定性问题,特别是 ReiserFS 的稳定性问题,这有两个原因。因为 ReiserFS 是一个“实验的”内核功能,您不能假定一个使用新内核的 ReiserFs 实现刚刚从 tarball 中解出就能够很好地运行。第二个原因(也许在目前是更重要的问题)在于大部分的 2.4 内核发行版和补丁都有一点不稳定,所以目前您行动时还是需要谨慎一点。理论上,所有的 2.4 发行版都应该是准产品化的,因为 2.4 版本应该是一个稳定的系列;但是,实际上它们(还)不是,所以强烈鼓励您小心使用新的、没有测试过的内核。
 
这段信息的意思不是要吓得您不敢使用 ReiserFS 或者 Linux 2.4,而是要给那些更敢于冒险的人一点理性。不要总是在重要的系统上使用各种还处于测试期的内核;如果这样,您 会吃到苦头的。当您使用一个不可靠的内核时,您不仅仅面临着系统锁定的危险;您还面临着丢失数据和文件系统崩溃的危险,这是您绝对不希望发生的。即便 ReiserFS 实现本身是稳定的,内核其他部分的主要错误也很可能引起文件系统崩溃的产生。
 
如果您没有最新的内核稳定性信息来源,我建议您定期地访问 Linux 每周新闻(请参阅本文后面的 参考资料),及时了解最新的可能出现的内核问题(信息每个星期四更新)。希望现在我已经说服了更多喜欢冒险的读者坚持使用 2.4.4 或 2.4.4-ac9 内核作为产品 ReiserFS 的配置,让我们继续吧。
 
标准内核
 
OK,我们现在可以谈谈安装和运行一个准产品化的 ReiserFS 系统的三种选择。第一种选择是只用标准 2.4.4 Linux 内核。第二种选择是使用 2.4.4 内核,同时使用 ReiserFS 大补丁,它包括了一些专门的补丁,使 ReiserFS 达到配额兼容,并与在本机运行的 NFS 服务器更加兼容。第三种选择是,我们可以使用 2.4.4 内核和 ac9 补丁(即 2.4.4-ac9),再加上或不加大补丁。通常我推荐使用 2.4.4-ac9 和大补丁,因为大补丁并没有任何负作用,而且您可能会需要它,而且 ac9 比标准内核执行得好多了。但是,如果您不愿意使用 ac 内核,标准 2.4.4 也不错了。我会简单地向您介绍设置 2.4.4-ac9 和大补丁的步骤,但是如果因为某些原因您不愿安装这两个补丁或其中之一,只要跳过这个步骤即可。现在,让我们开始吧。
 
首先,从 kernel.org 下载 2.4.4 内核源码并进入您的 /usr/src 目录。 移去该处的任何 linux 目录或者符号连接,如果是目录就将其改名,如果是符号连接就只需删除它。然后:
 
# cd /usr/src
 
#cat /path/to/linx-2.4.4.tar.bz2 | bzip2 -d | tar xvf -
 
ac9 补丁和大补丁
 
如果您计划只用标准 2.4.4 内核,您就已经拥有全部所需的资源了,可以跳过其余的补丁。然而,我推荐您继续使用下面的 ac 补丁和大补丁。
 
要使用 ac9 补丁,请从 kernel.org 下载 Alan Cox ac9 补丁。然后键入:
 
# cd /usr/src/linux
 
# bzip2 -dc /path/to/patch-2.4.4-ac9.bz2 | patch -p1
 
一旦标准内核安装完毕,就到 DiCE 去下载 DiCE 的 ReiserFS 大补丁。这个步骤还是可选的,不过也是推荐的,特别是如果您将在此系统上运行 NFS 服务器或者需要配额(如果不是这样,无论如何这个补丁也不会有什么坏处)。要使用大补丁请遵循以下步骤:
 
# cd /usr/src/linux
 
# bzip2 -dc /path/to/bigpatch-2.4.4.diff.bz2 | patch -p1
 
一旦所有可选的修正和大补丁安装完毕,您就可以为 ReiserFS 配置内核了。
 
注意:如果您需要更多关于如何编译 Linux 内核的指导,可参阅 developerWorks上的 编译 Linux 内核免费教程。下面是一个简单的摘要。
 
配置内核非常简单。首先,键入“make menuconfig”。在“Code maturity level options”部分,确保启用了“Prompt for development and/or incomplete code/drivers”选项。然后到“File systems”部分,启用“ReiserFS support”选项。 您将 ReiserFS 设置为直接编译到内核中(而不是作为一种模块);而且通常启用“Have reiserFS do extra internal checking”这个选项不是什么好主意。现在,保存您的设置,编译您的内核(“make dep;make bzImage;make modules;make modules_install”),然后配置您的引导装载器(boot loader)来装载新的配置了 ReiserFS 的内核。
 
重点:保存当前内核并配置引导装载器(boot loader)总是个好主意,这样万一您的新内核无法工作,您就可以用原来的内核启动。
 
使用 ReiserFS
 
安装工具
 
在重新启动之前,我们需要安装“reiserfsprogs”工具,它包括了“mkreiserfs”、“resize_reiserfs”(对 LVM 用户很有用),还有“fsck.reiserfs”。您可以从 Namesys.com 下载页下载最新版本的“reiserfsprogs”(目前是“3.x.0j”)。一旦这些工具下载完毕,您就可以按以下步骤编译和安装“reiserfsprogs”了:
 
# cd /tmp
 
# tar xzvf reiserfsprogs-3.x.0j.tar.gz
 
# cd reiserfsprogs-3.x.0j
 
# ./configure
 
./configure output will appear here
 
# make
 
make output will appear here
 
# make install
 
make install output will appear here
 
既然工具已经安装完毕,现在您可以按需要创建任何新的分区(使用“fdisk”或者“cfdisk”)或 LVM 逻辑卷(使用“lvcreate”)并重新启动您的系统。如果您正在创建标准分区,您可以简单地将此分区标记为一个“Linux native file system”(83)。
 
创建和安装文件系统
 
重新启动后,您就可以在一个空的分区上创建 ReiserFS 文件系统,如下所示:
 
# mkreiserfs /dev/
 
hdxy
 
在上面的示例中,/dev/hdxy 应该是对应于一个空闲分区的设备节点。象安装其它文件系统一样安装它:
 
# mount /dev/
 
hdxy
 
/mnt/reiser
 
而且,如果您想在自己的 /etc/fstab 文件中添加一个 ReiserFS 文件系统,只需将“freq”和“passno”字段设置为“0”,如下所示:
 
/dev/hdc1       /home                   reiserfs        defaults       0 0
 
从这以后,您的 ReiserFS 文件系统应该不比它的对手 ext2 逊色了,而且您再也不必担心长时间的“fsck”,整体性能也会好得多 ― 特别是对于小文件来说。
 
ReiserFS 技术注解
 
文件系统稳定性
 
我已经在一个产品环境下使用了一个多月 2.4.4 版本内核的 ReiserFS(在 cvs.gentoo.org 开发服务器上),根本没有什么崩溃问题。2.4.4 和 2.4.4-ac9 已经非常稳定了。我们的服务器执行大量的磁盘 IO 任务,因为它是我们的 cvs 资料档案库、 “dev-wiki”、gentoo.org 邮件服务器、基于邮差的邮件列表,还有很多其他东西的所在之处。
 
ReiserFS 的局限性
 
虽然 ReiserFS 在几乎每一种类型的应用程序上都比 ext2 文件系统表现得好,有些地方 ReiserFS 目前还是有点欠缺。令人欣慰的是,这些问题其实并不是 ReiserFS 无法克服的局限性,只是 Namesys 开发人员还没有来得及编码或优化的地方。
 
没有 dump/restore
 
是的,这是真的;ReiserFS 没有“dump”和“restore”实现。如果您想使用 ReiserFS 又碰巧是个喜欢使用“dump”的人,您就必须寻找备份数据的其他方法。实际上,事实证明这个根本就不是问题,因为 2.4 系列内核从一开始就不兼容“dump”和“restore”命令。要了解关于 dump 和 2.4 内核的不兼容性的更多信息,请阅读 Linus Torvalds 的帖子(请参阅 参考资料),他在帖子中说道“Dump 从一开始就是个愚蠢的程序。不要去碰它。”
 
性能问题
 
虽然 ReiserFS 通常抢尽了 ext2 的风头,但它在特定情况下确实有性能上的不足之处。第一个就是处理稀疏文件的能力。ReiserFS 处理稀疏文件的能力 会比 ext2 差很多。当 Namesys 开发人员一直设法为 ReiserFS 4 优化 ReiserFS 的这一部分时,这一点会有所改观。在这之前,ext2 是对处理稀疏文件有很高要求的应用程序的较好的解决方案。
 
在对大量文件执行多个 stat() 调用的代码中,可能也会碰到问题。一个可能触发这种性能缺陷的应用程序(它只存在于 2.4 系列内核的 ReiserFS 实现,在 2.2 内核中不存在)就是“mutt”邮件用户代理(请参阅 参考资料),它在被用于读取大型 maildir 形式的邮箱时会触发性能缺陷。显然,每个邮件文件 mutt 都要 stat 两次,这样就会比平常更加影响性能。ReiserFS 开发小组已经意识到了这个特殊的问题,并已经确定了它的起因,如果近期没有解决方案的话,您应该可以在 ReiserFS 4 中看到相应的解决方案。
 
性能调整
 
幸运的是,有几种简单通用的性能调整方法可以用来缓解这些问题。第一种是用“noatime”选项(一种对 ReiserFS 和其它文件系统都有用的安装选项)来安装您的 ReiserFS 文件系统。您可能知道,UNIX 系统为文件系统上的每一个对象记录一个 atime,或称为访问时间,每次读取文件时,atime 都会被更新。对于大部分人来说,atime 邮戳功能不是十分有用,而且几乎没有任何应用程序(我想不到一个)依靠 atime 处理什么重要的任务。出于这个原因,通常可以安全地关掉它,这样可以带来一个很好的全面性能提升。通常情况下,除非您确实知道自己需要 atime 支持,您还是应该用 noatime 选项来安装文件系统。使用如下所示的 /etc/fstab 条目:
 
/dev/hdc1       /home                   reiserfs        noatime       0 0
 
在关于 ReiserFS 的第一篇文章中,我曾提到 ReiserFS 有一项特别的功能,称为“tail packing”。在 ReiserFS 术语中,“tail”是小于一个系统文件块(4k)的文件,或不能填满一个完整的文件系统块的文件末尾部分。ReiserFS 具有确实卓越的小文件处理性能是因为它能够将这些 tail 合并到它的 b*tree(它的主要数据组织结构)中去,这样它们就能真正地接近 stat-data(ReiserFS 中等同于索引节点的单元)。然而,因为这些 tail 不能填满一个完整的块,它们会浪费很多磁盘空间(当然是相对地说)。为了解决这个问题,ReiserFS 使用了它的“tail packing”功能来将这些 tail 压缩到占用尽可能小的空间。通常,这么做可以让 ReiserFS 文件系统比大小相等的 ext2 文件系统多容纳大约 5% 的数据。
 
更多关于 notail 的内容
 
然而,tail packing 也有它的缺点。首先,它的确给性能带来了一个小却不可忽视的冲击。幸运的是,ReiserFS 开发人员已经预计到有些人宁愿牺牲大约 5%的磁盘空间换取一点额外的性能,所以他们创建了“notail”安装选项。当文件系统用这个选项安装时,tail packing 将被关闭,使您的存储容量减小,却有更快的速度。通常,重视文件系统性能的狂热分子同时启用“notail”和“noatime”选项来安装他们的文件系统,从而带来显著的性能提升:
 
/dev/hdc1       /home                   reiserfs        noatime,notail       0 0
 
即便您想节省一点磁盘空间,有时候暂时用一下“notail”选项安装文件系统也是件好事。特别是,大多数引导装载器(boot loader)装载一个在启用 tail packing 的 ReiserFS 文件系统上创建的内核时,都会出现问题。如果您正在使用一个比版本 21.6 还低的 LILO,您就会碰到这种问题。在使用最新版本的 GRUB 时也会碰到问题,即不能装载它的 stage1 和 stage1_5 文件,尽管在装载实际内核的时候没有什么问题。如果您已经在经历这种问题了,您可以这样修正此问题-使用“notail”选项安装文件系统,将文件移到另一个文件系统中,然后再把它们移回来。当文件被重新创建时,就不会有 tail 了。另外,记住您还可以很轻易地 重新安装文件系统(用新选项),而不需要卸载它。这个特别的示例使用“notail”选项重新安装根目录文件系统。通常情况下,如果您想使用 tail packing,但也需要引导装载器(boot loader)从根目录文件系统装载辅助文件(如内核),这条命令就很有用了:
 
# mount / -o remount,notail
 
Qmail 注解
 
如果您正在 ReiserFS 中使用 qmail,您应该了解一些重要的参考资料。首先,您应该使用 qmail 1.03 源码的补丁。它修正了 qmail 非同步调用“link()”和“unlink()”时出现的问题,它不但是 ReiserFS 的问题,恰好也是 ext2 的问题。接下来您应该看看 Jedi 的 qmail 调优页面,它包含了很多关于怎样尽可能发挥 qmail 性能的很好的建议。最后,请务必查看 Jedi 的 ReiserSMTP 软件包。ReiserSMTP 包含一个 GPL 插件来代替 qmail 的 SMTP 部分;Jedi 的这种替换是特别为 ReiserFS 调整的,依赖新的队列处理例程,它可以给您带来双倍的邮件收取性能。
 
结论
 
我发现 ReiserFS 真是一个不可思议的文件系统,它提供了很好的小文件处理性能和非常好的(通常比 ext2 要好)的常规文件处理性能。因为有了 ReiserFS,我的开发人员可以在仅仅 15 秒内完成 Gentoo Linux “cvs” 更新,而使用 ext2 通常要花大约两分钟时间。ReiserFS 使我们开发人员的生活更加愉快,还使我们的 cvs 服务器处理大量的并发 IO 而不会引起硬盘狂转和对交互性能的负面影响。
 
然而除了所有这些,关于 ReiserFS 最激动人心的地方是它将来的发展方向。Hans Reiser 对 ReiserFS 有一个非常激进而创新的计划,包括计划扩展文件系统从而能够将其作为一个发展成熟的高性能数据库来使用,最后还包括事务支持和高级查询功能。这意味 ReiserFS 将不仅仅“只是另一个高性能文件系统”;相反,它将开拓新的可能和道路,使我们能够用创新方式去解决传统的存储问题。有了 Namesys 的合作,Linux 将来的发展一定会非常激动人心 -这绝对是一件好事情。
·使用 tmpfs 虚拟内存文件系统和绑定挂装(第 3 部分)
在本文中,我们要谈论几个相对次要的主题。首先,我们会简单地介绍一下 tmpfs,也就是我们知道的虚拟内存(virtual memory,VM)文件系统。Tmpfs 可能是现在 Linux 可以使用的最好的类似于 RAM 磁盘的系统,而且是 2.4 内核的一个新功能。然后,我们将简单地介绍另一个 2.4 内核的新功能,叫做“绑定安装”,它在安装(和重新安装)文件系统的时候带来了很大的灵活性。在下一篇文章中,我们会把重点集中在 devfs 上,之后,我们会花点时间来进一步熟悉新的 ext3 文件系统。
 
 
介绍 tmpfs
 
如果我必须一下子说清楚 tmpfs,我会说 tmpfs 就象虚拟磁盘(ramdisk),但不一样。象虚拟磁盘一样,tmpfs 可以使用您的 RAM,但它也可以使用您的交换分区来存储。而且传统的虚拟磁盘是个块设备,并需要一个 mkfs 之类的命令才能真正地使用它,tmpfs 是一个文件系统,而不是块设备;您只是安装它,它就可以使用了。总而言之,这让 tmpfs 成为我有机会遇到的最好的基于 RAM 的文件系统。
 
tmpfs 和 VM
 
让我们来看看 tmpfs 更有趣的一些特性吧。正如我前面提到的一样,tmpfs 既可以使用 RAM, 也可以使用交换分区。刚开始这看起来可能有点武断,但请记住 tmpfs 也是我们知道的“虚拟内存文件系统”。而且,您可能也知道,Linux 内核的虚拟内存资源同时来源于您的 RAM 和交换分区。内核中的 VM 子系统将这些资源分配到系统中的其它部分,并负责在后台管理这些资源,通常是透明地将 RAM 页移动到交换分区或从交换分区到 RAM 页。
 
tmpfs 文件系统需要 VM 子系统的页面来存储文件。tmpfs 自己并不知道这些页面是在交换分区还是在 RAM 中;做这种决定是 VM 子系统的工作。tmpfs 文件系统所知道的就是它正在使用某种形式的虚拟内存。
 
不是块设备
 
这里是 tmpfs 文件系统另一个有趣的特性。不同于大多数“标准的”文件系统,如 ext3、ext2、XFS、JFS、ReiserFS 和其它一些系统,tmpfs 并不是存在于一个底层块设备上面。因为 tmpfs 是直接建立在 VM 之上的,您用一个简单的 mount 命令就可以创建 tmpfs 文件系统了。
 
# mount tmpfs /mnt/tmpfs -t tmpfs
 
执行这个命令之后,一个新的 tmpfs 文件系统就安装在 /mnt/tmpfs,随时可以使用。注意,不需运行 mkfs.tmpfs ;事实上,那是不可能的,因为没有这样的命令存在。在 mount 命令执行之后,文件系统立即就被安装并且可以使用了,类型是 tmpfs 。这和 Linux 虚拟磁盘如何使用大相径庭;标准的 Linux 虚拟磁盘是 块设备,所以在使用它们之前必须用您选择的文件系统将其格式化。相反,tmpfs 是一个文件系统。所以,您可以简单地安装它就可以使用了。
 
Tmpfs 的优势
 
动态文件系统的大小
 
您可能想知道我们前面在 /mnt/tmpfs 安装的 tmpfs 文件系统有多大。这个问题的答案有点意外,特别是在和基于磁盘的文件系统比较的时候。/mnt/tmpfs 最初会只有很小的空间,但随着文件的复制和创建,tmpfs 文件系统驱动程序会分配更多的 VM,并按照需求动态地增加文件系统的空间。而且,当 /mnt/tmpfs 中的文件被删除时,tmpfs 文件系统驱动程序会动态地减小文件系统并释放 VM 资源,这样做可以将 VM 返回到循环当中以供系统中其它部分按需要使用。因为 VM 是宝贵的资源,所以您一定不希望任何东西浪费超出它实际所需的 VM,tmpfs 的好处之一就在于这些都是自动处理的。 请参阅 参考资料。
 
速度
 
tmpfs 的另一个主要的好处是它闪电般的速度。因为典型的 tmpfs 文件系统会完全驻留在 RAM 中,读写几乎可以是瞬间的。即使用了一些交换分区,性能仍然是卓越的,当更多空闲的 VM 资源可以使用时,这部分 tmpfs 文件系统会被移动到 RAM 中去。让 VM 子系统自动地移动部分 tmpfs 文件系统到交换分区实际上对性能上是 好的,因为这样做可以让 VM 子系统为需要 RAM 的进程释放空间。这一点连同它动态调整大小的能力,比选择使用传统的 RAM 磁盘可以让操作系统有好得多的整体性能和灵活性。
 
没有持久性
 
这看起来可能不象是个积极因素,tmpfs 数据在重新启动之后不会保留,因为虚拟内存本质上就是易失的。我想您可能猜到了 tmpfs 被称为“tmpfs”的一个原因,不是吗?然而,这实际上可以是一件好事。它让 tmpfs 成为一个保存您不需保留的数据(如临时文件,可以在 /tmp 中找到,还有 /var 文件系统树的某些部分)的卓越的文件系统。
 
使用 tmpfs
 
为了使用 tmpfs,您所需要的就是启用了“Virtual memory file system support(以前是 shm fs)”选项的 2.4 系列内核;这个选项在内核配置选项的“File systems”部分。一旦您有了一个启用了 tmpfs 的内核,您就可以开始安装 tmpfs 文件系统了。其实,在您所有的 2.4 内核中都打开 tmpfs 选项是个好主意,不管您是否计划使用 tmpfs。这是因为您需要内核 tmpfs 支持来使用 POSIX 共享的内存。然而, System V共享的内存不需要内核中有 tmpfs 就 可以工作。注意,您 不需要为了让 POSIX 共享的内存工作而安装 tmpfs 文件系统;您只需要在内核中支持 tmpfs 就可以了。POSIX 共享的内存现在使用得不太多,但这种情况可能会随着时间而改变。
 
避免低 VM 情况
 
tmpfs 根据需要动态增大或减小的事实让人疑惑:如果您的 tmpfs 文件系统增大到它耗尽了 所有虚拟内存的程度,而您没有剩余的 RAM 或交换分区,这时会发生什么?一般来说,这种情况是有点讨厌。如果是 2.4.4 内核,内核会立即锁定。如果是 2.4.6 内核,VM 子系统已经以很多种方式得到了修正,虽然耗尽 VM 并不是一个美好的经历,事情也不会完全地失败。如果 2.4.6 内核到了无法分配更多 VM 的程度,您显然不愿意不能向 tmpfs 文件系统写任何新数据。另外,可能会发生其他一些事情。首先,系统的其他一些进程会无法分配更多的内存;通常,这意味着系统多半会变得 极度缓慢而且几乎没有响应。这样,超级用户要采取必要的步骤来缓解这种低 VM 的情况就会很困难,或异常地耗时。
 
另外,内核有一个内建的最终防线系统,用来在没有可用内存的时候释放内存,它会找到占用 VM 资源的进程并终止该进程。不幸的是,这种“终止进程”的解决方案在 tmpfs 的使用增加引起 VM 耗尽的情况下通常会导致不良后果。以下是原因。tmpfs 本身不能(也不应该)被终止,因为它是内核的一部分而非一个用户进程,而且也没有容易的方法可以让内核找出是那个进程占满了 tmpfs 文件系统。所以,内核会错误地攻击它能找到的最大的占用 VM 的进程,通常会是 X 服务器(X server),如果您碰巧在使用它。所以,您的 X 服务器会被终止,而引起低 VM 情况的根本原因(tmpfs)却没有被解决。Ick.
 
低 VM:解决方案
 
幸运的是,tmpfs 允许您在安装或重新安装文件系统的时候指定文件系统容量的最大值上限。实际上,从 2.4.6 内核到 2.11g 内核,这些参数只能在 安装时设置,而不是重新安装时,但我们可以期望在不久的将来可以在重新安装时设置这些参数。tmpfs 容量最大值的最佳设置依赖于资源和您特定的 Linux 主机的使用模式;这个想法是要防止一个完全使用资源的 tmpfs 文件系统耗尽所有虚拟内存结果导致我们前面谈到的糟糕的低 VM 情况。寻找好的 tmpfs 上限值的一个好方法是使用 top 来监控您系统的交换分区在高峰使用阶段的使用情况。然后,确保指定的 tmpfs 上限稍小于所有这些高峰使用时间内空闲交换分区和空闲 RAM 的总和。
 
创建有最大容量的 tmpfs 文件系统很容易。要创建一个新的最大 32 MB 的 tmpfs 文件系统,请键入:
 
# mount tmpfs /dev/shm -t tmpfs -o size=32m
 
这次,我们没有把 tmpfs 文件系统安装在 /mnt/tmpfs,而是创建在 /dev/shm,这正好是 tmpfs 文件系统的“正式”安装点。如果您正好在使用 devfs,您会发现这个目录已经为您创建好了。
 
还有,如果我们想将文件系统的容量限制在 512 KB 或 1 GB 以内,我们可以分别指定 size=512k 和 size=1g 。除了限制容量,我们还可以通过指定 nr_inodes=x 参数限制索引节点(文件系统对象)。在使用 nr_inodes 时, x 可以是一个简单的整数,后面还可以跟一个 k 、 m 或 g 指定千、百万或十亿(!)个索引节点。
 
而且,如果您想把上面的 mount tmpfs 命令的等价功能添加到 /etc/fstab,应该是这样:
 
tmpfs      /dev/shm tmpfs      size=32m 0     0
 
在现存的安装点上安装
 
在以前使用 2.2 的时候,试图在 已经安装了东西的安装点再次安装任何东西都会引发错误。然而,重写后的内核安装代码使多次使用安装点不再成为问题。这里是一个示例的情况:假设我们有一个现存的文件系统安装在 /tmp。然而,我们决定要开始使用 tmpfs 进行 /tmp 的存储。过去,您唯一的选择就是卸载 /tmp 并在其位置重新安装您新的 tmpfs/tmp 文件系统,如下所示:
 
#  umount /tmp
 
#  mount tmpfs /tmp -t tmpfs -o size=64m
 
可是,这种解决方案也许对您不管用。可能有很多正在运行的进程在 /tmp 中有打开的文件;如果是这样,在试图卸载 /tmp 时,您就会遇到如下的错误:
 
umount: /tmp: device is busy
 
然而,使用最近的 2.4 内核,您可以安装您新的 /tmp 文件系统,而不会遇到“device is busy”错误:
 
# mount tmpfs /tmp -t tmpfs -o size=64m
 
用一条命令,您新的 tmpfs /tmp 文件系统就被安装在 /tmp,并安装在已经安装的不能再被直接访问的分区 之上。然而,虽然您不能访问原来的 /tmp,任何在原文件系统上还有打开文件的进程都可以继续访问它们。而且,如果您 unmount 基于 tmpfs 的 /tmp,原来安装的 /tmp 文件系统会重新出现。实际上,您在相同的安装点上可以安装任意数目的文件系统,安装点就象一个堆栈;卸载当前的文件系统,上一个最近安装的文件系统就会重新出现。
 
绑定安装
 
使用绑定安装,我们可以将所有甚至 部分已经安装的文件系统安装到另一个位置,而在两个安装点可以同时访问该文件系统。例如,您可以使用绑定安装来安装您现存的根文件系统到 /home/drobbins/nifty,如下所示:
 
#  mount --bind / /home/drobbins/nifty
 
现在,如果您观察 /home/drobbins/nifty 的内部,您就会看到您的根文件系统(/home/drobbins/nifty/etc、/home/drobbins/nifty/opt 等)。而且,如果您在根文件系统修改文件,您在 /home/drobbins/nifty 中也可以看到所作的改动。这是因为它们是同一个文件系统;内核只是简单地为我们将该文件系统映射到两个不同的安装点。注意,当您在另一处安装文件系统时,任何安装在绑定安装文件系统 内部的安装点的文件系统都不会随之移动。换句话说,如果您在单独的文件系统上有 /usr,我们前面执行的绑定安装就会让 /home/drobbins/nifty/usr 为空。您会需要附加的绑定安装命令来使您能够浏览位于 /home/drobbins/nifty/usr 的 /usr 的内容:
 
#  mount --bind /usr /home/drobbins/nifty/usr
 
绑定安装部分文件系统
 
绑定安装让更妙的事情成为可能。假设您有一个 tmpfs 文件系统安装在它的传统位置 /dev/shm,您决定要开始在当前位于根文件系统的 /tmp 使用 tmpfs。虽然可以在 /tmp(这是可能的)安装一个新的 tmpfs 文件系统,您也可以决定让新的 /tmp 共享当前安装的 /dev/shm 文件系统。然而,虽然您可以在 /tmp 绑定安装 /dev/shm 就完成了,但您的 /dev/shm 还包含一些您不想在 /tmp 出现的目录。所以,您怎么做呢?这样如何:
 
# mkdir /dev/shm/tmp
 
# chmod 1777 /dev/shm/tmp
 
# mount --bind /dev/shm/tmp /tmp
 
在这个示例中,我们首先创建了一个 /dev/shm/tmp 目录,然后给它 1777 权限,对 /tmp 适当的许可。既然我们的目录已经准备好了,我们可以安装,也只能安装 /dev/shm/tmp 到 /tmp。所以,虽然 /tmp/foo 会映射到 /dev/shm/tmp/foo,但您没有办法从 /tmp 访问 /dev/shm/bar 文件。
 
正如您所见,绑定安装非常强大,让您可以轻易地修改文件系统设计,丝毫不必忙乱。下一篇文章,我们会谈到 devfs,至于现在,您也许会想看看下面的参考资料。
·设备管理文件系统,devfs 的好处(第 4 部分)
设备,到处都是设备
 
 
Devfs,也叫设备文件系统(Device Filesystem),设计它的唯一目的就是提供一个新的(更理性的)方式管理通常位于 /dev 的所有块设备和字符设备 。您也许知道,典型的 /dev 树包含数百个块特殊文件和字符特殊文件,它们全都在根文件系统上。每个特殊文件都可以让用户空间进程轻松地与内核设备实现交互。举例来说,通过对这些特殊文件执行操作,您的 X 服务器就能够访问视频硬件, fsck 可以执行文件系统检验, lpd 可以通过并行端口向打印机发送数据。
 
实际上,通常 Linux 和 Unix 更“酷”的方面是,设备不是简单地隐藏在晦涩的 API 之后,而是真正地与普通文件、目录和符号链接一样存在于文件系统上。因为字符和块设备是映射到普通文件系统名称空间的,我们通常可以用有意义的方式来与硬件交互,可以仅使用标准 Unix 命令,如cat 和 dd。除了有趣之外,这还使我们有更强的能力,并提高生产力。
 
设备管理问题
 
然而,虽然设备特殊文件本身是一件好事情,但典型的 Linux 系统以一种不太理想而且麻烦的方式管理这些特殊文件。 如今,Linux 支持 很多不同种类的硬件。这意味着严格意义上我们中绝大多数在 /dev 中都有数百个特殊文件来表示所有这些设备。还不止这样,这些特殊文件中大多数甚至不会映射到系统中存在的设备上(但需要它们存在,只是考虑到我们最终会在系统中添加新的硬件/驱动器),这让事情变得更令人困惑。
 
仅从这个方面来看,我们就知道 /dev 需要彻底检修,而创建 devfs 的明确目的就是让 /dev 变回原形。为了很好地理解 devfs 是怎样解决绝大多数 /dev 管理问题的,我们从设备驱动程序的角度来看看 devfs。
 
设备管理内幕
 
为了很好地理解 devfs ,最好是先理解从设备驱动程序的角度来看 devfs 是怎样改变事物的。传统地(不使用 devfs),根据是否注册在 块设备或 字符设备,基于内核的设备驱动程序通过调用 register_blkdev()或 register_chrdev() 向系统的其余部分注册设备。
 
您必须提供一个 主设备号(一个无符号 8 位整数)作为 register_blkdev()或 register_chrdev() 的参数;然后,在设备注册之后,内核就会知道这个特定的主设备号对应于执行 register_--?dev()调用的特定设备驱动程序。
 
那么,设备驱动程序开发人员为调用 register_--?dev() 提供的主设备号 应该是什么呢?如果开发人员不打算将设备驱动程序与外界共享,那么什么号码都可以,只要它与当前内核使用的其它主设备号都不冲突即可。开发人员还可以选择动态地分配 register_--?dev() 调用的设备的主设备号。然而,这样的解决方案通常只是在驱动程序不会被其它人使用的情况下可行。
 
获取号码
 
然而,如果开发人员想让驱动程序与外界共享(大多数 Linux 开发人员常常采用这一方法),那么仅仅从“真空”中抽一个主设备号或者使用动态的主设备号分配就不行了。相反,开发人员必须联系 Linux 内核开发人员,这样他(她)的特定的设备才能分配一个“正式”主设备号。那么,在整个 Linux 世界中,这个特定的设备(也 只有这个设备)才会被关联到那个特定的主设备号。
 
有一个“正式的”主设备号很重要,因为要与特定的设备交互,管理员必须在 /dev 创建一个特殊文件。当设备节点(特殊文件)创建后,它使用的主设备号必须同内核内部使用的完全相同。这样,进程对设备执行操作时,内核就会知道应该引用什么设备驱动程序。让特殊文件到内核驱动程序的映射成为可能的是主设备号,而不是真实的设备名称(它和非 devfs 系统无关)。
 
一旦设备驱动程序具备正式主设备号,设备就可以被公开使用了,设备节点也就可以开始并入不同分发版的 /dev 树,还有它们的正式 /dev/MAKEDEV 脚本(用来帮助超级用户用正确的主从设备号、权限和所有权创建设备节点的特殊脚本)中。
 
传统的问题
 
不幸的是,这种方法有很多可伸缩性问题。不仅设备驱动程序开发人员联系内核开发人员来获取正式主设备号是一件讨厌的事,内核开发人员弄清他们怎样分配所有这些主设备号甚至更加恼人。这种任务在很多方面很象系统管理员跟踪公司局域网静态 IP 地址分配的工作 ― 这并不十分有趣。正如系统管理员可以利用 DHCP 来缓解这种管理负担,如果设备注册有某种类似的方法就好了。
 
不只是这样,Linux 还正在耗尽主设备号和副号码。虽然这种问题可以通过简单地扩展主设备号和副号码使用的位数,首先维护这些主设备号映射就很讨厌了,所以我们又在考虑有没有更好的方法来处理这些事情。幸运的是,有这样的方法;进入 devfs。
 
进入 devfs
 
devfs_register()
 
这里是对 devfs 如何一下子处理事情和解决这些问题的一个简单明了的快速纲要。一旦 devfs 被正确配置(包括在内核添加 devfs 支持和对启动脚本进行一些稍复杂的更改),超级用户重新启动系统。然后内核开始启动,设备驱动程序开始向系统的剩余部分注册设备。您会记起在非 devfs 系统上, register_blkdev()和 register_chrdev() 调用(连同提供的主设备号)正是用于这一目的。然而,现在启用了 devfs,设备驱动程序是用一种新的、改进了的内核调用来注册设备,称为 devfs_register()。
 
这里是 devfs_register() 调用有趣的地方。虽然为了兼容性目的指定主设备号和副号码作为参数是可能的,但不再需要这样了。相反, devfs_register()调用接受 设备路径(就是它在 /dev 下可能的出现形式)作为参数。举例来说,假设 foo 设备驱动程序希望使用 devfs 注册设备。它会提供一个 foo0 的参数给 devfs_register(),从而告诉内核应该在 devfs 名称空间的根目录创建一个新的 foo0 设备。相应的, devfs_register() 在 devfs 名称空间的根目录添加 foo0设备节点,并记录这个新的 foo0 节点应该映射到内核中的 foo设备驱动程序。
 
运行的 Devfs
 
一旦所有设备驱动程序启动并向内核注册适当的设备,内核就启动 /sbin/init 和系统初始化脚本开始执行。在启动过程初期(在文件系统检查前),rc 脚本将 devfs 文件系统安装在 /dev 中,/dev 包含了 devfs 名称空间的表达。这意味着在安装 /dev 后,所有注册的设备(如上面的 /dev/foo0)都可以访问,就象在非 devfs 上一样。当它们被访问时,内核 通过 devfs 设备名称映射到合适的设备驱动程序,而不是通过主设备号。
 
这种系统的优点是,所有需要的设备节点(没有别的了)都由内核自动创建。这不仅仅意味着不再需要 MAKEDEV(因为所有注册的设备都只“出现”在 /dev 中),还意味着 /dev 不再被成百个“无用的”设备节点所充斥。实际上,使用 devfs,您可以只要查看 /dev 就知道系统上有什么设备。所以,如果您有一台支持热插拔的膝上型电脑,这意味着您甚至可以在您从系统中插入和拔出 PC 卡时魔术般地让设备从 /dev 中出现和消失。这让 devfs 成为对以前笨拙局面的一个非常彻底和实用的解决方案。
 
devfs 的优点
 
Devfs 让很多事变得容易许多。请考虑一下创建一张 Linux 可引导光盘的问题,它包括一个位于 CD 上的引导装载器、一个 initrd、一个内核和一个回送文件系统。当 CD 引导时,引导装载器装载内核和 initrd,然后内核执行 initrd 上的 /linuxrc脚本。 /linuxrc 的主要任务是安装 CD,从而使回送文件系统本身也可以被安装和访问。
 
没有 devfs, linuxrc 就需要“查看” /dev 中的很多特殊文件,它们可能有也可能没有表示连接到系统的真实硬件。例如, linuxrc 会需要检测 /dev/hdc、/dev/scd0、/dev/hdb 和其它的设备以检测“活动的”光盘驱动器设备。在检测进程中,很可能命中几个“无用的”设备节点。
 
然而,使用 devfs, linuxrc 只在 /dev/cdroms 中寻找,它包含了系统中所有和 活动的光盘驱动器相关联的特殊文件,不管是 IDE 的还是 SCSI 的。由于这种便捷的新式 devfs 约定,再不需要猜测了;只有活动的设备才会列出,而且设备检测代码甚至不必担心底层的光盘驱动器的细节,比如说它使用什么 IDE 通道或者什么 SCSI ID。实际上,这是 devfs 的另一个主要好处;在我下一篇文章中,我们会看到 devfs 下 /dev 中的设备有全新的缺省位置。
 
实际上,如果您想访问一个特定的块设备(如磁盘、分区、光盘驱动器等等),事实上有 几个不同的特殊文件可以引用。例如,我的服务器只有一个 SCSI 光盘驱动器;如果启用了 devfs,我就可以通过安装 /dev/cdroms/cdrom0 或 /dev/scsi/host0/bus0/target4/lun0/cd 访问它。两种都引用同一个设备,我可以引用我认为最方便的特殊文件。如果愿意,我还可以使用一种老式的设备名称(/dev/sr0)访问光盘驱动器,这都是因为有一个非常便捷的叫 devfsd的小程序。 devfsd 是一个有功能很多的程序,它负责创建老式的“兼容性”特殊文件,还允许您以很多种方式自定义 /dev。在我的下一篇文章中,我们会详细讨论 devfsd,到时我会一直引导您启动 devfs 并在您自己的系统上运行它。在那之前,请参考下面的参考资料以了解更多关于 devfs 的信息。
·开始到 devfs 的转换(第 5 部分)
在 上一部分(第 4 部分)中,我们具体讨论了什么是 devfs,以及它是如何解决几乎所有的设备管理问题的。现在是在您的系统上启动和运行 devfs 的时候了。 在本文中,我们将使您的系统为启用 d evfs 作好准备,在下篇文章中,我们将真正开始向 devfs 的转换。即使在 devfs 的下一篇(也是最后一篇)出版以前,您也完全可以遵循本文中的步骤,因为当我们完成了所有这些步骤之后,您的系统将仍旧继续正常运行 ― 它仅仅为即将来临的 devfs 转换作好了准备。因此,没有必要因为后继文章还未出现而不遵循这些步骤。
 
 
请注意: 因为我们将对 Linux 系统的几个部分作出相当大的更改,所以确实可能搞糟您的系统。因此,如果您在对 Linux 系统内部更改方面没有经验的话,毫无疑问您应该在一个无关紧要的 Linux 机器上这样做,至少第一次应该这样。
 
需求
 
要启动和运行 devfs,需要使用 Linux 2.4 的一些版本(2.4.6 或 2.4.8 就不错)以及 glibc 2.1.3 或更高版本。还建议您使用 Xfree86 4.0 或更高版本,如果您的系统版本较低,建议您首先升级到 Xfree86 4.1。
 
紧急 bash 救援
 
在下篇文章中,我们将更改 Linux 系统中对启动至关重要的部分。既然完全可能因某个错误而使您偶尔搞糟引导过程,我将在本文首先讲述:如何用紧急 bash shell 命令启动和运行系统。如果您确实碰巧发现系统因为 init 脚本或 /sbin/init 本身的问题而不能引导,就可以使用这个紧急引导过程。
 
进行紧急引导最简单的方法是:在引导时刻使用 GRUB 或 LILO 把 init=/bin/bash 选项传递给内核。 如果使用 GRUB,您应该能够通过点击 e 来实时地编辑当前菜单项,从而在需要时交互地传递该选项。如果使用 LILO,则确保在继续下一步之前,您已知道如何向内核传递启动选项,必要时,还要创建一个新的“紧急”LILO 启动选项。
 
过程
 
这样,基本的“救援”过程如下。首先,将 init=/bin/bash 作为一个内核引导选项传递给内核。当内核引导时,它将以 /bin/bash 而不是通常的 /sbin/init 作为第一个进程启动。 您不会被提示进行登录就看到一个 root 用户 bash 提示符:
 
#
然而,尽管您看到一个 root bash 提示符,实际上只安装了根文件系统,而且仅以只读的形式安装。下面介绍在这之后如何启动和运行您的系统。如果文件系统没有卸装干净的话,应该首先对它们进行 fsck 。首先对根文件系统执行 fsck -a ,然后 fsck -R -A -a 就会负责处理所有其它的文件系统:
 
# fsck -a /dev/hda1
 
# fsck -R -A -a
 
既然文件系统已经一致(或者,如果文件系统在系统重引导时已经卸装干净,并且您跳过了上一步骤),我们可以简单地以可读写方式重新安装根文件系统并且安装 /proc,如下所示:
 
# mount / -o remount,rw
 
# mount /proc
 
然后,安装可能位于其它分区中的所有重要的文件系统树。例如,如果在另一个分区上有 /usr,则还要输入:
 
# mount /usr
 
如果您想做的不仅仅只是打开一个编辑器的话,则最好是激活交换分区。如果您使用 emacs,您可能会需要它:)
 
# swapon -a
 
现在,您应该能够使用您喜爱的编辑器来编辑任何需要编辑的文件,以便修复现有的引导问题。一旦完成,只需按安装时的顺序,以只读方式重新安装分区即可。例如,如果有一个单独的 /usr 分区,为使所有的文件系统处于一致的状态(准备重新引导),可以输入:
 
# mount /usr -o remount,ro
 
# mount / -o remount,ro
 
现在,您可以安全地重新引导了。但愿现在已经解决了引导问题,并且可以使用正常的 LILO 或 GRUB 选项启动和运行系统。
 
# /sbin/reboot -nfi
 
为 devfs 作好准备
 
devfs 配置
 
既然您知道了在紧急情况下该怎么做,我们就可以使系统为 devfs 做好准备了。在下一篇文章里,我们将对 Linux 系统作相对复杂的更改。为什么需要这样呢?因为我们不仅仅在内核中启用 devfs 功能,这的确非常容易。我们还将以一种特别方式设置 devfsd (设备管理守护进程),用它来备份和恢复任何对设备许可权和所有权的更改。我们需要用到很多小窍门来使这个新的系统极佳地工作。但是一旦实现,我想您将对这个结果 非常满意。
 
devfs 内核支持
 
在系统上启用 devfs 的第一步比较简单:就是要使内核支持 devfs。为此目的,您需要一个 2.4 系列的内核。使用 make menuconfig 或者 make xconfig ,转至 Code maturity level选项部分并确保启用了 Prompt for development and/or incomplete code/drivers选项。然后转至 File systems内核配置部分,查找 /dev file system support (EXPERIMENTAL) 选项。选中它。您将会看到在您刚启用的选项下方,出现了两个附加选项。第一个选项控制 devfs 在内核引导时是否自动安装到 /dev。 不要启用它,我们将用一个特殊脚本手动安装 /dev。 第二个选项, Debug devfs,也应该被禁用。
 
禁用 /dev/pts
 
当您在屏幕上看到 File systems kernel configuration 部分时,如果您碰巧启用了 /dev/pts file system for Unix98 PTYs 的支持,则禁用该支持。devfs 提供了相似的功能,所以您就不再需要 devpts 文件系统了。继续进行然后保存内核配置;我们很快就要编译并安装一个新的内核!最后,在进行下一步以前,检查一下在 /etc/fstab 中是否有 /dev/pts 项;如果有,把它注释掉,使它在启动时不再被安装。
 
各种配置风格
 
下一步,将 /etc/securetty 文件装入编辑器。该文件由 login 使用,它允许您指定允许 root 用户使用以进行登录的 tty s。通常,它包含从 tty1 到 tty12 的设备,每行一个。为了使这个文件适用于 devfs,您应当为这些 ttys 加入适当的 devfs 类型名字,并保留原有的 tty? 名字,以备日后您决定禁用 devfs 引导之需。把以下几行添加到 /etc/securetty 的最下面。
 
vc/1
 
vc/2
 
vc/3
 
vc/4
 
vc/5
 
vc/6
 
vc/7
 
vc/8
 
vc/9
 
vc/10
 
vc/11
 
vc/12
 
安装 devfsd
 
接下来就是在系统上安装 devfsd ,即 devfs 助手守护进程。Devfsd 将会负责创建“旧类型”兼容性设备节点;在注册/注销设备时执行自动化操作;负责备份对根文件系统上某个目录的设备许可权和所有权的更改,以及其它更多功能。现在,我们将只安装 devfsd;在下一篇文章中,我们将使它和 devfs 一起启动和运行。为了安装 devfsd,首先需要下载最新版本的 devfsd 压缩文件。(请参阅本文后面的 参考资料),当前版本为 1.3.16。然后执行下列步骤:
 
# tar xzvf devfsd-1.3.16.tar.gz
 
# cd devfsd
 
# make
 
现在,devfsd 应该编译好并可以安装了。如果您的帮助手册页存储在 /usr/man 中,输入 make install ;如果您正在使用 FHS 兼容系统,并且您的帮助手册页存储在 /usr/share/man 中,输入 make mandir=/usr/share/man install 。现在将安装 Devfsd,但还未运行,这正是我们现在要做的。
 
安装注释
 
我们即将配置 devfsd ,以便完全支持兼容性设备,所以tty? 应该足够了。然而,小心驶得万年船,尤其在可能会影响到 login 是否允许超级用户进入系统的时候。用我们的方法,即使存在问题而且 devfsd 无法启动,超级用户在 login: 提示符下登录时也不会有问题,哪怕已经启用了 devfs。
 
启动新内核
 
现在,继续前进,编译并安装刚刚配置好的内核。这个内核应该是您现在内核的临时替代品;它应能正常引导;并且尽管它内置了 devfs 支持,您应该觉察不出它与您现在正在运行的内核有什么差别。 一旦新内核安装完毕,重新引导系统以确保到目前为止一切工作正常。
 
Devfs 配置方法
 
您的系统现在已经作好了向 devfs 转换的准备,我将在下一篇文章中详细介绍。现在,是熟悉一下我们正在使用的方法的时候了。您将看到,用 devfs 启用系统可能会很棘手,尤其当您使用到 devfs 的所有优点(如持久的许可权和所有权)的时候。
 
内核自动安装带来的问题
 
确实有很多方法来使系统 devfs 启用。其中之一就是让内核在引导时自动将 devfs 安装到 /dev;我们将不使用此选项,尽管这样 确实是可以的。乍看起来,这种方法似乎很有意义,因为它可以保证所有 devfs 类型的设备对于所有进程都是可用的,甚至对第一个进程 /sbin/init 也是如此。然而,这种方法有一个问题。尽管 devfs 提供所有“新类型”的设备,但旧类型的设备节点却是由 devfsd 守护进程创建。 devfsd 不是由内核启动的,所以即使让内核在引导时安装 devfs ,当 /sbin/init 启动时我们仍然只会得到部分而非所有的设备节点。这就意味着为了使 devfsd 能在恰当的时间启动和运行,您必须修改系统初始化脚本。这不但棘手(因为这需要对系统启动脚本有专家级的理解),而且这种方法还存在其它问题。
 
内核安装方法的主要的问题是, devfsd 只有在能够访问原来旧类型磁盘上的 /dev 目录下的内容时,才能最好地工作。我们允许访问原来的旧类型设备的典型办法是,在 devfs 自身被安装到 /dev 之前,绑定安装 /dev 到另一个位置(通常是 /dev-state)。
 
这样就确保了即使在安装了 devfs 以后,也可以在 /dev-state 中访问旧的 /dev,这就允许 devfsd 将该目录用于持久设备存储。理解这点很重要,即如果没有绑定安装的话,/dev 里的旧内容将不可访问,因为在 /dev 安装 devfs 时实际上会覆盖它们。这就是用内核安装 devfs 存在的问题。如果 kernel 在任何其它进程能够启动之前就在 /dev 安装 devfs 文件系统的话,那么我们就没有机会执行绑定安装,/dev 的最初内容也就被完全隐藏。这很不友善,是吗?(想知道更多绑定安装的内容,请参阅本系列的 第 3 部分。
 
最佳解决方案
 
理想的情况是:我们能在 /sbin/init 一启动时就能拥有完整的设备节点(新类型 和兼容性设备), 并且有机会在安装 devfs 以前将 /dev 绑定安装到另一位置。但如何才能做到这点?
 
初始封装器
 
一个方法是添加一个内核补丁来执行从 /dev 到 /dev-state 的绑定安装。然而,尽管这完全可行,而且也确实很容易执行,手工为您安装的每个 Linux 内核打补丁仍是相当麻烦的。因此,解决 devfs 的“先有鸡还是先有蛋”问题的最好办法,可能就是使用 初始封装器。对于我们这个特别的应用而言,初始封装器就是一个 bash 脚本,它代替 /sbin/init ― 而 真正的 init 则已被重命名为 /sbin/init.system 。简而言之,初始封装器将做以下事情:
 
#!/bin/bash
 
mkdir -f /dev-state
 
mount --bind /dev /dev-state
 
mount -t devfs none /dev
 
devfsd /devexec /sbin/init.system
 
正如您所见,初始封装器所做的第一件事就是确保 /dev-state 存在。然后将 /dev 树绑定安装到 /dev-state,以便可以通过 /dev-state 目录使用 /dev 的内容。然后,在 /dev 之上安装我们的 devfs 文件,然后启动 devfsd ,以便自动在 devfs 中注册我们的兼容性设备。 最后,我们 exec 最初的 /sbin/init ,现在它已被重命名为 /sbin/init.system 。 exec 命令使 init.system 取代正在运行的 bash 进程。这意味着我们的 bash 脚本被终止,而 init.system 继承了标识符为 1 的进程,也就是 init 进程以前被占用的进程标识。当 /sbin/init.system 启动后,系统将正常引导,devfs 现在也已经 完全可操作了;通过使用初始封装器,我们不必给内核打补丁,不必修改启动脚本,也不必为只有一半可操作的 devfs 系统而伤脑筋了。
 
在我的下一篇文章中,我将指导您经历启动和运行完整版本的初始封装器的整个过程,并为您演示如何利用 devfsd 的众多强大特性。请继续阅读!
·用init封装器完成到devfs的转换(第 6 部分)准备好了吗?
 
 
在本文中,我们将完成把我们的 Linux 系统转换到 devfs,即设备文件系统。对于那些正在加入 devfs 系列的人来说,请阅读 本系列的第 4 部分,在那里面我解
释了 devfs 是如何解决内核级设备注册这一令人头疼的问题。然后请阅读 本系列的第 5 部分,在那一部分里我谈到了所有的所需步骤,您需要采用这些步骤来使得 Linux 系统与 devfs 兼容以便将系统最终转 换到 devfs。
 
 
如果还没有阅读过第 5 部分,那么在按这里所写的去做之前现在就阅读它是很重要的。 如果跳过了第 5 部分中的步骤,那么几乎可以肯定将要安装的初始化封装器将不能正确地运行,并且将最终得到一个无法引导的、需要紧急恢复的系统。这不是一件好事。然而,如果已经读过第 5 部分,那么就可以往下做了。
 
 
初始化封装器
 
 
开始
 
 
我曾经在第 5 部分结束时,介绍了初始化封装器的概念,并且解释了初始化封装器非常适合于解决若干 devfs 初始化问题的原因。无须多说,让我们逐步来看完整的初始化封装器并且研究每一部分做什么。我们将从头开始:
 
 
初始化封装器,开始部分
 
#!/bin/bash
 
# Copyright 2001 Daniel Robbins , Gentoo Technologies, Inc.
 
# Distributed under the GNU General Public License, version 2.0 or later.
 
 
trap ":" INT QUIT TSTP
 
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
 
umask 022
 
 
if [ $$ -ne 1 ]
 
then
 
exec /sbin/init.system $*
 
fi
 
 
正如您所看到的一样,由于在脚本的开始有 #!/bin/bash 语句,所以初始化封装器是一个真正的 bash 脚本。这里正好向您指出初始化封装器运行 需要 bash 2.0 或更高版本;输入 /bin/bash --version 命令来看一下 bash shell 版本是否足够新 。如果不是,可能想知道是否安装了 /bin/bash2 可执行文件。如果安装了,请将脚本的第 1 行改为 #!/bin/bash2 。
 
 
现在,让我们来阅读脚本。 trap 命令防止用户在脚本执行时中断(例如,在引导时键入 control-C)脚本。然后, export 一个合理的缺省路径并且设置缺省 umask 为 022。由于在 2.4 之前发布的一些内核中有一个会产生 umask 缺省为 0 的错误,这一错误可能会造成安全威胁,因此在引导阶段尽可能早的设置一个缺省的 umask 总不失为一个好主意。
 
接下来,碰到了第一个条件语句, if [ $$ -ne 1 ] 。 bash 将 $$ 扩展为当前正在运行的进程标识,因此可以发现我们真正想问的问题是“我们的进程标识根本不是 1 吗?”这样做有什么意义呢?如果是在引导期间,则 bash 是由内核启动的,由于 PID 1 是为 init 进程保留的,所以将总会得到 PID 为 1。如果 PID 不是 1,则知道在系统已经引导之后,正在从命令行方式运行。由于 /sbin/init 命令有双重用途,允许超级用户改变已经引导的系统的运行级别,因此这很正常。如果是这样的话,那么仅仅 exec 了原来的 /sbin/init (现在 改名为 /sbin/init.system ) 。通过使用 $* 变量来传递任何命令行参数给 init.system ,初始化封装器终止,并且 init.system 开始执行。
 
 
内核引导选项
 
 
然而,如果在引导期间 正在由内核启动封装器,则 bash 的 PID 将为 1 ,当 bash 继续执行封装器时,将跳过该条件语句。就这么提一下,以下是接下来的几行:
 
 
初始化封装器中的更多内容
 
mount -n /proc
 
devfs="yes"
 
for copt in `cat /proc/cmdline`
 
do
 
if [ "${copt%=*}" = "wrapper" ]
 
then
 
parms=${copt##*=}
 
#parse wrapper option
 
if [ "${parms/nodevfs//}" != "${parms}" ]
 
then
 
devfs="no"
 
fi
 
fi
 
done
 
 
 
 
如果运行到这一代码块,那就意味着在系统引导期间,正在启动;作为处理的第一条命令,将 /proc 安装到根文件系统,这个 /proc 当前为只读。在那之后,执行一个大而复杂的 bash 代码块,该代码块利用了一个非常便利的 Linux 特性。您可能不了解这一特性,内核允许查看 /proc/cmdline 的内容来弄清楚 LILO 或 GRUB 传给内核什么选项。在我们的开发机器中,/proc/cmdline 的内容如下所示:
 
 
 
/proc/cmdline 的内容
 
# cat /proc/cmdline
 
root=/dev/hda6 hda=89355,16,63 mem=524224K
 
 
 
 
在上面的代码中,利用已有的 /proc/cmdline,通过它来查找一个我们自己创建的、称为 wrapper 的内核引导变量。如果 wrapper=nodevfs 出现在内核引导选项中,那么该脚本知道不去启动 devfs。然而,如果这一变量没有出现在 /proc/cmdline 中,那么封装器将进行 devfs 初始化。这里的含意是说您可以通过使用 wrapper=nodevfs 内核引导选项来禁止 devfs。如果这么做的话, devfs 变量将被设置成 no ;否则它将被设置成 yes 。
 
 
封装它
 
 
下面是该封装器的剩余部分:
 
 
 
初始化封装器的剩余部分
 
if [ "$devfs" = "yes" ]
 
then
 
if [ -e /dev/.devfsd ]
 
then
 
clear
 
echo
 
echo "The init wrapper has detected that /dev has been automatically mounted by"
 
echo "the kernel. This will prevent devfs from automatically saving and"
 
echo "restoring device permissions. While not optimal, your system will still"
 
echo "be able to boot, but any perm/ownership changes or creation of new compat."
 
echo "device nodes will not be persistent across reboots until you fix this"
 
echo "problem."
 
echo
 
echo "Fortunately, the fix for this problem is quite simple; all you need to"
 
echo "do is pass the \"devfs=nomount\" boot option to the kernel (via GRUB"
 
echo "or LILO) the next time you boot.  Then /dev will not be auto-mounted."
 
echo "The next time you compile your kernel, be sure that you do not"
 
echo "enable the \"Automatically mount filesystem at boot\" devfs kernel"
 
echo "configuration option.  Then the \"devfs=nomount\" hack will no longer be"
 
echo "needed."
 
echo
 
read -t 15 -p "(hit Enter to continue or wait 15 seconds...)"
 
else
 
mount -n /dev /dev-state -o bind
 
mount -n -t devfs none /dev
 
if [ -d /dev-state/compat ]
 
then
 
echo Copying devices from /dev-state/compat to /dev
 
cp -ax /dev-state/compat/* /dev
 
fi
 
fi
 
/sbin/devfsd /dev >/dev/null 2>&1;
 
fi
 
 
exec /sbin/init.system $*
 
 
 
 
现在我们碰到了一个大的条件语句,只有在 devfs 被设置成为 yes 时,该条件语句才会执行。如果不是这样,则完全跳过 devfs 初始化,甚至不会安装 devfs。这会导致一个常见的非 devfs 引导。
 
 
然而,如果 正在安装 devfs,则进入该条件语句。在该条件语句里,检查内核是否已经安装 devfs;通过检查 /dev/.devfsd 字符设备是否存在来实现这一目的。当安装 devfs 时,内核自动创建该设备,并且随后的 devfsd 进程将使用它来与内核通信。如果已经安装了 devfs(因为用户选择了“Automatically mount devfs at boot”内核选项),将打印一条信息告诉用户:由于只有在内核 还没有 devfs 时,才可以安装 devfs 的持久性特性,现在无法完成这一任务。
 
 
设备持久性
 
 
然而,如果一切正常,则执行了在上篇文章中所谈到的 devfs 安装:/dev 被绑定安装到 /dev-state 并且 devfs 文件系统被安装在 /dev 上。然后,执行一项在上篇文章中 没有提到的一个步骤;检查目录 /dev-state/compat 是否存在并且递归地把它的内容复制到 /dev 目录。虽然这一过程最初看起来有些多余(我们将利用 devfsd 的设备持久性特性,不是吗?),但是最终会证明这一过程是必要和有用的。需要一个 compat 目录的原因在于 devfsd 的持久性特性 仅仅只能使用支持 devfs 的驱动器。
 
 
最后,启动 devfsd ,然后退出该条件语句并且 exec 实际的 init, /sbin/init.system 来开始标准的系统引导过程。除了现在需要一个支持 devfs 的系统之外,所有东西都是标准的 :)
 
 
初始化封装器安装
 
 
下面是我们如何安装初始化封装器。首先, 获取 wrapper.sh 所需资源,并且把它保存在系统的某个地方。然后,按下面所说的做:
 
 
安装初始化封装器
 
# cd /sbin
 
# cp init init.system
 
# cp /path/to/wrapper.sh init
 
# chmod +x init
 
 
现在初始化封装器安装在正确的地方了。
 
 
调整 umount
 
 
通过使用初始化封装器,避免了编制大量复杂的启动脚本来进行调整。不过,我们可能还是不能避免 一个调整。既然,我们将 devfs 安装在 /dev,则 rc 脚本卸载根文件系统将可能会非常困难。幸运的是,有一个简便方法可以解决这一问题。只需要输入 cd /etc/rc.d; grep -r umount * 或 cd /etc/init.d; grep -r umount * 来 grep rc 脚本目录中所有出现 umount 的地方,具体输入哪条命令取决于 rc 脚本安装在什么地方。然后,在每个引用 umount 的脚本里,请确保调用时带有选项 -r 。虽然在各处使用 umount -r 仍然会起作用,但这一特定的 umount 的命令对于卸载根文件系统是十分重要的:)
 
 
-r 选项告诉 umount ,如果没能成功卸载文件系统,就以只读方式重新安装。对于把根文件系统设置到一致状态来说,这已经足够了,而且即便根文件系统由于在 /dev 上的有一个安装而无法卸载(由于无法卸载打开的设备节点),这也为根文件系统重新引导做好了准备。
 
 
现在,我们 几乎为重新引导做好了准备;但是在重新引导之前,让我们来看一下 devfsd 并且修改 /etc/devfsd.conf 以便支持兼容性设备和设备持久性。不用担心,我们离完成转换到 devfs 只有一步之遥。
 
 
 
devfsd.conf
 
 
用您喜爱的编辑器打开 /etc/devfsd.conf。下面是我推荐的 devfsd.conf 中的头四行:
 
 
devfsd.conf,开始部分
 
REGISTER        .*              MKOLDCOMPAT
 
UNREGISTER      .*              RMOLDCOMPAT
 
REGISTER        .*              MKNEWCOMPAT
 
UNREGISTER      .*              RMNEWCOMPAT
 
 
上面四行中的每一行都含有一个 事件( REGISTER 或 UNREGISTER ),一个正则表达式( .* )以及一项操作( *COMPAT 字符串)。那么,它们都表示什么意思呢?第 1 行告诉 devfsd ,当有内核中注册 任何设备( .* 是表示匹配 任何设备的正则表达式)时,执行 MKOLDCOMPAT 操作。 MKOLDCOMPAT 操作是 devfsd 内置的,它的含义是“创建任何与正在通过 devfs 注册的设备相对应的旧兼容设备”。正如您可能想到的一样,在删除设备时,会运行 RM*COMPAT 操作,这使得这些特定的兼容设备魔术般的消失。总的来说,这四行语句告诉 devfsd ,当设备注册时,创建兼容设备(如果有的话),当解除设备注册时,删除兼容设备。多亏了这几行,当 IDE 设备驱动器在系统中注册 /dev/ide/host0/bus0/target0/lun0/disc devfs 样式的设备时, devfs 自动创建一个匹配 /dev/hda 兼容样式的设备。这对于诸如 mount 和 fsck 命令来说,是极其有帮助的,这些命令可能会读一个包含旧样式设备的 /etc/fstab 。一般来说,创建兼容设备使得 devfs 的转换成了一个无缝转换。devfsd.conf 中的下一行是:
 
 
自动装载模块
 
 
 
devfsd.conf,续上
 
LOOKUP          .*              MODLOAD
 
 
这一项告诉 devfsd ,无论什么时侯“查看”任何设备( .* ),就执行 MODLOAD 操作,这是指当程序查找特定设备节点是否存在时所发生的操作。 MODLOAD 操作将导致执行 modprobe /dev/mydev ,这里的 /dev/mydev 是特定进程所要查找的设备名。多亏了这一特性(以及正确配置的 /etc/modules.conf),当启动音乐播放器或其它美妙的东西时,可能可以按照需要自动装入声卡驱动器。
 
 
设备持久性
 
 
以下是 devfsd.conf 中的接下来的几行:
 
 
devfsd.conf,续上
 
REGISTER        ^pt[sy]/.*      IGNORE
 
CHANGE          ^pt[sy]/.*      IGNORE
 
REGISTER        .*              COPY    /dev-state/$devname $devpath
 
CHANGE          .*              COPY    $devpath /dev-state/$devname
 
CREATE          .*              COPY    $devpath /dev-state/$devname
 
 
这几行告诉 devfsd 使用 /dev-state 作为用于设备许可权和所有权变更以及任何用户可以创建的兼容设备的资源库。在头两行中,显式地告诉 devfsd ,当内核中注册了任何伪终端设备或当它们的属性被更改时,不要执行任何特殊的操作。如果没有这几行,则在重引导系统之后,仍然会保留伪终端的许可权和所有权。那样做不太理想,因为应该总是在系统启动之后给予伪终端一套新的缺省许可权。
 
 
接下来的三行为所有其它设备打开 /dev-state 持久性。特别地,注册设备或 devfsd 自己启动时,将 从 /dev-state 恢复设备的任何属性(以及复制给任何现有的兼容设备),并且我们将立即 备份属性的任何更改,以及将任何新兼容设备创建到 /dev-state 中去。
 
 
CFUNCTION 和符号链接
 
 
给出以下几行,最终完成 devfsd.conf:
 
 
 
devfsd.conf,结束
 
REGISTER        ^cdrom/cdrom0$          CFUNCTION GLOBAL symlink cdroms/cdrom0 cdrom
 
UNREGISTER      ^cdrom/cdrom0$          CFUNCTION GLOBAL unlink cdrom
 
REGISTER        ^misc/psaux$            CFUNCTION GLOBAL symlink misc/psaux mouse
 
UNREGISTER      ^misc/psaux$            CFUNCTION GLOBAL unlink mouse
 
 
这最后四行是可选的,但它们也值得一看。虽然对于设备节点,/dev-state 持久性工作得非常好,但对符号链接却根本不起任何作用,它会忽略符号链接。因此,这就产生一个问题:人们怎么确保 /dev/mouse 或 /dev/cdrom 符号链接不仅存在,而且在重新引导系统之后它还是存在的呢?幸运的是, devfsd 可配置性非常好,这四行(或类似这样的,可以定制你的特定系统)将完成这一任务。头两行告诉 devfsd ,当注册 /dev/cdrom/cdrom() 时,使 /dev/cdrom 符号链接出现。为了做到这一点, devfsd 实际上执行指定的 libc 函数的动态调用,这里是 symlink() 和 unlink() 。该文件的最后两行使用相同的方法,在 /dev/misc/psaux(PS/2 鼠标)设备注册到 devfs 时,创建 /dev/mouse 符号链接。根据 您的系统来定制这几行,然后保存该文件。如果您愿意,可以 下载这个 devfsd.conf 文件,用在您自己的系统上。
 
 
重新引导之前的注意事项
 
 
在重新引导之前,您可能想看一下 Richard Gooch 的 devfs FAQ;您可能会找到关于 devfs 命名方案的信息,这些信息对于熟悉新风格设备名是非常有帮助的(请参阅下面的 参考资料)。我还建议您打印一份 本系列第 5 部分,以备您在解决与引导相关的问题时,能利用“紧急 bash 抢救”指导。记住,如果初始化封装器因为某种原因而崩溃,请遵循我的紧急抢救指导,重新安装根文件系统为读/写,然后执行下面步骤,这总是可以除去它的:
 
 
 
如果需要,请返回至使用封装器前的状态
 
# cd /sbin
 
# mv init wrapper.sh
 
# mv init.system init
 
 
在执行完这些步骤,并将文件系统重新安装成只读,然后,重新引导之后,系统将返回到使用预封装器前的状态。现在继续,重新引导,体验一下 devfs!
·Ext3 文件系统的好处(第 7 部分)
在前面几部分中,我们花费了一些精力去研究非传统文件系统(譬如 tmpfs 和 devfs)。现在,是时候回到基于磁盘的文件系统上来了,我们将通过研究 ext3 来实现这个目的。ext3 文件系统(由
Stephen Tweedie 博士设计)构建在现有的 ext2 文件系统的框架上;实际上,除了一个微小(但重要)的区别 ― ext3 支持日志记录以外,ext3 和 ext2 非常相似。但正是因为具有了这个小小的增加,您会发现 ext3 具有几种令人惊讶和富有吸引力的能力。在本文中,我将让您充分了解与当前可用的其它日志记录文件系统相比,ext3 有哪些优缺点。在我的下一篇文章中,我们将设置和运行 ext3。
 
 
理解 Ext3
 
 
那么,与 ReiserFS 相比,ext3 到底如何呢?在以前的文章中,我解释了 ReiserFS 是如何充分适合处理小文件的(4K 以下),并且,在某些情况下,ReiserFS 处理小文件的能力比 ext2 和 ext3 强十到十五 倍。但尽管 ReiserFS 有许多长处,它还是有弱点。在当前的 ReiserFS(版本 3.6)实现中,与 ext2 和 ext3 相比,尤其是读取大的邮件目录时,特定文件访问模式实际上可能导致 特别糟糕的性能。还有,ReiserFS 没有好的 NFS 兼容性跟踪记录,同时稀疏文件性能也较差。 相反,ext3 是一个非常 全面的文件系统。ext3 很象 ext2;它不会为您提供象 ReiserFS 那样特别快的小文件性能,但是,它也不会给您带来意外的性能或功能性瓶颈。
 
 
ext3 最妙的特性之一是:因为 ext3 基于 ext2 的代码,所以它的磁盘格式和 ext2 的相同;这意味着,一个干净卸装的 ext3 文件系统可以作为 ext2 文件系统毫无问题地重新挂装。并且不仅如此。应该感谢 ext2 和 ext3 都使用相同的元数据,因而有可能执行 ext2 到 ext3 文件系统的现场升级。是的,您的理解是正确的。通过升级一些关键系统实用程序、安装新的 2.4 内核,并在每个文件系统上输入单条 tune2fs 命令,就可以把现有的 ext2 服务器转换成日志记录 ext3 系统。甚至可以在 ext2 文件系统 已挂装的情况下进行这些操作。转换是安全的、可逆的、并且令人难以置信地简单,和到 XFS、JFS 或 ReiserFS 的转换不同,您不必备份和从头创建文件系统。现在花一些时间思考一下,有数以千计的 ext2 生产服务器,只要几分钟时间就能升级到 ext3;那么,您就会充分理解 ext3 对于 Linux 社区的重要性了。
 
 
如果非要用一个词来描述 ext3,我会说“舒适”。在已有的 ext2 系统上安装启用 ext3 的过程轻松得令人难以置信,并且升级以后,也不会导致任何意外的性能急剧下降。并且,ext3 在舒适方面还有一个优点,那就是,ext3 恰巧又是 Linux 可用的最 可靠的日志记录文件系统之一,我将在下面解释这一点。
 
 
Ext3 可靠性
 
 
除了与 ext2 兼容之外,ext3 还通过共享 ext2 的元数据格式继承了 ext2 的其它优点。譬如,ext3 用户可以使用一个稳固的 fsck 工具。您会回想起使用日志记录文件系统的要点之一是首先避免对彻底的 fsck 的需求,但是如果您确实要从脆弱的内核、坏的硬盘或者别的什么地方获得毁坏的元数据,您将非常感激 ext3 从 ext2 继承了 fsck 这个事实。相反,ReiserFS 的 fsck 还很幼稚,当脆弱的元数据 真的出现时,对脆弱元数据的修复过程将是困难和危险的。
 
 
仅元数据日志记录
 
 
有趣的是,ext3 处理日志记录的方式与 ReiserFS 和其它日志记录文件系统所用的方式迥异。使用 ReiserFS、XFS 和 JFS 时,文件系统驱动程序记录 元数据,但不提供 数据日志记录。使用 仅元数据日志记录,您的文件系统元数据将会异常稳固,因而可能永远不需要执行彻底 fsck。然而,意外的重新引导和系统锁定可能会导致最近修改 数据的明显毁坏。Ext3 使用一些创新的解决方案来避免这些问题,我们将对此做稍微深入的研究。
 
 
但首先,重要的是确切理解仅元数据日志记录最终是如何危害您的。举例来说,假设您正在修改名为 /tmp/myfile.txt 的文件时,机器意外锁定,被迫需要重新引导。如果您使用的是仅元数据日志记录文件系统,譬如 ReiserFS、XFS 或者 JFS,文件系统元数据将容易地修复,这要感谢元数据日志,您不必耐着性子等待艰苦的 fsck 了。
 
 
但是,存在一种明显的可能性:在将 /tmp/myfile.txt 文件装入到文本编辑器时,文件不仅仅丢失最近的更改,而且还包含许多乱码甚至可能完全不可读的信息。这种情况并不总会发生,但它 可能并且经常发生。
 
 
下面解释原因。典型的日志记录文件系统(譬如 ReiserFS、XFS 和 JFS)对元数据有特别处理,但是对数据不够重视。在上述示例中,文件系统驱动程序处于修改一些文件系统块的过程中。文件系统驱动程序更新适当的元数据,但是没有时间将其缓存中的数据刷新到磁盘的新块中。因此,当您将 /tmp/myfile.txt 文件装入文本编辑器时,文件的部分或全部包含乱码 ― 在系统锁定之前来不及初始化的数据块。
 
 
ext3 方法
 
 
既然我们对这个问题已经有了一个总的很好的理解,让我们来看 ext3 是如何实现日志记录的。在 ext3 里,日志记录代码使用一个特殊的称为“日志记录块设备”层或 JBD 的 API。JBD 被设计成在任何块设备上实现日志的特殊目的。Ext3 通过“钩入(hooking in)”JBD API 来实现其日志记录。例如,ext3 文件系统代码将正在执行的修改告知 JBD,并且还会在修改磁盘上包含的特定数据之前请求 JBD 的许可。通过执行这些操作,给予了 JBD 代表 ext3 文件系统驱动程序管理日志的适当机会。这是很好的安排,因为 JBD 是作为一个单独的、一般实体而开发的,将来它可以用于向其它文件系统添加日志记录能力。
 
 
关于 JBD 管理的 ext3 日志有一些巧妙的特性。其中之一是,ext3 的日志存储在一个索引节点中 ― 基本上是个文件。能否看到这个位于 /.journal 的文件,取决于您是如何在文件系统上“启用 ext3”的。当然,通过将日志存储在索引节点中,ext3 可以向文件系统添加必要的日志,而不需要对 ext2 元数据进行不兼容扩展。 这是 ext3 文件系统保持对 ext2 元数据,以及 ext2 文件系统驱动程序的向后兼容性的关键方式之一。
 
 
不同的日志记录方法
 
 
不必惊讶,确实有许多方法用于实现日志。例如,文件系统开发者可能会设计出一种日志,该日志存储在主机文件系统上需要修改的 字节范围。这种方法的好处在于,日志能够以一种非常高效的方式存储许多对文件系统的微小修改,这是因为它只记录需要修改的个别字节,而不记录除此以外的任何信息。
 
 
JBD 使用另外一种(从某种意义来说是更好的)方法。JBD 存储完整的被修改的文件系统块本身,而不是记录必定会被更改的字节范围。ext3 文件系统驱动程序也使用这种方法,存储内存中被修改的块(大小为 1K、2K 或 4K)的完整副本,以跟踪暂挂的 IO 操作。开始,这看起来有点浪费。毕竟,包含已修改数据的完整块中还可能包含 未修改的(已经在磁盘上)数据。
 
 
JBD 所使用的方法称为 物理日志记录,这意味着 JBD 使用完整的物理块,作为实现日志的主要媒介。相反,只存储已修改的字节范围而非完整块的方法称为 逻辑日志记录,这是 XFS 所使用的方法。因为 ext3 使用物理日志记录,所以 ext3 日志 将具有比其它文件系统日志(例如,XFS 日志)更大的相对磁盘占用。但是,因为 ext3 在文件系统内部和日志中使用完整块,ext3 处理的复杂度比实现逻辑日志记录的要小。另外,完整块的使用允许 ext3 执行一些额外的优化,譬如,将多个暂挂的 IO 操作“压扁”到同一内存数据结构的单个块中。 接下来,这种优化允许 ext3 将这多个更改在一次写操作中写到磁盘上,而不需要多次写操作。此外,因为文字块数据存储在内存中,这些内存数据在写到磁盘之前,不必或只需作很少更改,大大减少了 CPU 开销。
 
 
Ext3,数据保护者
 
 
现在,我们最后来了解一下 ext3 文件系统是如何高效地提供元数据 和数据日志记录,以避免在本文前面部分所描述的数据毁坏问题的。实际上,ext3 有两种确保数据和元数据完整性的方法。
 
 
最初,ext3 被设计用来执行完整数据和元数据日志记录。在这种方式下(称之为“data=journal”方式),JBD 将所有对数据和元数据的更改都记录到文件系统中。因为数据和元数据都被记录,JBD 可以使用日志将元数据 和数据恢复到一致状态。完整数据日志记录的缺点是它可能会比较慢,但可以通过设置相对较大日志来减少性能损失。
 
 
最近,ext3 添加了一种新的日志记录方式,该方式提供完整日志记录的好处而不会带来严重的性能损失。这种新方式只对元数据进行日志记录。但是,ext3 文件系统驱动程序保持对与每个元数据更新对应的特殊 数据块的跟踪,将它们分组到一个称为事务的实体中。当事务应用于适当的文件系统时,数据块首先被写到磁盘。一旦写入数据块,元数据将随后写入日志。通过使用这种技术(称为“data=ordered”方式),即使只有元数据更改被记录到日志中,ext3 也能够提供数据和元数据的一致性。ext3 缺省使用这种方式。
 
 
结束语
 
 
最近,有许多人在尝试确定哪种 Linux 日志记录文件系统是“最好的”。实际上,没有一个针对每个应用程序都“合适的”文件系统,每个文件系统都有自身的长处。这是有这么多下一代 Linux 文件系统供选择的好处之一。所以,理解每种文件系统的长处和弱点,以便对使用哪种文件系统作出一个有根据的选择,远远优于选出一个绝对的“最好的”文件系统,并将它用于所有可能的应用程序。
 
 
Ext3 具有许多长处。它被设计得极易部署。它基于稳固的 ext2 文件系统代码,并继承了一个很好的 fsck 工具。还有,ext3 的日志记录能力经过特别设计,以确保元数据和数据的完整性。总之,ext3 确实是一个很棒的文件系统,并且是现在仍受到推崇的 ext2 文件系统的一个合格的继承者。请关注我的下一篇文章,那时我们将设置和运行 ext3。在那之前,您可能会需要查看下列参考资料。
·Ext3 和最新内核更新(第 8 部分)
我实话实说。我本来计划用这篇文章向您展示如何在系统上设置和运行 ext3。尽管我这么说,但我并不打算这么做。Andrew Morton 那个很棒的“在 2.4 内核中
使用 ext3 文件系统”页面(请参阅本文后面的 参考资料)已经很好地解释了如何在系统上启用 ext3,因此我没必要在这里重复所有基本概念。而是深入钻研某些更耐人寻味的 ext3 主题,我认为您会发现这些主题非常有用。阅读了这篇文章后,当您准备设置和运行 ext3 时,请参考 Andrew 的页面。
 
2.4 内核更新
 
首先,让我们从 2.4 内核更新开始。我在介绍 ReiserFS 时最后讨论到 2.4 内核稳定性。在那个时候,要找到一个稳定的 2.4 内核是个难题,我建议坚持使用已知的内核(那时最前沿的是 2.4.4-ac9 内核)― 特别对于计划在生产环境中使用 ReiserFS 文件系统的那些人。您可能猜到,自从 2.4.4-ac9 以来发生了许多事,而现在无疑是着眼于更新的内核的时候了。
 
随着内核 2.4.10 的出现,在性能和可伸缩性方面,2.4 系列达到了新的层次(这是我们盼望已久的)。那么,是什么碰巧让 Linux 2.4 最终成长起来的呢?用首字母缩写词表示,就是 VM。在认识到 2.4 系列表现不是非常出色后,Linus 去除了 Linux 有问题的 VM 代码,并用 Andrea Archangeli 开发的简单而普通的 VM 实现来替代。Andrea 的新 VM 实现(最先出现在 2.4.10 中)非常了不起;它真正提高了内核速度,并使整个系统反应更迅速。2.4.10 绝对是 2.4 Linux 内核开发中一个主要的转折点;到那时为止,事物还不是非常完美,我们当中有许多人都奇怪为什么自己不是 FreeBSD 开发人员。我们都应该感谢 Linus 在 2.4 稳定内核系列中所做的这些主要(但迫切需要)的更改。这的确是一件了不起的事。
 
因为 Andrea 的新 VM 代码需要一些时间才能与内核其余部分达到无缝集成,所以使用 2.4.13+。使用 2.4.16+ 会更好,因为稳固的 ext3 文件系统代码最终是集成到从 2.4.15-pre2 发行版开始的正式 Linus 内核中的。没有理由避免使用 2.4.16+ 内核,它能使设置和运行 ext3 的工作更为轻松。如果确实使用 2.4.16+ 内核,记住不再需要象 Andrew 的页面上所描述的那样应用 ext3 补丁了(请参阅 参考资料)。Linus 已经为您添加了它 :)
 
您会注意到我建议使用 2.4.16+ 而非 2.4.15+,这是有充分理由的。随着内核 2.4.15-pre9 的发行,给内核引入一个非常讨厌的文件系统崩溃错误。直到 2.4.16-pre1 才发现并解决了这个问题,导致人们应该不计任何代价地避免使用这两个发行版之间的内核(包括 2.4.15)。选择 2.4.16+ 内核可以让您完全避免这个错误的批处理。
 
膝上电脑……小心?
 
Ext3 是一种稳固的文件系统,这个好名声由来已久,因此我在知道有相当多的膝上电脑用户在切换到 ext3 时有文件系统崩溃问题时很诧异。一般来说,对这些类型的报告做出的反映往往是完全避免使用 ext3;不过,在到处打听之后,我发现人们所遇到的磁盘崩溃问题与 ext3 本身并没有关系,而是由某些膝上电脑硬盘所引起的。
 
写高速缓存
 
您可能不知道,但大多数新式硬盘有一个称为“写高速缓存”的东西,硬盘使用它来收集暂时被挂起的写操作。通过将暂挂的写操作放到高速缓存中,硬盘固件就可以重新为它们排序,并为它们分组,以便用尽可能最快的方式将它们写到磁盘中。通常认为写高速缓存是个非常好的事物(请阅读 参考资料中 Linus 对写高速缓存的解释和看法)。
 
不幸的是,现在市场上某些膝上电脑的硬盘具有这样一种不可靠的特性,即忽略将它们的写高速缓存刷新到磁盘上的所有正式 ATA 请求。虽然直到最近 ATA 规范 还允许这种做法,但这并不是一种完美的设计特性。使用这些类型的驱动器,内核无法保证某一特定块实际上是否被记录到磁盘上。尽管听上去这是个棘手的问题,但这个特定问题本身可能不是人们所遇到的数据毁坏问题的原因。
 
不过,情况越来越差。一些新式膝上电脑的硬盘有一个更令人讨厌的习惯:每当重新引导或暂挂系统,就 丢弃它们的写高速缓存。很明显,如果硬盘有这两个问题,就会定期破坏数据,而 Linux 对于防止这种情况束手无策。
 
那么,解决方案是什么呢?如果您有膝上电脑,请小心使用。在对文件系统执行任何重大更改之前备份所有重要文件。如果遇到似乎符合上面我所描述的模式的数据毁坏问题,特别在使用 ext3 时,记住这可能是您膝上电脑硬盘的错。在这种情况下,您需要与膝上电脑制造商联系,并询问更换驱动器事宜。希望在几个月后,这些脆弱的硬盘将从市场上消失,我们就再也不必担心这个问题了。
 
既然我已经警告过您要小心,就让我们看看 ext3 的各种数据日志记录选项。
 
日志记录选项和写等待时间
 
在安装文件系统时,Ext3 允许您从三种数据日志记录方式中选择一个: data=writeback 、 data=ordered 和 data=journal 。
 
要指定日志方式,可以向 /etc/fstab 的选项节添加适当的字符串(例如 data=journal ),也可以在调用 mount 时直接指定 -o data=journal 命令行选项。如果您愿意指定用于根文件系统的数据日志记录方法( data=ordered 是缺省值),则可以使用名为 rootflags 的特殊内核引导选项。因此,如果愿意将根文件系统置于完整数据日志记录方式下,则向内核引导选项添加 rootflags=data=journal 。
 
data=writeback 方式
 
处于 data=writeback 方式下,ext3 根本不执行任何形式的数据日志记录,提供给您的是和在 XFS、JFS 和 ReiserFS 文件系统中找到的类似的日志记录(仅元数据)。正如我在 前一篇文章中讲到过,这会让最近修改的文件在出现意外的重新引导事件中被毁坏。如果不考虑这个缺点, data=writeback 方式在大多数情况下应该能够为您提供最佳的 ext3 性能。
 
data=ordered 方式
 
处于 data=ordered 方式下,ext3 只是正式记录元数据,而在逻辑上将元数据和数据块分组到称为事务的单个单元中。到了将新的元数据写到磁盘上的时候, 首先写的是相关的数据块。 data=ordered 方式有效地解决了在 data=writeback 方式 下和大多数其它日志记录文件系统中发现的毁坏问题,而这是在不需要完整数据日志记录的情况下做到的。一般说来, data=ordered ext3 文件系统执行的速度比 data=writeback 文件系统执行的速度稍微慢一些,但比对应的完整数据日志记录还是要快出许多。
 
将数据 附加到文件时, data=ordered 方式提供了 ext3 完整数据日志记录方式提供的所有完整性保证。不过,如果正在 覆盖某一部分文件,而此时系统崩溃,那么有可能所写的区将包含原始块和在其中散布了更新块的组合。这是因为 data=ordered 不提供首先覆盖哪一个数据块的保证,因此不能假设只是因为更新了被覆盖的块 x,也就更新了被覆盖的块 x-1。 data=ordered 让写操作顺序由硬盘的写高速缓存决定。一般说来,这个限制并不经常对人们具有负面影响,因为附加的文件一般比覆盖的文件更普遍。出于这个原因, data=ordered 方式是对完整数据日志记录的一个很好的更高性能的替代。
 
data=journal 方式
 
data=journal 方式提供了完整数据和元数据日志记录。所有新数据首先写入日志,然后再写入它的最终位置。在崩溃情况下,可以重放日志,使数据和元数据处于一致的状态。
 
从理论上说, data=journal 方式是所有日志记录方式中最慢的,因为要将数据写入磁盘两次而不是一次。不过,在某些情况下, data=journal 方式也可以是极快的。Andrew Morton 在听取了有关 LKML 的报告(ext3 data=journal 文件系统为人们提供了难以置信的出色的交互式文件系统性能)后,决定组合出一个小测试。首先,他创建了一个简单的 shell 脚本,该脚本设计用来将数据尽快写入测试文件系统:
 
快速写
while true
do
dd if=/dev/zero of=largefile bs=16384 count=131072
done
 
在将数据写入测试文件系统的同时,他尝试从 位于同一磁盘上的另一个 ext2 文件系统中读取 16Mb 的数据,并对此进行计时:
 
读取 16Mb 的文件
timecat 16-meg-file > /dev/null
 
结果让人惊奇。 data=journal 方式允许 16 兆文件以比其它 ext3 方式、ReiserFS,甚至 ext2(没有日志记录开销)高出 9 到 13 倍的速度读取:
 
写入文件系统  16 兆读取时间(秒)
ext2 78
ReiserFS 67
ext3 data=ordered  93
ext3 data=writeback  74
ext3 data=journal  7
 
Andrew 重复这个测试,但尝试从测试文件系统(而不是从其它文件系统)读取 16Mb 的文件,获得的结果是相同的。那么,这意味着什么呢?不知什么原因,ext3 的 data=journal 方式非常适合于需要同时从磁盘读写数据的情况。 因此,ext3 的 data=journal 方式(被认为在几乎所有情况中是所有 ext3 方式中最慢的)实际上证明在需要最大化交互式 IO 性能的繁忙环境中具有重要的性能优势。可能 data=journal 方式毕竟没那么缓慢!
 
Andrew 仍然在尝试发现究竟为什么 data=journal 方式比其它方式好这么多。在这样做的同时,他也许能够对 ext3 的另外两种方式做必要调整,以便也能看到 data=writeback 和 data=ordered 方式的好处。
 
对 data=journal 的调整
 
有些人在繁忙的服务器上 ― 特别是在繁忙的 NFS 服务器上 ― 使用 ext3 的 data=journal 方式时曾经碰到一个特殊的性能问题。每隔 30 秒,服务器就会遇到磁盘写活动高峰,导致系统几乎陷于停顿。如果您遇到这个问题,修复它很容易。只要以 root 用户输入以下命令,就可以调整 Linux“脏”缓冲区刷新算法:
 
调整 bdflush
echo 40 0 0 0 60 300 60 0 0 > /proc/sys/vm/bdflush
 
这些新的 bdflush 设置将导致 kupdate 每隔 0.6 秒而不是每隔 5 秒运行。另外,它们告诉内核每隔 3 秒而不是 30 秒(缺省值)刷新“脏”缓冲区。通过更有规律地将最近修改的数据刷新到磁盘,可以避免这些写操作的高峰。以这种方式执行的效率比较低,因为内核不太有机会组合写操作。但对于繁忙的服务器,写操作将更一致地进行,并将极大地改进交互式性能。
 
结束语
 
现在,我们已经结束了 ext3 的报道。敬请期待我的下一篇文章,因为我们将要研究……XFS 的许多奇妙之处!
·XFS简介(第 9 部分)
在本文中,我们将研究 XFS ― 用于 Linux 的 SGI 的免费、64 位高性能文件系统。首先,我将说明,XFS 与 ext3 和 ReiserFS 相比,各有什么优缺点,并描述许多 XFS
内部使用的技术,然后,在 下一篇文章中,我将全程指导您在自己的系统上设置 XFS,并讲述了 XFS 调优技巧和诸如 ACL(访问控制表)及扩展属性支持之类有用的 XFS 特性。
 
 
XFS 简介
 
 
XFS 最初是由 Silicon Graphics,Inc. 于 90 年代初开发的。那时,SGI 发现他们的现有文件系统(existing filesystem,EFS)正在迅速变得不适应当时激烈的计算竞争。为解决这个问题,SGI 决定设计一种全新的高性能 64 位文件系统,而不是试图调整 EFS在先天设计上的某些缺陷。因此,XFS 诞生了,并于 1994 年随 IRIX 5.3 的发布而应用于计算。它至今仍作为 SGI 基于 IRIX 的产品(从工作站到超级计算机)的底层文件系统来使用。现在,XFS 也可以用于 Linux。XFS 的 Linux 版的到来是激动人心的,首先因为它为 Linux 社区提供了一种健壮的、优秀的以及功能丰富的文件系统,并且这种文件系统所具有的可伸缩性能够满足最苛刻的存储需求。
 
 
XFS、ReiserFS 和 ext3 的性能
 
 
到目前为止,选择合适的下一代 Linux 文件系统一直很简单。那些只寻求原始性能的人通常倾向于使用 ReiserFS,而那些更关心数据完整性特性的人则首选 ext3。然而,随着 XFS 的 Linux 版的发布,事情突然变得令人困惑。尤其是,对于 ReiserFS 是否依然是下一代文件系统性能方面的佼佼者,人们开始感到疑惑。
 
 
最近,我进行了一系列测试,试图比较 XFS、ReiserFS 和 ext3 在原始性能方面的优劣。在与您分享该结果之前,理解以下事实很重要:该结果只着重比较了在单处理器系统上,系统负载较轻的情况下,常规文件系统的性能趋势,它并 不是衡量某一个文件系统是否比另一个文件系统“更好”的绝对尺度。尽管如此,我的结果应该可以帮助您形成一些概念,那就是:哪个文件系统可能最适于某个特定任务。再次声明,不应该将我的结果视为结论性的;最好的测试总是:在每个文件系统上运行您的特定应用程序,以观察它是如何执行的。
 
 
结果
 
 
在测试中,我发现 XFS 通常是相当快的。在大文件操作方面,XFS 在所有测试中一直处于领先地位,这是意料之中的,因为其设计者花了数年时间设计和调整它,以便能够极出色地完成此类任务。我还发现 XFS 有一个单点性能缺陷:它删除文件不是很快;在这一方面,ReiserFS 和 ext3 轻易地胜过了它。据 Steve Lord(SGI 的文件系统软件总工程师)说,刚编写完一个补丁来解决该问题,并且不久将可以使用该补丁。
 
 
除此以外,XFS 的性能非常接近 ReiserFS,并在大多数测试指标上都超过了 ext3。XFS 最佳表现之一在于:象 ReiserFS 一样,它不产生不必要的磁盘活动。XFS 设法在内存中缓存尽可能多的数据,并且,通常仅当内存不足时,才会命令将数据写到磁盘时。当它将数据清仓(flushing)到磁盘时,其它 IO 操作在很大程度上似乎不受影响。相反,在 ext3(“data=ordered”缺省方式)下,将数据清仓到磁盘时,将导致许多额外寻道,甚至还会引起某种不必要的磁盘抖动(thrashing)(取决于 IO 负载)。
 
 
我的性能和调整测试主要是关于将 RAM 磁盘中未压缩的内核源文件 tar 包(tarball)抽取到要测试的文件系统,然后递归地将新源文件树复制到同一文件系统中的一个新目录中。XFS 对这类任务执行得很好,尽管,最初 XFS 性能比 ReiserFS 略差一点。然而,在调整了测试 XFS 文件系统的 mkfs.xfs 和 mount 选项以后,当处理诸如在内核源文件树中的中等大小的文件时,XFS 执行效率比 ReiserFS 略好一点。但这不包括删除操作;至少目前,ReiserFS 和 ext3 删除文件要比 XFS 快得多。
 
 
性能总结
 
 
XFS 在哪些方面可以给您提供哪种性能,对于这一点,希望我的测试结果有助于您形成总的概念;我的测试结果显示,如果需要操作大文件,XFS 文件系统是您最好的选择。对于小文件和中等大小的文件,如果您使用一些能够增强性能的选项创建和挂装 XFS 文件系统的话,它可以与 ReiserFS 匹敌,有时甚至比 ReiserFS 更快。在“data=journal”方式下的 ext3 提供了良好性能,但是它很难获得一致的性能数据,原因在于,ext3 将先前测试中的数据清仓到磁盘所使用的方式,具有明显的不规律性,这将导致某种磁盘抖动。
 
 
XFS 设计
 
 
在 USENIX ‘96 上刊载的文章“Scalability in the XFS Filesystem”中(请参阅本文后面的 参考资料),SGI 工程师解释:他们设计 XFS 的主要思想只有一个,那就是:“考虑大东西”。确实,XFS 的设计消除了传统文件系统中的一些限制。现在,让我们研究 XFS 幕后一些有趣的设计特性,正是这些设计特性使这一点成为可能。
 
 
分配组(allocation groups)简介
 
 
当创建 XFS 文件系统时,底层块设备被分割成八个或更多个大小相等的线性区域(region)。您可以将它们想象成“块”(chunk)或者“线性范围(range)”,但是在 XFS 术语中,每个区域称为一个“分配组”。分配组是唯一的,因为每个分配组管理自己的索引节点(inode)和空闲空间,实际上,是将这些分配组转化为一种文件子系统,这些子系统正确地透明存在于 XFS 文件系统内。
 
 
分配组与可伸缩性
 
 
那么,XFS 到底为什么要有分配组呢?主要原因是,XFS 使用分配组,以便能有效地处理并行 IO。因为,每个分配组实际上是一个独立实体,所以内核可以 同时与多个分配组交互。如果不使用分配组,XFS 文件系统代码可能成为一种性能瓶颈,迫使大量需求 IO 的进程“排队”来使索引节点进行修改或执行其它种类的元数据密集操作。多亏了分配组,XFS 代码将允许多个线程和进程持续以并行方式运行,即使它们中的许多线程和进程正在同一文件系统上执行大规模 IO 操作。因此,将 XFS 与某些高端硬件相结合,您将获得高端性能而不会使文件系统成为瓶颈。分配组还有助于在多处理器系统上优化并行 IO 性能,因为可以同时有多个元数据更新处于“在传输中”。
 
 
 
 
B+ 树无处不在
 
 
分配组在内部使用高效的 B+ 树来跟踪主要数据,譬如空闲空间的范围和索引节点。实际上,每个分配组使用 两棵 B+ 树来跟踪空闲空间;一棵树按空闲空间的大小排序来存储空闲空间的范围,另一棵树按块设备上起始物理位置的排序来存储这些区域。XFS 擅长于迅速发现空闲空间区域,这种能力对于最大化写性能很关键。
 
 
当对索引节点进行管理时,XFS 也是很有效的。每个分配组在需要时以 64 个索引节点为一组来分配它们。每个分配组通过使用 B+ 树来跟踪自己的索引节点,该 B+ 树记录着特定索引节点号在磁盘上的位置。您会发现 XFS 之所以尽可能多地使用 B+ 树,原因在于 B+ 树的优越性能和极大的可扩展性。
 
 
日志记录
 
 
当然,XFS 也是一种日志记录文件系统,它允许意外重新引导后的快速恢复。象 ReiserFS 一样,XFS 使用逻辑日志;即,它不象 ext3 那样将文字文件系统块记录到日志,而是使用一种高效的磁盘格式来记录元数据的变动。就 XFS 而言,逻辑日志记录是很适合的;在高端硬件上,日志经常是整个文件系统中争用最多的资源。通过使用节省空间的逻辑日志记录,可以将对日志的争用降至最小。另外,XFS 允许将日志存储在另一个块设备上,例如,另一个磁盘上的一个分区。这个特性很有用,它进一步改进了 XFS 文件系统的性能。
 
 
 
象 ReiserFS 一样,XFS 只对元数据进行日志记录,并且在写元数据之前,XFS 不采取任何专门的预防措施来确保将数据保存到磁盘。这意味着,使用 XFS(就象使用 ReiserFS)时,如果发生意外的重新引导,则最近修改的数据有可能丢失。然而,XFS 日志有两个特性使得这个问题不象使用 ReiserFS 时那么常见。
 
 
使用 ReiserFS 时,意外重新引导可能导致最近修改的文件中包含先前删除文件的部分内容。除了数据丢失这个显而易见的问题以外,理论上,这还可能引起安全性威胁。相反,当 XFS 日志系统重新启动时,XFS 确保任何未写入的数据块在重新引导时 置零。因此,丢失块由空字节来填充,这消除了安全性漏洞 ― 这是一种好得多的方法。
 
 
现在,关于数据丢失问题本身,该怎么办呢?通常,使用 XFS 时,该问题被最小化了,原因在于以下事实:XFS 通常比 ReiserFS 更频繁地将暂挂元数据更新写到磁盘,尤其是在磁盘高频率活动期间。因此,如果发生死锁,那么,最近元数据修改的丢失,通常比使用 ReiserFS 时要少。当然,这不能彻底解决不及时写数据块的问题,但是,更频繁地写元数据也确实促进了更频繁地写数据。
 
 
延迟分配
 
 
研究一下 延迟分配这个 XFS 独有的特性,然后我们将结束关于 XFS 的技术概述。正如您可能知道的,术语 分配(allocation)是指:查找空闲空间区域并用于存储新数据的过程。
 
 
XFS 通过将分配过程分成两个步骤来处理。首先,当 XFS 接收到要写入的新数据时,它在 RAM 中记录暂挂事务,并只在底层文件系统上 保留适当空间。然而,尽管 XFS 为新数据保留了空间,但 它却没有决定将什么文件系统块用于存储数据,至少现在还没决定。XFS 进行拖延,将这个决定延迟到最后可能的时刻,即直到该数据真正写到磁盘之前作出。
 
 
通过延迟分配,XFS 赢得了许多机会来优化写性能。到了要将数据写到磁盘的时候,XFS 能够以这种优化文件系统性能的方式,智能地分配空闲空间。尤其是,如果要将一批新数据添加到单一文件,XFS 可以在磁盘上分配一个 单一、相邻区域来储存这些数据。如果 XFS 没有延迟它的分配决定,那么,它也许已经不知不觉地将数据写到了多个非相邻块中,从而显著地降低了写性能。但是,因为 XFS 延迟了它的分配决定,所以,它能够一下子写完数据,从而提高了写性能,并减少了整个文件系统的碎片。
 
 
在性能上,延迟分配还有另一个优点。在要创建许多“短命的”临时文件的情况下,XFS 可能根本不需要将这些文件全部写到磁盘。因为从未给这些文件分配任何块,所以,也就不必释放任何块,甚至根本没有触及底层文件系统元数据。
 
 
结束语
 
 
我希望您喜欢阅读这篇关于 XFS(Linux 的、功能强大的下一代文件系统之一)的性能和技术特征的文章。在下一篇文章中,我们再见,那时我将向您展示如何设置 XFS,并在您的系统上运行它。在下一篇文章中,我们还将研究 XFS 的一些高级特性,譬如 ACL 和扩展属性。那么我们下次见!
·部署XFS(第 10 部分)
·文件系统更新(第 11 部分)
在我们开始 JFS 和 EVMS 之前,我要同您谈谈官方对 Linux 文件系统领域所发生的事情的当前状况所做的最新表述。我们遇到过许多 2.4 内核;其中一些很好,另外一些就没那么好。伴随着内核的
开发进程,XFS、ext3 和 ReiserFS 的开发正紧锣密鼓地进行。在此期间,许多 Gentoo Linux用户使用 XFS、ext3 和 ReiserFS 文件系统的不同组合得出了各种各样的结果。一般来说,不管 Gentoo Linux 用户在使用哪个新出现的文件系统时遇到了问题,我肯定会听说那个问题。那么,哪些文件系统最受欢迎?哪些又最可靠呢?在这篇文章中,我会与您分享我的经验,以及来自 ReiserFS、ext3 和 XFS 开发团队的反馈和状况的更新。
 
 
XFS 有什么新鲜事吗?
 
 
在过去的几个月中,选择 XFS 作为 Linux 文件系统成为一件很流行的事。根据来自 Gentoo Linux 用户的反馈,人们之所以喜欢是因为它具有健壮的功能集,而且一般情况下,它的整体性能良好。不过,XFS 的 1.0.x 发行版有一个严重的问题。您也许还记得,如果更新的是文件的元数据,但是某种不可预知的情况(如崩溃)导致无法将新数据写到磁盘上,那么象 XFS 和 ReiserFS 这样的“只支持元数据”的日志文件系统就会引起数据破坏。对于 ReiserFS,受影响的文件会包含受损的或无用的数据块,而对于 XFS,该文件会包含整块的二进制零。事实证明,如果您的服务器碰巧崩溃或者意外断电,XFS 1.0.x 有一种很不好的倾向,那就是会破坏最近修改过的文件。那些刚好在承受力较强的服务器上使用 XFS 的人一般都没问题,但那些在遇到某种软件或硬件稳定性问题的系统上运行 XFS 的人就面临着丢失很多数据的风险。
 
 
幸运的是,SGI XFS 的开发人员在 XFS 1.1 中大大减少了这个问题的出现次数。这个问题之所以在 XFS 1.0 中更加频繁的发生,原因在于对某类元数据的更新 必须按照其发生的顺序记录到文件系统中。这些按照顺序的元数据更新(被称为“同步”元数据更新)同样有将所有以前还没更新到磁盘上的元数据都刷新的效果。问题就出在这儿。如果这些早先的元数据刷新中也有一些相应的数据块需要被刷新,那么很可能在元数据被记录之后的半分钟内新的数据块仍然没有被写到磁盘上去。这就给发生数据丢失创造了一个很大的漏洞。
 
 
技术说明
 
 
对于 XFS 1.1,文件系统的元数据只在两种情况下会同步(有序)更新:
 
 
如果文件系统需要分配空间,并且有一个紧随的事务来释放同一块空间
 
在 XFS 处理用 O_SYNC (同步)选项打开的文件事务的时候;在这种情况下,对这个文件进行写操作将会使得文件系统对元数据所做的其它任何紧随其后的更改被刷新到磁盘。
 
 
幸运的是,绝大多数典型的服务器的 I/O 操作在本质上都是异步的。
 
 
 
如果在这个漏洞开启 期间(在元数据被刷新之后但是在相应的数据被写到磁盘上之前)重新启动系统或系统死机,那么新老数据都会丢失。出现这种情况的原因如下:元数据更新删除了对原先数据块的任何引用,却指向磁盘上没有填充过数据的数据块。服务器在崩溃后再次启动时,XFS 代码会查看日志,了解情况,把那些不完整的数据块填上二进制零以预防安全性问题。不幸的是,数据就永远丢失了。
 
 
这个问题在频繁使用全新的数据覆盖文件的情况下尤其麻烦。在这样的情况下,如果系统恰好在不合适的时候死机了,那么前面刷新过的元数据会导致文件内容整个丢失。这种特殊的情况 gentoo.org 服务器也遇到过几次,丢失了数据。由于每隔几分钟我们的邮差邮件列表软件就会用新数据覆盖它自己的配置文件,因此它最有可能发生上面所描述的情况。
 
 
这个故事寓意是这样的:SGI 的开发者在 XFS 1.1 中已经大大改善了这种局面,如果您运行的是 XFS 1.0,那么您应该下定决心尽快升级到 XFS 1.1。XFS 1.1 还包括了许多附加的修订。在 SGI 缓解了 XFS 对同步元数据更新的依赖的时候,它还能改进 XFS 1.0.x 的弱点之一 — 文件删除。真是太棒了!
 
 
过不了多久,我们还可以期待看到 XFS 的新发行版,它将更适合于Intel Itanium 平台。目前,XFS 的 Linux 版本要求 XFS 文件系统块的大小与平台的内存页面大小相同。这常常使得在 x86 系统上的磁盘不可能移到 Itanium 系统上,因为 Itanium 可以使用最大 64K 的页面,而 x86 只能用 4K。此外,对于大多数的任务来说,大小为 64K 的文件系统块并非最佳选择,但当前的代码迫使一些 Itanium 系统必须使用这样的文件系统块大小。如果修复了这个块大小问题,不仅将 XFS 文件系统从 x86 迁移到 ia64 上变得容易了,而且提供了一个额外的好处,就是允许系统管理员选择适合于他们的需要的 XFS 文件系统块大小。
 
 
ReiserFS
 
 
ReiserFS 文件系统堪称最有魄力的日志文件系统开发项目,因为它不只是将现有的文件系统移植到 Linux 内核(比如:XFS、JFS),它的设计也不是象 ext3 那样基于早先的文件系统。相反,ReiserFS 的设计完全是从头开始的,就其处理小文件而言,有一些 非常吸引人的性能指标。那么,自从 ReiserFS 被引入到 2.4 内核以来,它是怎样解决稳定性问题和一般的文件系统健壮性问题的呢?
 
 
从引入 ReiserFS 开始,它一直有数目非常多的稳定性和崩溃问题。有很多内核对 ReiserFS 用户来说根本就是噩梦,其中包括 2.4.3、2.4.9 甚至相对较新的 2.4.16。不过,尽管这些问题中有些是因 ReiserFS 文件系统代码本身的错误引起的,但有数目惊人的问题会因对内核的其它部分所做的修改而产生不希望的副作用。Linux 内核的开发过程中有一件不幸的事就是,无论您多么细心的测试您自己的代码,还是可能会有某个另外的内核开发者插入的一条很可能没有经过测试的更改而导致您的代码崩溃。最常见的情况是,只有在这些不希望的副作用已经被引入并向深信不疑的 Linux 计算公众用户发行 之后,开发者之间才会有交流。我认为,公平的说,有相当多的伤心的 ReiserFS 用户,他们发现自己正处于这一不幸的失败的环境中。
 
 
但是,我的朋友,好消息来了。在过去的几个月中,对于 ReiserFS 来说,情况看上去已经开始好转许多了。一是内核源代码开始稳定在 2.4.17 发行版。此外,在过去的几个月来 Namesys 的开发者(ReiserFS 的开发者)已经能修复相当多隐藏在他们的代码中的错误了。甚至还有更好的消息,内核 2.4.18 好象有一个 非常稳定的 ReiserFS 实现。2.4.18 绝非新出现的 — 在写这篇文章的时候,它出现已经将近三个月了,在代码中还没有发现任何重大问题。事实上,由于缺少新来的错误报告,Namesys 已经重新给发行版经理(Release Manager)指派了新的工作,改进 ReiserFS 的性能。
 
 
因此,ReiserFS 和 2.4 内核好象最终解决了它们的差异问题。就我个人观点而言,这真是振奋人心的消息;我很想再开始使用 ReiserFS,并计划在我下一次重新加载我的开发工作站时用它作为我的根文件系统。因为内核方面的问题已经平静下来了,我确信,现在有许多别的前 ReiserFS 用户也很愿意再次回到 ReiserFS。坦白的说,一旦您看到 ReiserFS 的小文件性能可以给某些应用程序的性能带来多大的提升,就很难再离开它了。
 
 
那么,在不远的将来我们期望在 ReiserFS 看到些什么呢?据 Hans Reiser 和他的开发者小组所说,预计在 2.4.20_pre1 会有一些非常好的改进,包括对 Chris Mason 的数据日志(象 ext3 的“data=journal”模式)的支持、伸缩性要好得多的新的块分配代码以及对大文件性能方面的一些改进,预计从 IDE 驱动器读取大文件时性能的提高要高达 15%。除这些马上就要着手进行的重大改进之外,我们很可能会看到 ReiserFS 将支持与 ext3 的“data=ordered”模式等价的模式。在这个问题上,ReiserFS 将提供和在 ext3 文件系统中发现的一样的数据完整性功能。我很高兴的看到 ReiserFS 开发小组正在将数据完整性(而不只是元数据完整性)提到这样高的优先级。
 
 
Ext3
 
 
那么,ext3 的情况如何呢?通常,ext3 相当稳定,还没有遇到过重大问题。为此,ext3 堪称非常可靠而且健壮的日志文件系统选择。尽管有些人也许会认为这个文件系统很“沉闷”,因为除很好的日志实现之外,看不出它对 ext2 做了任何重大改进,但“沉闷”在文件系统世界中是一件好事。它意味着文件系统很擅长只是不紧不慢的执行它的工作。此外,虽然与 ResierFS、XFS 和 JFS 相比,ext3 的可伸缩性具有局限性,但 ext3 已经证明,在大多数服务器和工作站所执行的典型文件系统操作中使用,它不仅速度很快而且很容易调整。很明显,ext3 开发者已经达到了他们要创建一个高质量的日志文件系统的目标,Linux 用户不用费什么力气就可以放心的升级到这一文件系统。
 
 
对于内核 2.4.19_pre5,现在同步安装 ext3 文件系统和“chattr +S”文件比从前快大约十倍。很快,我们有望看到添加一个选项用于特定目录树的同步更新,这个功能将主要用于邮件程序。此外,我们还期望能看到定期对代码进行小错误修复和性能改进,但是不会修改主要部分;ext3 已经很完美了,现在代码似乎处于维护模式。
 
 
非常感谢您花时间阅读我的这篇文章,请您下一次继续阅读我的文章,我们要看看 JFS
·EVMS简介(第 12 部分)
您是否曾经停下来思考有多少种可用于 Linux 的功能强大的与存储相关的技术?仅考虑日志记录文件系统这一项,就有 ReiserFS、ext3、XFS 和 JFS 这些文件系统。几年以前,Linux
甚至连一个日志记录文件系统也 没有。而现在,我们有许多日志记录文件系统,并且发现自己的处境很妙 — 可以按自己的需求选择最好的文件系统。有选择余地显然是件好事情。
 
现在,让我们来考虑纯文件系统之外的一些内容。Linux 的软件 RAID 功能(已经出现了一段时间)给 Linux 管理提供了另一组可能性(要获取更多信息,请参阅ibm.com/developerworks/cn/linux/filesystem/l-fs12/index.html#resources">参考资料,那里有一些链接,通过它们可以阅读我的关于 Linux 软件 RAID 的两部分系列文章)。最近,我们幸运地拥有了 Sistina 的 Linux LVM 技术(逻辑卷管理;请参阅参考资料,那里有一些链接,通过它们您可以阅读我的关于 LVM 的两部分系列文章,还可以下载 Sistina)。LVM 使管理员可以用更加灵活的方式来管理其存储资源,这要比用静态磁盘分区这种传统方式灵活得多。使用 LVM,管理员可以在运行中的服务器上扩大和缩小文件系统,并可以利用其它一些令人惊讶的功能,如文件系统快照。
 
 
因此,总体而言,Linux 拥有一组极其丰富的与存储相关的技术。但问题也出在这里;就 总体而言,我们确实有一些很棒的工具。但是尝试将这些存储技术中的几种结合起来使用,事情就变得复杂了。例如,设想我们希望创建位于 LVM 逻辑卷上的 ReiserFS 文件系统(这样就可以按需动态地扩展它),而 LVM 又位于 RAID-1 软件 RAID 卷上(针对磁盘故障而提供的一些保护)。
 
要做到这一点,我们首先在系统中添加两个新驱动器,然后使用 fdisk 在每个驱动器上建立分区。接下来,指定 /etc/raidtab 文件,并使用 mkraid 来启用 RAID-1 卷。做完这些之后,使用 pvcreate 、 vgcreate 和 lvcreate 在 RAID-1 卷之外创建 LVM 逻辑卷。最后,使用 mkreiserfs 在这个新逻辑卷之上创建文件系统。在完成这些工作之后,就可以准备挂装新的 ReiserFS 文件系统了。
 
是的,我们完成了任务,但是我们必须使用四种不同类型的工具来完成这一切,而且我们的工具没有一致的界面。当我们继续维护卷时,必要时将需要使用软件 RAID 和 LVM 这两种工具。而且这些工具都不能看到所有各层是如何关联的“总览图”,确切地讲,是以下方面:
 
有两个磁盘;
每个磁盘上有一个分区;
合并成一个 RAID-1 卷;
用来创建一个 LVM 物理卷;
将其添加到 LVM 卷组;
在卷组中创建了一个 LVM 逻辑卷;
在此逻辑卷上放置了 ReiserFS 文件系统。
 
这其中所涉及的所有工具的数量(更不要提每个工具都不可以与其它工具交互这一事实了)足以让许多管理员对尝试任何如此具有挑战性的任务感到灰心。并且,如果一个新管理员要接管我们这台经过巧妙配置的服务器,则即使是为了首先理解我们如何配置每件东西,也要采取相当多的探测工作。即便这位新管理员真的领悟了这个问题(或者即使我们向他交接了这方面的文档,这是我们应该做的!),他还是会冲出服务器机房,充满恐惧地尖叫。为什么会这样呢?因为,尽管 Linux 有许多与存储相关的技术,但是提供给管理员的用于管理这些技术的界面肯定既不容易管理也不易保持一致;这种一致性和统一性的缺乏使得这种复杂的存储配置错综复杂地交织在一起,从而难以实现和维护。
 
 
 
 


 
 
 

 
 
 
 

回页首
 
如果您猜测我将会说:EVMS 一下子就轻易地解决了所有这些问题,那么您完全正确。它确实做到了这一点。EVMS 为 Linux 下的所有存储技术提供了统一的、可扩展的、基于插件的 API。这意味着什么?它意味着由于 EVMS,您可以使用单个工具来对磁盘分区、创建 LVM 对象以及甚至创建 Linux 软件 RAID 卷。并且可以使用这一工具以强有力的方式合并这些技术。EVMS 可以看见“总览图”;它可以确切地知道每件东西是如何分层的,从文件系统一直到底层保存数据的物理磁盘。不仅如此,EVMS 还与所有现有的 Linux 技术兼容。它不会强迫您替换分区、LVM 或软件 RAID 卷。相反,它会“愉快”地工作并通过其统一的存储管理界面与现有的存储配置交互。实际上,EVMS 目前向您提供的选项包括命令行界面、基于 ncurses 的界面和用 GTK+ 编写的很棒的存储管理 GUI。为了增强您这方面的兴趣,我将向您展示两张运行中的 GTK+ EVMS Administration Utility(来自版本 1.0.0)的抓屏。在图 1 中,您可以看到 lvm/vg1/swap 区域(region)所包含的数据是来自 hda2 段(segment),该段位于 hda 磁盘。
在本文中,Daniel 继续探讨他在ibm.com/developerworks/cn/linux/filesystem/l-fs12/index.shtml">EVMS 简介中所遗
留下来的内容,并一步一步地指导您使用 evmsn (EVMS 的基于 ncurses 的管理工具)的过程。他向您展示了如何使用 evmsn 来利用新硬盘、对它分区并在其上创建 LVM 卷。接着,他向您介绍了重要的 EVMS 概念,当您继续研究此功能强大的技术时,您会发现这些概念是必不可少的。
 
 
开始使用 EVMS 的最佳方法是找一个空闲的硬盘,并将其安装到系统上。然后,就可以为您重要的内容在这个空闲硬盘上创建和删除分区和卷,而不必担心您的数据会遭到破坏。下面,通过引导 Gentoo Linux Game CD(请参阅本文后面的参考资料),我遵循了这个方法,这张 CD 实际上正好包含了 EVMS 1.0.1 支持。我使用 vmware(请查阅参考资料,获取链接)来引导这张 CD,并将我的虚拟 PC 配置成包含一个 SCSI 磁盘,sda。当出现带有“login:”提示符的欢迎界面时,输入 root,并在密码提示符下按一下 Enter 键,这样我就以 root 用户登录到这个生动的基于 Gentoo Linux CD 的系统。然后输入 evmsn 启动 ncurses 版本的 EVMS 管理界面。当然,也可以选择仅仅将一个新硬盘挂装在已有的支持 EVMS 的系统上。另外,如果您有 X,那么可以考虑使用 evmsgui ,这是一个基于 GTK+ 的 EVMS 管理界面。尽管 evmsn 运行在使用 ncurses 的控制台上,而 evmsgui 使用更现代的、基于 GTK+ 的界面,但这两个程序的界面非常相似。了解了其中一个,则很容易转到另外一个。
 
现在,让我们开始使用 EVMS。由于我正在用这个控制台,所以我输入 evmsn ,出现以下屏幕:
 

 
在这个屏幕上,可以看到 /dev/evms/sda ,它是我系统上唯一的卷。如果您在自己的机器上运行 EVMS,那么肯定会看到至少列出了两个额外的 /dev/evms/---- 项。通过使用上下方向键,可以在每个设备节点间移动。
 
您可能对这些 /dev/evms/--? 设备节点是什么正感到疑惑。乍一看,似乎它们可能仅仅是 /dev 中各个设备节点的副本;例如, /dev/evms/sda 仅仅与更传统的 /dev/sda 设备节点一样,表示同一个磁盘。那么,在 /dev/evms/ 目录中创建重复的设备节点有什么意义呢?回答是:EVMS 的工作之一是为系统上所有的卷创建统一的名称空间,而且它将这个名称空间创建在 /dev/evms 之下。所以,EVMS 检测到 sda 并识别出它应该属于 EVMS 名称空间,因此 EVMS 在 /dev/evms 中创建了相应的设备节点。现在正是一个最佳时间来指出:如果您没有在用 devfs,那么您可能需要运行 evms_devnode_fixup 程序来更新 /dev/evms 名称空间。
 
以下是另一段巧妙却又很重要的事实。尽管 EVMS 可以看到系统上所有的存储资源,但它可能不会为它找到的所有资源都创建 /dev/evms/ 设备节点项。例如,在我的开发系统中有两个硬盘 /dev/hde 和 /dev/hdg 。但是,没有对应的 /dev/evms/hde 和 /dev/evms/hdg 设备节点。当我查看 /dev/evms/ 时,发现的唯一设备节点是 /dev/evms/hde1 ,它表示第一个硬盘上的引导分区。现在,为什么是 /dev/evms/hde1 而不是 /dev/evms/hde 呢?唔,看来 EVMS 十分智能,它已识别出 hde 和 hdg 没有将其自身加入到卷中;相反,EVMS 可以看到我已经对 hde 和 hdg 进行了分区。它还可以看到我已用这些分区创建了 LVM 逻辑卷。/dev/evms/hde2 分区上的空间正用于更高级的存储对象,所以 EVMS 没有创建“ /dev/evms/hde2 ”设备节点。相反,它识别出我已创建的 LVM 卷,并在 /dev/evms/lvm 下为这些卷创建了设备节点:
 
 
 
 
 
$ ls /dev/evms/lvm/*/* -l
brw-rw-rw- 1 root root 117, 2 Dec 31 1969 /dev/evms/lvm/mainvg/root
brw-rw-rw- 1 root root 117, 3 Dec 31 1969 /dev/evms/lvm/raid0vg/swap
brw-rw-rw- 1 root root 117, 4 Dec 31 1969 /dev/evms/lvm/raid0vg/tmplv
 
这说明了 EVMS 遵循的一个重要原则:它只为它在系统上找到的 最终(而不是临时)存储对象创建设备节点。EVMS 将这些最终存储对象统称为“逻辑卷”,而不管它们实际上正好是磁盘、分区,还是 LVM 逻辑卷。正如稍后将看到的,当我们使用 evmsn 和 evmsgui 管理工具时,在能看到 /dev/evms/ 中 EVMS 卷相对应的设备节点之前,需要显式地从存储对象上创建 EVMS 卷。假如我们不打算直接使用存储对象,而是计划用它来创建更高级的存储对象,在将存储对象转换成卷之前,EVMS 将不会为它创建设备节点。EVMS 主张在创建 /dev/evms 设备节点之前先创建卷,这样可以为我们提供保护,以免在输入 mke2fs /dev/evms/foo 时,会在不应该使用的设备节点上创建文件系统。由于存在这个行为,所以我们可以获得保证,即 /dev/evms/ 下的每个设备节点都包含或打算包含文件系统或交换空间。
 
尽管这个行为非常巧妙、有用而且还减少了混乱,但确实需要花些功夫才能领会它。为了真正理解这一点,以下是在我的开发工作站上运行的 evmsgui 的抓屏。正如您可以看到的,在我的存储资源中,只有四项被配置为卷,而且它们都被用于文件系统或交换空间。
 

 
现在,回到 vmware EVMS 配置示例。如果您看了本文的第一个抓屏,那么会注意到,尽管 /dev/evms/sda 为空,但该磁盘还是显示为卷。这只是“细心的” EVMS 的另一个示例;尽管它不能检测到磁盘上任何有效的存储资源类型,但它还是将整个磁盘标记为卷,以便在开始使用该磁盘上的存储空间之前必须先 删除这个卷。这是一个极好的行为,因为理论上磁盘 可能包含一些 EVMS 完全不能识别的重要数据。通过采用这个方法,EVMS 就不会让人认为磁盘为空,也就不可能愚弄管理员(例如使他覆盖原始的 FreeBSD 卷),从而可以避免有风险的行为。这再一次说明了 EVMS 很巧妙,但乍一看它的行为稍微有点费解。
 
当然,在我这个特定情况下, /dev/emvs/sda 为空,而且我需要对它进行分区。为此,我需要按如下那样先破坏 /dev/evms/sda 卷。首先,选中 /dev/evms/sda 之后,按一下 Enter 键:
 

 
正如您可以看到的,这一操作产生了一个子菜单,其中有查看有关卷的细节、删除存储对象上的卷或破坏卷等选项。“破坏卷”和“删除存储对象上的卷”之间有一个很重要的区别;如果选择了前者,那么卷(以及该卷下的任何子对象)将被分解成它们的基本组件。但是,如果选择只删除卷,那么该卷将被破坏,但其子对象(如果存在的话)将完好无损地保留下来。“删除”和“破坏”之间的区别类似于剥去一层洋葱皮和将整只洋葱放到食品加工机中。
 
但是,在这个特定的例子中,我的洋葱只有一层,所以这两个选项将完成相同的事情。我决定选择“Destroy the volume”;在确认了我的选择并按任意键以返回到卷列表之后,现在您会看到这个操作的结果:“No logical volumes found”。
 
这个特定的视图突然变得非常令人讨厌,所以我将更改它。按一下“4”将弹出一个选择视图的子窗口。刚才,我在“Logical volumes”视图;现在转到“Available topmost storage objects”。完成选择后,现在我们能看到“sda”又在我们的列表中了!但与前面的操作不同,我们不查看卷,而是查看存储对象。如果接着做下去,对这个存储对象按一下 Enter 键,那么您将看到可以在“sda”上创建几个事物,其中包括一个 EVMS 或兼容性卷。
 
但是,我们真的不想这样做 — 如果我们想在 sda 上创建卷,应该回到本文开始的地方,即首次装入 evmsn 时,一个卷包括了整个磁盘。相反,我想创建分区, 然后将分区转换成卷。为此,我按一下“4”(来选择视图),然后选择“Logical disks”。这里我看到了逻辑磁盘“sda”。是的,在“sda”存储对象和“sda”逻辑磁盘之间 存在一个重要的区别。把“sda”作为存储对象来处理,我们可以将它转换成卷,或使用它来构建更高级的存储对象。但把“sda”作为磁盘来处理允许我把磁盘分割成多个分区(在 EVMS 的行话中称为“段(segment)”),这些分区反过来可以用作存储对象。为此,我按一下 Enter 键,弹出一个菜单,其中提供对磁盘分配段管理器的选项:
 

 
现在,如果 段与分区是一回事,则您可能想知道 段管理器是什么。可以将段管理器比作“分区方法”或方案。一旦我们选择了段管理器,EVMS 会在磁盘上存放一些元数据,同时创建主引导记录并将磁盘上的剩余空间标记为“freespace”,使之用于新段。要划分新段,可以从屏幕的菜单上选择“DefaultSegMgr”,再按一下 Enter 键,然后按下空格键。选择“sda”作为接收段管理器的对象。然后,再按一下 Enter 键,选择磁盘的类型为“Linux”(选项中还有“OS/2”),并按一下 Enter 键继续。按下一个键后,现在我按一下“4”(选择视图),然后选择“Disk Segments”以查看我已创建的新段:
 

 
现在我的磁盘有一个主引导记录,然后准备创建新的分区,即 EVMS 行话中的“段”。当我创建新段时,“sda_freespace1”段的大小将减少;如果我将磁盘上所有可用空间都用于创建分区,则 sda_freespace1 段将完全消失。如果硬盘上的可用存储空间确实完全映射到我选择的分区大小,则也可能继续保留剩余的很小的 sda_freespace1。
 
现在,为了创建那些段,当鼠标停留在“sda_freespace1”上时按一下 Enter 键,随后选择“Create a New Segment”。接着,选择 DefaultSegMgr 插件,并通过按一下空格键来接受使用 sda_freespace1 对象,随后按一下 Enter 键。接着,在磁盘的开始部分,创建一个很小的引导分区,用来保存内核。为此,我将按两次空格键,然后输入“100”以将分区大小设置为 100 MB,接着在 Bootable 选项上按两次空格键来将“Bootable”设置为真。最后,按一下 Enter 键以创建新的分区。重复这些步骤,然后可以创建用于文件系统的其它分区和交换分区。一些注意事项:如果需要创建的分区多于四个,那么请记住在创建引导卷之后切换掉“Primary Partition”选项。还要记住在创建交换分区时,将“Partition Type”设置为“Linux Swap”。
 
最后一步是在“sda1”、“sda2”和“sda3”上按一下 Enter 键。会出现一个菜单,其中会显示“Create Compatibility Volume from the Segment”的选项。在选择这个选项之后,段将被配置成包含一个可以使用的逻辑卷,这意味着它现在在 /dev/evms/ 下有一个设备节点。另外,因为我选择创建 兼容性卷,所以即使刚好用非 EVMS 内核重新引导了系统,也可以使用新卷。如果选择了“Create EVMS Volume”,那么这就是另外的情况了。请注意 EVMS 版本 1.1.0 及其更高级版本允许您将兼容性卷转换成 EVMS 卷,反之亦然。
 
 
至此,您应该掌握 EVMS 了吧,通过使用 EVMS,我已经对一个系统磁盘进行了分区。尽管这不是一个惊天动地的成就,但在您尝试如创建存储容器、LVM 逻辑卷、RAID 卷、快照以及其它吸引人的事物之前,这样一次经历将有助于向您提供所需的 EVMS 基础知识。当然,本文中我没有留出篇幅涉及这些主题,但我希望在明年能陆续讨论其中某些 EVMS 应用程序。但首先,在讨论其它话题之前,先结束“高级文件系统实现者指南”。希望您喜欢这次旅行:)
(IBM.COM)
_xyz