mycat 实施指南
你准备好了吗?
在本文中,我们将完成将Linux系统转换为devfs或设备文件系统的操作。 对于那些刚刚加入devfs系列的人,请阅读本系列的第4部分,我在其中解释了devfs如何解决内核级别的设备注册难题。 然后阅读本系列的第5部分 ,我在其中介绍了使Linux系统与devfs兼容所需的所有步骤,以便您准备好最终转换为devfs。
如果您还没有阅读第5部分,那么在按照此处的说明进行操作之前非常重要。 如果您跳过第5部分中的步骤,几乎可以保证我们将要安装的init包装程序将无法正常工作,并且最终您将得到无法启动的系统,需要进行紧急复苏。 那不是好事。 但是,如果您已经阅读了第5部分,那么就可以开始了。
初始化包装器
开端
我在第5部分结束时介绍了init包装器的概念,并解释了为什么它非常适合解决一些devfs初始化问题。 事不宜迟,让我们逐步介绍完整的init包装器,看看每个部分的功能。 我们将从顶部开始:
#!/bin/bash
# Copyright 2001 Daniel Robbins <drobbins@gentoo.org>, 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
如您所见,init包装器是一个真正的bash脚本,因为脚本顶部有一个#!/bin/bash
。 现在是时候提到我们的初始化包装程序要求运行bash 2.0或更高版本的好时机; 键入/bin/bash --version
以查看您的bash shell是否足够新。 如果不是,则可能要查看是否已安装/bin/bash2
可执行文件。 如果是这样,请将脚本的第一行更改为#!/bin/bash2
。
现在,让我们遍历脚本。 trap
命令防止脚本在执行过程中被用户中断(例如,在引导过程中按Ctrl-C)。 然后,我们export
一个合理的默认路径,并将默认umask设置为022。在引导过程中尽早设置默认umask始终是一个好主意,因为许多早期的2.4内核发行版中存在一个错误,导致默认的umask为0,这可能构成安全威胁。
接下来,我们遇到第一个条件语句if [ $$ -ne 1 ]
。 bash
将$$
扩展为当前正在运行的进程的进程ID,因此您可以看到我们确实在问一个问题:“我们的进程ID是否为1以外的任何数字?” 这有什么意义? 好吧,如果我们在引导过程中被内核启动,则我们将始终将PID设置为1,因为PID 1是为init
过程保留的。 如果我们的PID 不为 1,那么我们就知道在系统启动后正在从命令行运行。 这很常见,因为/sbin/init
命令具有双重目的,即允许超级用户更改已经引导的系统的运行级别。 如果是这种情况,我们只需exec
原始的/sbin/init
,现在将其重命名为/sbin/init.system
。 我们使用$*
变量将任何命令行参数传递给init.system
,我们的init包装器终止,并且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挂接到当前是只读的根文件系统。 之后,我们执行了一大块复杂的bash代码,利用了非常方便的Linux功能。 您可能不知道这一点,但是内核允许我们通过查看/ proc / cmdline的内容来查看LILO或GRUB传递了哪些选项。 在我的开发框中,/ 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(因为用户选择了“在启动时自动安装devfs”内核选项),我们将打印出一条信息性消息,让用户知道我们将无法设置devfs的持久性功能,因为如果devfs 尚未被内核挂载,则只能这样做。
设备持久性
但是,如果一切正常,我们将执行上一篇文章结尾介绍的devfs设置:/ dev绑定安装到/ dev-state,而devfs文件系统安装在/ dev上。 然后,我们执行一个我没有提到上一篇文章的步骤; 我们检查是否存在/ dev-state / compat目录,然后将其内容递归复制到/ dev。 尽管此过程乍看起来似乎有点多余(我们将利用devfsd
的设备持久性功能,对吗?),这确实是必要且有用的。 之所以需要一个compat目录,是因为devfsd
的持久性功能只能与启用了devfs的驱动程序一起使用 。
因此,如果碰巧正在使用非devfs内核模块,则需要在/ dev中手动创建设备节点。 这种方法的问题在于, devfsd
将忽略此新设备节点,这意味着下次重新启动时,它将消失。 我们对这个问题的解决方案是拥有/ dev-state / compat目录。 如果您有一个非devfs模块,只需在/ dev-state / compat中创建您的老式设备节点,由于我们方便的初始化包装程序的周全步骤,它们将在启动时手动添加到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
初始化包装器现在就位。
调整量
通过使用初始化包装器,我们避免了很多复杂的初始化脚本调整。 但是,可能有一项我们无法避免的调整。 既然我们已经在/ dev挂载了devfs文件系统,那么您的rc脚本可能会很难卸载根文件系统。 幸运的是,有一个简单的解决方案。 只需grep
你的rc脚本目录中所有出现的umount
通过键入cd /etc/rc.d; grep -r umount *
cd /etc/rc.d; grep -r umount *
或cd /etc/init.d; grep -r umount *
cd /etc/init.d; grep -r umount *
取决于特定发行版的rc脚本的安装位置。 然后,在每个引用umount
脚本中,确保使用-r
选项调用它。 特别重要的是用于umount
根文件系统的特定umount
命令,尽管在整个地方都使用umount -r
也可以。 :)
如果卸载失败,则-r
选项告诉umount
尝试将文件系统重新安装为只读。 这足以使根文件系统进入一致状态,并使其准备好重新启动,即使由于/ dev上的现有挂载而无法将其卸载也由于打开的设备节点而无法自行卸载它时也是如此。
现在,我们几乎可以重新启动了。 但是在进行此操作之前,我们先来看一下devfsd
并鞭打/etc/devfsd.conf,以便启用兼容设备和设备持久性。 不用担心,我们离完成向devfs的过渡仅一步之遥。
devfsd.conf
将/etc/devfsd.conf加载到您喜欢的编辑器中。 这是我推荐的devfsd.conf的前四行:
REGISTER .* MKOLDCOMPAT
UNREGISTER .* RMOLDCOMPAT
REGISTER .* MKNEWCOMPAT
UNREGISTER .* RMNEWCOMPAT
以上四行分别由一个事件 ( REGISTER
或UNREGISTER
),一个正则表达式( .*
)和一个动作( *COMPAT
字符串)组成。 那么,它们全都意味着什么呢? 第一行告诉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中的下一行是:
模块自动加载
LOOKUP .* MODLOAD
该条目告诉devfsd
每当“查找”任何设备( .*
)时执行MODLOAD
操作,这是当程序寻找特定设备节点的存在时发生的情况。 MODLOAD
操作将导致执行modprobe /dev/mydev
,其中/ dev / mydev是特定进程试图查找的设备的名称。 借助此功能(连同正确配置的/etc/modules.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。
功能和符号链接
为了完成我的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
是非常可配置的,这四行代码(或类似的东西,已针对您的特定系统定制)将达到目的。 在注册/ dev / cdrom / cdrom0设备时,前两个指令devfsd
会创建一个/ dev / cdrom符号链接。 为此, devfsd
实际上对指定的libc函数(在本例中为symlink()
和unlink()
执行动态调用。 在/ dev / misc / psaux(PS / 2鼠标)设备注册到devfs时,文件的最后两行使用相同的方法来创建/ dev / mouse符号链接。 自定义这些行到您的系统,然后保存此文件。 如果愿意,可以下载此devfsd.conf以在您自己的系统上使用。
重新启动前的注意事项
重新启动之前,您可能需要查看Richard Gooch的devfs常见问题; 你可能会发现关于devfs的命名方案特别有用当你与新样式的设备名称(见熟悉的信息相关主题下文)。 我还建议您打印本系列第5部分的副本,以防需要使用我的“紧急bash抢救”说明来解决与引导相关的问题。 请记住,如果由于某种原因新的初始化包装器被炸毁,则可以始终按照我的紧急救援说明将其删除,将根文件系统重新安装为可读写,然后执行以下步骤:
# cd /sbin
# mv init wrapper.sh
# mv init.system init
执行完这些步骤,将文件系统重新安装为只读并重新引导后,系统将恢复为预包装状态。 现在继续,重新启动,并享受devfs!
翻译自: https://www.ibm.com/developerworks/opensource/library/l-fs6/index.html
mycat 实施指南