Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2151486
  • 博文数量: 361
  • 博客积分: 10828
  • 博客等级: 上将
  • 技术积分: 4161
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-20 14:34
文章分类

全部博文(361)

文章存档

2011年(132)

2010年(229)

分类: LINUX

2011-04-07 09:53:24

介绍 devfs

设备,到处都是设备

 

Devfs,也叫设备文件系统(Device Filesystem),设计它的唯一目的就是提供一个新的(更理性的)方式管理通常位于 /dev 的所有块设备 
和字符设备。您也许知道,典型的 /dev 树包含数百个块特殊文件和字符特殊文件,它们全都在根文件系统上。每个特殊文件都可以让用户空间进程轻松地与内核设备实现交互。举例来说,通过对这些特殊文件执行操作,您的 X 服务器就能够访问视频硬件, fsck 可以执行文件系统检验, lpd 可以通过并行端口向打印机发送数据。

 

实际上,通常  和  更“酷”的方面是,设备不是简单地隐藏在晦涩的 I 之后,而是真正地与普通文件、目录和符号链接一样存在于文件系统上。因为字符和块设备是映射到普通文件系统名称空间的,我们通常可以用有意义的方式来与硬件交互,可以仅使用标准 Unix 命令,如 t 和 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)访问光盘驱动器,这都是因为有一个非常便捷的叫 devf的小程序。 devfsd 是一个有功能很多的程序,它负责创建老式的“兼容性”特殊文件,还允许您以很多种方式自定义 /dev。在我的下一篇文章中,我们会详细讨论 devfsd,到时我会一直引导您启动 devfs 并在您自己的系统上运行它。在那之前,请参考下面的参考资料以了解更多关于 devfs 的信息。
在 上一部分(第 4 部分)中,我们具体讨论了什么是 devfs,以及它是如何解决几乎所有的设备管理问题的。现在是在您的系统上启动和运行 devfs 的时候了。 在本文中,我们将使您的系统为启用 d 
evfs 作好准备,在下篇文章中,我们将真正开始向 devfs 的转换。即使在 devfs 的下一篇(也是最后一篇)出版以前,您也完全可以遵循本文中的步骤,因为当我们完成了所有这些步骤之后,您的系统将仍旧继续正常运行 ― 它仅仅为即将来临的 devfs 转换作好了准备。因此,没有必要因为后继文章还未出现而不遵循这些步骤。

 

请注意: 因为我们将对  系统的几个部分作出相当大的更改,所以确实可能搞糟您的系统。因此,如果您在对 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

 

既然文件系统已经一致(或者,如果文件系统在系统重引导时已经卸装干净,并且您跳过了上一步骤),我们可以简单地以可读写方式重新安装根文件系统并且安装 /c,如下所示:

 

# mount / -o remount,rw

 

# mount /proc

 

然后,安装可能位于其它分区中的所有重要的文件系统树。例如,如果在另一个分区上有 /usr,则还要输入:

 

# mount /usr

 

如果您想做的不仅仅只是打开一个编辑器的话,则最好是激活交换分区。如果您使用 es,您可能会需要它:)

 

# swon -a

 

现在,您应该能够使用您喜爱的编辑器来编辑任何需要编辑的文件,以便修复现有的引导问题。一旦完成,只需按安装时的顺序,以只读方式重新安装分区即可。例如,如果有一个单独的 /usr 分区,为使所有的文件系统处于一致的状态(准备重新引导),可以输入:

 

# mount /usr -o remount,ro

 

# mount / -o remount,ro

 

现在,您可以安全地重新引导了。但愿现在已经解决了引导问题,并且可以使用正常的 LILO 或 GRUB 选项启动和运行系统。

 

# /sbin/reboot -nfi

 

为 devfs 作好准备

 

devfs 配置

 

既然您知道了在紧急情况下该怎么做,我们就可以使系统为 devfs 做好准备了。在下一篇文章里,我们将对 Linux 系统作相对复杂的更改。为什么需要这样呢?因为我们不仅仅在内核中启用 devfs 功能,这的确非常容易。我们还将以一种特别方式设置 devf (设备管理守护进程),用它来备份和恢复任何对设备许可权和所有权的更改。我们需要用到很多小窍门来使这个新的系统极佳地工作。但是一旦实现,我想您将对这个结果 非常满意。

 

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 (EERIMENTAL) 选项。选中它。您将会看到在您刚启用的选项下方,出现了两个附加选项。第一个选项控制 devfs 在内核引导时是否自动安装到 /dev。 不要启用它,我们将用一个特殊脚本手动安装 /dev。 第二个选项, Debug devfs,也应该被禁用。

 

禁用 /dev/pts

 

当您在屏幕上看到 File systems kernel configuration 部分时,如果您碰巧启用了 /dev/pts file system for 98 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 系统而伤脑筋了。


阅读(829) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~