Linux已经改变了。最初受到Unix的启发,有一些很好理解但没有很好执行的规则,每个人都理解。程序做一些小事,并使用管道进行通信。X Windows服务器并不总是在您的本地计算机上运行。/usr中的任何内容都无法引导系统。
这些天,我们有系统地控制一切。如果你在一台显示器上运行Chrome,它就会被锁定在那台显示器上,它真的希望那是本地显卡。除非您采取预防措施,否则将/usr移到另一个分区很容易阻止您启动。我搬家了,我活着讲述了这件事。如果你需要这样做,你会想听听我的故事。
很多人都在批评systemd--包括我在内--但实际上这不是systemd的错。当我们有更多的程序员时,这些原则就会丧失,而且他们中的许多人都受到其他系统的影响,在这些系统中,事情的工作方式不同。不过,我不是在大喊大叫。我最近的一次经历让我想起了这一切,在此过程中,我学到了一些关于引导过程的现代状态的东西。故事开始于一位朋友给了我一个英特尔计算棒。但我遇到的问题并不是特定于该硬件,而是现代Linux发行版如何管理它们的启动过程。
就像我说的,我的一个朋友给了我一个英特尔计算棒。这是早期相当贫血的疾病之一。特别是,它只有1 GB的内存和8 GB的闪存。它启动了一个旧版本的Ubuntu,正如你所预期的那样,速度相当慢。但我喜欢它的外形,而且我有一个可以使用永久性PC的新工作室,所以我决定升级它。
出现了一些常见的问题。BIOS升级中断了网络。升级到KDE Neon修复了网络,但较新的内核有可怕的C-State bug,导致挂起。幸运的是,这很容易解决。所以经过一些努力,我有了一个合理的工作系统。说大也大吧。
问题出在8 GB的闪存。我放了一张64 GB的SD卡,但我不想从它启动。安装了霓虹灯和一些其他必需品后,闪光灯几乎100%满了。我的计划是/opt/home,/usr转到SD卡。我以为这会很容易。
传统上,这是一个简单的过程。首先,使用无需更改即可获得隐藏文件和链接的内容复制文件。许多人使用rsync,但通常使用tar。然后删除旧目录(为了安全起见,我先将其重命名,并在一切正常后将其删除)。最后,创建一个新的空目录,并更改/etc/fstab以在引导时挂载磁盘。
对于/home和/opt,这很好。系统会毫不费力地启动,我很快就把它做好了。我知道/usr会有点难,但我想我可以在没有GUI的根shell中,或者只需从USB驱动器引导并执行所有相同的工作。
实际上我预计会有四匹坐骑。我将整个SD卡挂载到/sdcard。然后,我确实将挂载从/sdcard/opt绑定到/opt,将/sdcard/home绑定到/home。/usr挂载也是绑定挂载,但不会那么容易。
我尝试移动/usr导致系统停止引导。为什么?原来systemd负责挂载/etc/fstab中的内容,而systemd需要/usr中的内容。我认为如果systemd不必读取/etc/fstab,那么它可能足够智能来引导系统,所以我决定使用systemd的本机设施挂载SD卡。
Systemd可以像处理服务一样处理挂载。这意味着它将管理安装,并将其编织到依赖项中。因此,在挂载某些磁盘之前,可能要求服务或其他挂载准备就绪,当然,其他服务和挂载也可能依赖于该磁盘。理论上,这是完美的,因为例如,在挂载/sdcard之前尝试挂载/sdcard/home是没有意义的。
显然,UUID会根据磁盘的不同而改变。不是很明显,这个文件必须命名为sdcard.mount。如果挂载点是/usr/lib,那么文件必须是usr-lib.mount。
请注意,挂载需要/sdcard。一旦您将这些文件放到正确的位置并重新加载系统,您就可以启动这些单元,文件将会挂载。您可以使它们在引导时启动。除了文件名之外,/opt单元看起来完全相同。
这仍然给/usr留下了问题。当然,编写单元很容易,但问题是systemd需要/usr之外的一些库,所以系统将拒绝引导。我考虑将库复制到/lib或初始RAM磁盘中的某个位置,但在发现有相当多的库之后,我决定不这么做。
我最终决定,在引导过程的早期挂载所有内容将是正确的答案。这样,systemd就可以想象它有一个完整的磁盘。我实际上曾考虑过使用LVM将磁盘连接在一起,但由于很多原因,我认为这是不好的,而且可能会遇到同样的问题。我想要控制SD卡和内部存储上的内容,所以是时候看看initramfs脚本了。
大多数现代Linux发行版不会直接引导您的根文件系统。取而代之的是,它们有一个压缩的文件系统,加载到RAM中,然后引导。该系统负责使系统为真正的引导做好准备。除了其他功能外,它还挂载根文件系统,然后旋转以使其成为真正的根。
对于Debian风格的发行版,这是initramfs,您可以在/etc/initramfs-tools中找到一些用户可定义的脚本。大部分预定义文件位于/usr/share/initramfs-tools中。在脚本目录中,您将看到许多后缀为-top、-Bottom和-premount的子目录。
正如您可能想象的那样,init-*发生在系统初始化时,而local-*发生在挂载本地磁盘时。不要将这些与钩子脚本混淆。钩子脚本在构建初始文件系统时执行。如果您需要静态修改引导环境,这会很有帮助。我们需要的脚本是在引导时执行的脚本。
首先运行顶部脚本,然后运行预装载,然后运行底部脚本。所以init-top首先运行,而init-Bottom是最后运行的。在此期间,其他脚本运行,并在本地底层运行,根文件系统应该可以运行了。
如果您阅读文档,您将看到脚本具有特定的格式。然而,也有一些例子具有误导性。例如,模板脚本显示获取/usr/share/initramfs-tools/钩子函数以加载通用函数。如果/usr已经存在,那就太好了,但是对于我们来说,它不存在。其他一些脚本使用位于/usr/share/initramfs-tools/script/function的引导环境中的副本。这就是我在剧本中使用的:
唯一棘手的部分是我们最终的根文件系统不在/,而是在/root,所以挂载反映了这一点。
当然,我必须禁用/opt和/home的systemd挂载,尽管我本可以离开它们而不将它们放在此脚本中。现在,在systemd获得控制之前,它可以在/usr中找到它想要的所有内容,并且系统会引导。移动这三个目录给我留下了大约70%的内部存储空间,只占用了SD卡的一小部分。
可能还有许多其他方法可以做到这一点。我提到了LVM,或者您可以恢复到旧的初始化脚本。但是,一旦你把一切都弄清楚了,这确实可以可靠地工作,而且非常灵活。
英特尔棒很小,但我们见过更小的。如果您确实在家里尝试这样做,不要忘记登录到eMMC设备并不总是一个好主意。