9.System configuration
9.1 Introduction
引导 Linux 系统需要完成若干任务。引导过程必须挂载虚拟和真实文件系统、初始化设备、检查文件系统完整性、挂载并启用所有交换分区或文件、设定系统时钟、启用网络、启动系统需要的守护进程,并完成用户自定义的其他工作。引导过程必须被组织好,以保证这些任务以正确顺序进行,并以尽量快的速度完成。
9.1.1 System V
System V 是自 1983 年以来就在 Unix 和 Linux 等类 Unix 系统中被广泛应用的经典引导过程。它包含一个小程序 init,该程序设定 login (通过 getty) 等基本进程,并运行一个脚本。该脚本一般被命名为 rc,控制一组附加脚本的运行,这些附加脚本完成初始化系统需要的各项工作。
init 程序受到 /etc/inittab 文件的控制,被组织为用户可以选择的系统运行级别。在 LFS 中,运行级别定义为:
0 — 停止运行 1 — 单用户模式 2 — 用户自定义模式 3 — 完整的多用户模式 4 — 用户自定义模式 5 — 拥有显示管理器的完整多用户模式 6 — 重启系统
通常的默认运行级别是 3 或 5。
优点
-
完备的,已经被详细理解的系统。
-
容易定制。
缺点
-
引导速度较慢。一个中等速度的基本 LFS 系统从第一个内核消息开始,到出现登录提示符为止,需要 8-12 秒的引导时间,之后还需要约 2 秒启动网络连接。
-
串行执行引导任务。这与前一项缺点相关。引导过程中的延迟 (如文件系统检查) 会拖延整个引导过程。
-
不支持控制组 (cgroups) 和每用户公平共享调度等高级特性。
-
添加脚本时,需要手动决定它在引导过程中的次序。
9.2 LFS-Bootscripts-20230101
LFS-Bootscripts 软件包包含一组在引导和关机过程中,启动和停止 LFS 系统的脚本。它们的配置文件和自定义引导过程的方法将在后续章节中描述。
估计构建时间: 不到 0.1 SBU 需要硬盘空间: 244 KB
9.2.1 安装 LFS-Bootscripts
安装该软件包:
make install
9.2.2 LFS-Bootscripts 的内容
安装的脚本: checkfs, cleanfs, console, functions, halt, ifdown, ifup, localnet, modules, mountfs, mountvirtfs, network, rc, reboot, sendsignals, setclock, ipv4-static, swap, sysctl, sysklogd, template, udev, 以及 udev_retry
安装的目录: /etc/rc.d, /etc/init.d (符号链接), /etc/sysconfig, /lib/services, /lib/lsb (符号链接)
简要描述
checkfs 在挂载文件系统前检查文件系统完整性 (日志文件系统和基于网络的文件系统除外)
cleanfs 删除在重启过程中不应保留的文件,例如 /run/ 和 /var/lock/ 中的文件;重新创建 /run/utmp 并删除可能存在的 /etc/nologin, /fastboot, 以及 /forcefsck 文件
console 加载用户希望使用的键盘布局对应的键映射表,并加载屏幕字体
functions 包含若干引导脚本使用的错误处理和状态检查等脚本
halt 停止系统运行
ifdown 停用网络设备
ifup 启用网络设备
localnet 设置系统主机名和本地回环设备
modules 加载 /etc/sysconfig/modules 中指定的模块,并在加载过程中使用该文件中给定的参数
mountfs 挂载所有文件系统,除了标为 noauto 的文件系统和基于网络的文件系统
mountvirtfs 挂载 proc 等虚拟内核文件系统
network 设定网卡等网络接口,并 (在可能的情况下) 设定默认网关
rc 主要运行级别控制脚本;它负责逐个运行所有其他引导脚本,运行的顺序根据指向那些脚本的符号链接名称确定
reboot 重启系统
sendsignals 保证在系统重启或停止运行前,所有进程已经终止
setclock 在硬件时钟没有设定为 UTC 时,将内核时钟重设为本地时间
ipv4-static 提供为网络接口分配静态网际互联协议 (IP) 地址的功能
swap 启用或禁用交换文件和交换分区
sysctl 如果 /etc/sysctl.conf 存在,将其中的系统配置选项值加载到内核
sysklogd 启动或停止系统与内核日志守护进程
template 为其他守护进程创建自定义引导脚本的模板
udev 准备 /dev 目录,并启动 udev 守护程序
udev_retry 重试失败的 udev 事件,如果必要的话,将生成的规则从 /run/udev 拷贝到 /etc/udev/rules.d
9.3 Overview of Device and Module Handling
在第 8 章中,我们在构建 eudev 时安装了 udev 守护程序。在我们详细讨论它的工作原理之前,首先按时间顺序简要介绍历史上曾经使用过的设备管理方式。
传统的 Linux 系统通常静态地创建设备,即在 /dev 下创建大量设备节点 (有时有数千个节点),无论对应的硬件设备是否真的存在。一般通过 MAKEDEV 脚本完成这一工作,它包含以相关的主设备号和次设备号,为世界上可能存在的每个设备建立节点的大量 mknod 命令。
使用 udev 时,则只有那些被内核检测到的设备才会获得为它们创建的设备节点。这些设备节点在每次引导系统时都会重新创建;它们被储存在 devtmpfs 文件系统中 (一个虚拟文件系统,完全驻留在系统内存)。设备节点不需要太多空间,它们使用的系统内存可以忽略不计。
9.3.1 历史
在 2000 年 2 月,一个称为 devfs 的新文件系统被合并到 2.3.46 版内核中,并在 2.4 系列稳定内核中可用。尽管它本身曾经存在于内核源代码中,但这种设备节点动态创建方法从未得到内核核心开发者的大力支持。
devfs 实现机制的主要问题是它处理设备的检测、创建和命名的方式。其中最致命的或许是最后一项,即设备节点命名方式。通常认为,如果设备名称是可配置的,那么设备命名策略应该由系统管理员,而不是某个 (某些) 开发者决定。devfs 还受到其设计中固有的竞争条件的严重影响;在不对内核进行大量修改的前提下无法修复这一问题。由于缺乏维护,它早已被标记为弃用特性,最终在 2006 年 6 月被从内核中移除。
在不稳定的 2.5 系列内核开发过程中,加入了一个新的虚拟文件系统,称为 sysfs,并在 2.6 系列稳定内核中发布。sysfs 的工作是将系统硬件配置信息提供给用户空间进程,有了这个用户空间可见的配置描述,就可能开发一种 devfs 的用户空间替代品。
9.3.2 Udev 实现
9.3.2.1 Sysfs
前面已经简要提到了 sysfs 文件系统。有些读者可能好奇,sysfs 是如何知道系统中存在哪些设备,以及应该为它们使用什么设备号的。答案是,那些编译到内核中的驱动程序在对应设备被内核检测到时,直接将它们注册到 sysfs (内部的 devtmpfs)。对于那些被编译为模块的驱动程序,注册过程在模块加载时进行。只要 sysfs 文件系统被挂载好 (位于 /sys),用户空间程序即可使用驱动程序注册在 sysfs 中的数据,udev 就能够使用这些数据对设备进行处理 (包括修改设备节点)。
9.3.2.2 设备节点的创建
内核通过 devtmpfs 直接创建设备文件,任何希望注册设备节点的驱动程序都要通过 devtmpfs (经过驱动程序核心) 进行注册。当一个 devtmpfs 实例被挂载到 /dev 时,设备节点将被以固定的名称、访问权限和所有者首次创建。
很快,内核会向 udevd 发送一个 uevent。根据 /etc/udev/rules.d,/usr/lib/udev/rules.d,以及 /run/udev/rules.d 目录中文件指定的规则,udevd 将为设备节点创建额外的符号链接,修改其访问权限,所有者,或属组,或者修改该对象的 udevd 数据库条目 (名称)。
以上三个目录中的规则都被编号,且这三个目录的内容将合并处理。如果 udevd 找不到它正在创建的设备对应的规则,它将会沿用 devtmpfs 最早使用的配置。
9.3.2.3 模块加载
编译为内核模块的设备驱动程序可能有内建的别名。别名可以通过 modinfo 程序查询,它通常和该模块支持的设备的总线相关标识符有关。例如,snd-fm801 驱动程序支持厂商 ID 为 0x1319,设备 ID 为 0x0801 的 PCI 设备,其别名为“pci:v00001319d00000801svsdbc04sc01l*”。对于多数设备,总线驱动程序会通过 sysfs 导出应该处理该设备的驱动程序别名,例如 /sys/bus/pci/devices/0000:00:0d.0/modalias 文件应该包含字符串“pci:v00001319d00000801sv00001319sd00001319bc04sc01i00”。Udev 附带的默认规则会导致 udevd 调用 /sbin/modprobe 时传递 MODALIAS uevent 环境变量 (它的值应该和 sysfs 中 modalias 文件的内容相同),从而加载那些在通配符扩展后别名与这个字符串匹配的模块。
在本例中,这意味着除了 snd-fm801 外,过时 (且不希望) 的 forte 如果可用,也会被加载。之后将介绍防止加载不希望的驱动程序的方法。
内核本身也能够在需要时为网络协议,文件系统,以及 NLS 支持加载模块。
9.3.2.4 处理热插拔/动态设备
当您插入一个设备,例如通用串行总线 (USB) MP3 播放器时,内核能够发现该设备现在已经被连接到系统,并生成一个 uevent 事件。之后 udevd 像前面描述的一样,处理该 uevent 事件。
9.3.3 加载模块和创建设备时的问题
在自动创建设备节点时,可能出现一些问题。
9.3.3.1 内核模块没有自动加载
Udev 只加载拥有总线特定别名,且总线驱动程序正确地向 sysfs 导出了必要别名的模块。如果情况不是这样,您应该考虑用其他方法加载模块。在 Linux-6.1.11 中,已知 Udev 可以加载编写正确的 INPUT,IDE,PCI,USB,SCSI,SERIO,以及 FireWire 驱动程序。
为了确定您需要的设备驱动程序是否包含 Udev 支持,以模块名为参数运行 modinfo 命令。然后试着在 /sys/bus 中找到设备对应的目录,并检查其中是否有 modalias 文件。
如果 modalias 文件存在于 sysfs 中,说明驱动程序支持该设备,并能够直接和设备交互,但却没有正确的别名。这是驱动程序的 bug,您需要不通过 Udev 直接加载驱动,并等待这个问题日后被解决。
如果 modalias 文件不存在于 /sys/bus 下的对应目录中,说明内核开发者尚未对该总线类型增加 modalias 支持。在 Linux-6.1.11 中,ISA 总线不受支持。只能等待这个问题在日后被解决。
Udev 根本不会尝试加载“包装器” 驱动程序,比如 snd-pcm-oss 等,或 loop 等非硬件驱动程序。
9.3.3.2 内核模块没有自动加载,且 Udev 不尝试加载它
如果“包装器”仅仅用于增强其他模块的功能 (例如,snd-pcm-oss 增强 snd-pcm 的功能,使 OSS 应用程序能够使用声卡),需要配置 modprobe,使其在 Udev 加载被包装的模块时,自动加载包装器。为此,需要将“softdep”行添加到对应的 /etc/modprobe.d/
softdep snd-pcm post: snd-pcm-oss
注意“softdep”命令也允许 pre: 依赖项,或混合使用 pre: 和 post: 依赖项。参阅 modprobe.d(5) man 手册页面,了解更多关于“softdep”语法和功能的信息。
如果出现问题的模块不是包装器,而是本身就有特定功能的模块,需要配置 modules 引导脚本以在系统引导时加载它。为此,将模块名添加到 /etc/sysconfig/modules 中,作为单独的一行。包装器模块也可以被添加到这里,但对于包装器来说这不是最好的方案。
9.3.3.3 Udev 加载了不希望的模块
不要构建该模块,或者在 /etc/modprobe.d/blacklist.conf 文件中禁用它。以 forte 为例,下面一行禁用了该模块:
blacklist forte
被禁用的模块仍然可以通过直接执行 modprobe 手动加载。
9.3.3.4 Udev 创建了错误的设备或错误的符号链接
这一般是由于一条规则意外地匹配了某个设备。例如,一个写得不好的规则可能同时匹配到 SCSI 磁盘 (正确的) 和对应厂商的 SCSI 通用设备 (不正确的)。找到引起问题的规则,并通过 udevadm info 的帮助,将它进一步细化。
9.3.3.5 Udev 规则工作不可靠
这可能是前一个问题的另一个表现形式。如果不是,而且您的规则使用了 sysfs 属性,这个问题可能由内核计时问题引发,这类问题需要在新的内核版本中修复。目前,您可以创建一条规则以等待被使用的 sysfs 属性,并将它附加到 /etc/udev/rules.d/10-wait-for-sysfs.rules 文件中 (如果不存在就创建一个文件),绕过这个问题。如果您通过这种方法解决了问题,请通知 LFS 开发邮件列表。
9.3.3.6 Udev 没有创建设备
首先,确认驱动程序已经被编译到内核中或作为模块被加载,而且udev 没有创建命名错误的设备。
如果驱动程序没有将它的信息导出到 sysfs,udev 就无法获得创建设备节点必需的信息。这种问题往往出现在内核源代码树以外的第三方驱动程序中。这时,需要在 /usr/lib/udev/devices 中使用正确的主设备号和次设备号,创建一个静态设备节点 (参考内核文档中的 devices.txt 或第三方驱动厂商提供的文档),该静态设备节点将被复制到 /dev,udev 会自动完成复制。
9.3.3.7 重启后设备命名顺序随机变化
这是由于 Udev 从设计上就是并行加载模块的,因此无法预测加载顺序。这个问题永远也不会被 “修复”。您不应该指望内核提供稳定的设备命名,而是应该创建您自己的规则,以根据设备的一些稳定属性,例如设备序列号或 Udev 安装的一些 *_id 工具的输出,来创建具有稳定名称的符号链接。可以参考第 9.4 节 “管理设备”和第 9.5 节 “一般网络配置”中的例子。
9.3.4 扩展阅读
以下链接包含了一些额外的帮助文档:
A Userspace Implementation of devfs http://www.kroah.com/linux/talks/ols_2003_udev_paper/Reprint-Kroah-Hartman-OLS2003.pdf
The sysfs Filesystem https://www.kernel.org/pub/linux/kernel/people/mochel/doc/papers/ols-2005/mochel.pdf
9.4 Managing Devices
9.4.1 网络设备
Udev 在默认情况下,根据固件或 BIOS 的数据,或总线、插槽,以及 MAC 地址等物理特性命名网络设备。这种命名法的主要目的是保证网络设备在每次引导时获得一致的命名,而不是基于网卡被系统发现的时间进行命名。例如,如果在一台拥有两块网卡 —— 例如,其中一块由 Intel 生产,另一块由 Realtek 生产 —— 的计算机使用旧版的 Linux,则 Intel 网卡可能被命名为 eth0,而 Realtek 网卡被命名为 eth1。然而在重新启动系统后,它们的顺序可能会颠倒。
在新的命名架构中,典型的网络设备名称类似 enp5s0 或 wlp3s0。如果您不喜欢这种命名惯例,可以采用传统或自定义命名架构。
9.4.1.1 在内核命令行中禁用一致性命名
传统的,类似 eth0,eth1 这样的命名格式可以通过在内核命令行中加入 net.ifnames=0 而恢复。这在那些没有两块同类以太网设备的机器上最为合适。笔记本一般拥有两个以太网连接,在这种命名方式下分别是 eth0 和 wlan0;也可以为这些笔记本采用这种方法。内核命令行需要写入 GRUB 配置文件中,参阅第 10.4.4 节 “创建 GRUB 配置文件”。
9.4.1.2 创建自定义 Udev 规则
可以创建自定义 Udev 规则,定制命名架构。系统中包含一个生成初始规则的脚本,执行以下命令生成初始规则:
bash /usr/lib/udev/init-net-rules.sh
现在检查文件 /etc/udev/rules.d/70-persistent-net.rules,确认网络设备与命名的对应关系:
cat /etc/udev/rules.d/70-persistent-net.rules
注意: 某些情况下,例如 MAC 地址被手动指定给了某块网卡,或在 Qemu、Xen 等虚拟环境下,可能不会生成网络设备规则文件,因为 MAC 地址的分配没有一致性。此时不能使用这种方法。
该文件的开头是一个注释块,之后对于每个网络接口设备 (NIC) 都给出两行。第一行是注释,给出该 NIC 的硬件 ID (例如对于 PCI 设备,就是 PCI 生产商 ID 和设备 ID),以及其驱动程序的名称 (在括号中,如果能找到驱动程序的话)。它们并不被用于确定该设备的命名,仅供您在编写规则时进行参考。第二行是匹配该 NIC 的 udev 规则,和实际赋予它的设备名。
所有 udev 规则包含一些关键字,用逗号和可选的空格进行分隔。下面给出规则中包含的关键字和对它们的解释:
-
SUBSYSTEM=="net"- 告诉 udev 忽略除网卡以外的设备。 -
ACTION=="add"- 告诉 udev 忽略除了“添加”外的所有 uevent (也存在“删除”或“修改”类型的 uevent,但在这种情况下不需要重命名网络接口)。 -
DRIVERS=="?*"- 这使得 udev 忽略 VLAN 或桥接子接口 (它们没有驱动程序)。它们必须被忽略,否则其命名会与父设备冲突。 -
ATTR{address}- 该关键字的值是 NIC 的 MAC 地址。 -
ATTR{type}=="1"- 这保证在使用创建多个虚拟接口的无线驱动程序时,只匹配主要接口。跳过其他接口的原因和忽略 VLAN 与桥接子接口一样,是为了防止命名冲突。 -
NAME- 该关键字的值是 udev 将赋予该网络接口的命名。
NAME 的值是我们关注的重点。在继续阅读之前,您需要确保自己知道赋予每个网络接口的命名,在之后的配置文件中需要使用 NAME 的值。
9.4.2 CD-ROM 符号链接
您之后可能希望安装的一些程序 (如某些媒体播放器) 预期 /dev/cdrom 和 /dev/dvd 符号链接存在,并指向 CD-ROM 或 DVD-ROM 设备。另外,在 /etc/fstab 中引用它们也非常方便。Udev 提供了一个脚本,能根据每个设备的功能,为您生成创建这两个符号链接的规则。但是,您需要确定,自己希望使用该脚本提供的两种操作模式中的哪一种。
首先,该脚本可以在 “by-path” 模式下运行 (这是 USB 和 FireWire 设备的默认模式),此时它创建的规则依赖于 CD 或 DVD 设备的物理路径。其次,它可以在 “by-id” 模式下运行,此时它创建的规则依赖于 CD 或 DVD 设备本身存储的识别字符串。物理路径由 udev 的 path_id 脚本确定,而识别字符串由 ata_id 或 scsi_id 程序 (根据设备类型选用其中一个) 从硬件中读取。
两种方式各有优点;正确的方式依赖于设备可能发生的变化。如果您预期指向设备的物理路径 (即它连接的端口或插槽) 可能变化,例如您可能会将它移动到另一个 IDE 接口或另一个 USB 接口,您应该使用 “by-id” 模式。另一方面,如果您可能用具有相同功能,并接入相同接口的另一台设备替换它 (因为设备可能损坏),则应该使用 “by-path” 模式。
如果两种变化都可能发生,则根据您预期较常发生的变化选择模式。
重要
外接设备 (例如 USB 接口的 CD 驱动器) 不应使用 by-path 模式,因为每次将该设备插入到新的外部接口时,其物理路径都可能变化。只要您使用了基于物理路径识别外接设备的 udev 规则,都会导致这个问题,并不仅限于 CD 或 DVD 设备。
如果您希望知道 udev 脚本会使用的路径或识别字符串值,对于正确的 CD-ROM 驱动器,在 /sys 目录中找到对应的目录 (例如 /sys/block/hdd),然后运行类似下面这样的命令:
udevadm test /sys/block/hdd
观察包含一些 *_id 程序输出的行。“by-id” 模式在 ID_SERIAL 存在且非空时会使用它,否则就使用 ID_MODEL 和 ID_REVISION 的组合。“by-path” 模式会使用 ID_PATH 的值。
如果默认模式不适合您的情况,可以像下面这样修改 /etc/udev/rules.d/83-cdrom-symlinks.rules 文件 (将 mode 替换成 “by-id” 或 “by-path” 中的一个):
sed -e 's/"write_cd_rules"/"write_cd_rules mode"/' \
-i /etc/udev/rules.d/83-cdrom-symlinks.rules
注意现在并不需要创建规则文件和符号链接,因为已经绑定挂载了宿主的 /dev 目录,我们假定宿主系统存在正确的符号链接。只要在第一次引导 LFS 系统后创建规则和符号链接即可。
然而,如果您有多个 CD-ROM 设备,则生成的符号链接可能指向不同于您的宿主系统的设备,因为设备发现的顺序不可预测。在您第一次引导 LFS 系统后,创建的设备分配将会是稳定的,因此这仅在您希望宿主系统和 LFS 中的符号链接指向同一设备时才会成为问题。如果您有这种需求,在引导后检查 (如果需要的话修改) 生成的 /etc/udev/rules.d/70-persistent-cd.rules 文件,保证分配的符号链接和您的需要一致。
9.4.3 处理重复设备
正如第 9.3 节 “设备和模块管理概述”中所述,那些功能相同的设备在 /dev 中的顺序是随机的。例如,如果您有一个 USB 摄像头和一个电视棒,有时 /dev/video0 会指向摄像头,/dev/video1 指向电视棒,而有时在重启后这个顺序正好颠倒过来。对于所有除了声卡和网卡以外的设备,该问题都可以通过建立 udev 规则以创建持久化符号链接来解决。对于网卡的解决方案在第 9.5 节 “一般网络配置”中单独描述,而声卡配置可以在 BLFS 中找到。
对于您的每个可能有这类问题的设备 (即使在您当前使用的 Linux 发行版上并没有问题),找到 /sys/class 或 /sys/block 中的对应目录。对于视频设备,目录可能是 /sys/class/video4linux/videoX。找出能够唯一确认该设备的属性 (通常是厂商和产品 ID,或者序列号):
udevadm info -a -p /sys/class/video4linux/video0
然后编写创建符号链接的规则,例如:
cat > /etc/udev/rules.d/83-duplicate_devs.rules << "EOF"
# 摄像头和电视棒的持久化符号链接
KERNEL=="video*", ATTRS{idProduct}=="1910", ATTRS{idVendor}=="0d81", SYMLINK+="webcam"
KERNEL=="video*", ATTRS{device}=="0x036f", ATTRS{vendor}=="0x109e", SYMLINK+="tvtuner"
EOF
结果是,/dev/video0 和 /dev/video1 仍然会随机指向电视棒和摄像头 (因此不应直接使用它们),但符号链接 /dev/tvtuner 和 /dev/webcam 总会指向正确设备。
9.5 General Network Configuration
9.5.1 创建网络接口配置文件
负责网络的引导脚本通常根据 /etc/sysconfig 中配置文件的内容,判定需要启用或禁用哪些网络接口。对于每个需要配置的网络接口,该目录中都应该包含一个文件,文件名类似 ifconfig.xyz,这里“xyz”应该能够描述该网卡。使用接口名 (如 eth0) 一般比较合适。文件中包含该网络接口的属性,如 IP 地址、子网掩码等。文件名必须以 ifconfig 开头。
注意
如果没有使用前一节描述的自定义命名策略,udev 会根据系统物理特征命名网卡接口,例如 enp2s1。如果您不能确定接口名,可以在引导您的 LFS 系统后使用 ip link 或 ls /sys/class/net 命令确认。
接口名依赖于系统正在运行的 udev 守护进程的实现和配置。LFS 的 udev 守护进程 (在第 8.70 节 “Eudev-3.2.11”中安装) 直到 LFS 系统引导时才会运行。因此,在宿主系统中使用上述命令未必能确定 LFS 系统将会使用的接口名,即使在 chroot 环境中仍然如此。
例如,以下命令为 eth0 设备创建一个静态 IP 地址配置:
cd /etc/sysconfig/
cat > ifconfig.eth0 << "EOF"
ONBOOT=yes
IFACE=eth0
SERVICE=ipv4-static
IP=192.168.1.2
GATEWAY=192.168.1.1
PREFIX=24
BROADCAST=192.168.1.255
EOF
您必须修改每个文件中用斜体显示的设定值,以正确设置网络接口。
如果 ONBOOT 变量被设置为“yes”,则 System V 网络脚本会在引导系统时启用该网络接口卡 (NIC)。如果设定的值不是 “yes”,网络脚本会忽略该 NIC,不自动启用它。您可以使用 ifup 和 ifdown 命令,手动启用或禁用网络接口。
IFACE 变量指定网络接口名,例如 eth0。所有网络设备配置文件都需要它。配置文件扩展名必须与该变量的值相同。
SERVICE 变量定义获取 IP 地址的方法。LFS-Bootscripts 软件包使用模块化 IP 配置格式,在 /lib/services/ 目录中新建一些文件,即可使用其他 IP 分配方法,这一般被用于动态主机配置协议 (DHCP) 配置,具体方法在 BLFS 手册中说明。
如果默认网关存在,GATEWAY 变量应该包含默认网关的 IP 地址。如果默认网关不存在,应该将这一行完全注释掉。
PREFIX 变量指定子网使用的 IP 地址位数。IP 地址中的每一段都是二进制为 8 位的数,如果子网掩码是 255.255.255.0,说明 IP 地址中前三段 (24 位) 表示子网编号。如果子网掩码是 255.255.255.240,则使用了前 28 位表示子网编号。子网前缀比 24 长的情况一般见于基于 DSL 或同轴电缆的 Internet 服务提供商 (ISP)。在我们的例子中,子网掩码是 255.255.255.0。您应该根据您所处的子网调整 PREFIX 变量。如果忽略它,则默认值为 24。
参考 ifup 的 man 页面获得更多信息。
9.5.2 创建 /etc/resolv.conf 文件
系统需要某种方式,获取域名服务 (DNS),以将 Internet 域名解析成 IP 地址,或进行反向解析。为了达到这一目的,最好的方法是将 ISP 或网络管理员提供的 DNS 服务器的 IP 地址写入 /etc/resolv.conf。执行以下命令创建该文件:
cat > /etc/resolv.conf << "EOF"
# Begin /etc/resolv.conf
domain <您的域名>
nameserver <您的主要域名服务器 IP 地址>
nameserver <您的次要域名服务器 IP 地址>
# End /etc/resolv.conf
EOF
可以省略 domain 语句,或使用一条 search 语句代替它。阅读 resolv.conf 的 man 页面了解更多细节。
将 *<域名服务器 IP="" 地址="">* 替换为您的网络环境下最合适的 DNS 服务器 IP 地址。这里往往会写入不止一个 DNS 服务器 (需要提供后备功能的次要服务器)。如果您只需要或只希望使用一个 DNS 服务器,可以删除文件中的第二个 nameserver 行。也可以写入本地路由器的 IP 地址。域名服务器>
注意
Google 公用 DNS 服务器的 IP 地址是 8.8.8.8 和 8.8.4.4。
9.5.3 配置系统主机名
在引导过程中,/etc/hostname 被用于设定系统主机名。
执行以下命令,创建 /etc/hostname 文件,并输入一个主机名:
echo "<lfs>" > /etc/hostname
*
9.5.4 自定义 /etc/hosts 文件
选择一个全限定域名 (FQDN) 和可能的别名,以供 /etc/hosts 文件使用。如果使用静态 IP 地址,您还需要确定要使用的 IP 地址。hosts 文件条目的语法是:
IP_地址 主机名.域名 别名
除非该计算机可以从 Internet 访问 (即拥有一个注册域名,并分配了一个有效的 IP 地址段 —— 多数用户没有分配公网 IP),则使用的 IP 地址必须属于私网 IP 范围。有效的范围是:
私网地址范围 公共前缀长度
10.0.0.1 - 10.255.255.254 8
172.x.0.1 - 172.x.255.254 16
192.168.y.1 - 192.168.y.254 24
x 可以是 16-31 之间的任何数字。y 可以是 0-255 之间的任何数字。
有效的私网 IP 地址的一个例子是 192.168.1.1。与之对应的 FQDN 可以是 lfs.example.org。
即使没有网卡,也要提供一个有效的 FQDN,某些程序需要它才能正常工作。
执行以下命令,创建 /etc/hosts:
cat > /etc/hosts << "EOF"
# Begin /etc/hosts
127.0.0.1 localhost.localdomain localhost
127.0.1.1 <FQDN> <HOSTNAME>
<192.168.1.1> <FQDN> <HOSTNAME> [alias1] [alias2 ...]
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
# End /etc/hosts
EOF
其中 *<192.168.0.2>,
9.6 System V Bootscript Usage and Configuration
9.6.1 System V 引导脚本如何工作?
当前构建的 LFS 版本使用一种称为 SysVinit 的特殊引导架构,它的设计基于一组运行级别 (run level)。不同系统的引导过程可能会区别很大;一般不能假设那些在某个 Linux 发行版上正常工作的方法也能在 LFS 正常工作。LFS 有一些独特的工作方式,但它也遵守被广泛接受的标准。
另一种引导过程称为 systemd,我们不会继续讨论它。https://www.linux.com/training-tutorials/understanding-and-using-systemd/ 对其进行了详细介绍。
SysVinit (之后简称为“init”) 使用一组运行级别。有七个编号为 0 到 6 的运行级别 (实际上还有更多,但它们用于一些特殊情况,一般并不使用。参阅 init(8) 了解更多细节。) 每个运行级别对应于计算机在启动或关闭时应该进行的一组操作。默认运行级别是 3,下面介绍是 LFS 实现的不同运行级别:
0:停止系统运行 1:单用户模式 2:保留用于自定义,如果没有自定义和 3 相同 3:有网络连接的多用户模式 4:保留用于自定义,如果没有自定义和 3 相同 5:和 4 相同,一般用于 GUI 登录 (如 GNOME 的 gdm) 或 LXDE 的 lxdm 6:重启计算机
注意
传统上,运行级别 2 被定义为“没有网络连接的多用户模式”,但是这只有在多年以前,多位用户可以通过串口同时连接系统时才有意义。在当前的系统环境中,这样的定义没有道理,因此我们将其指定为“保留”。
9.6.2 配置 Sysvinit
在内核初始化过程中,第一个运行的程序是 init (如果没有通过命令行指定另外的程序)。这个程序读取初始化文件 /etc/inittab。执行以下命令创建该文件:
cat > /etc/inittab << "EOF"
# Begin /etc/inittab
id:3:initdefault:
si::sysinit:/etc/rc.d/init.d/rc S
l0:0:wait:/etc/rc.d/init.d/rc 0
l1:S1:wait:/etc/rc.d/init.d/rc 1
l2:2:wait:/etc/rc.d/init.d/rc 2
l3:3:wait:/etc/rc.d/init.d/rc 3
l4:4:wait:/etc/rc.d/init.d/rc 4
l5:5:wait:/etc/rc.d/init.d/rc 5
l6:6:wait:/etc/rc.d/init.d/rc 6
ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now
su:S06:once:/sbin/sulogin
s1:1:respawn:/sbin/sulogin
1:2345:respawn:/sbin/agetty --noclear tty1 9600
2:2345:respawn:/sbin/agetty tty2 9600
3:2345:respawn:/sbin/agetty tty3 9600
4:2345:respawn:/sbin/agetty tty4 9600
5:2345:respawn:/sbin/agetty tty5 9600
6:2345:respawn:/sbin/agetty tty6 9600
# End /etc/inittab
EOF
在 inittab 的 man 页面中可以找到对该初始化文件的解释。在 LFS 中,关键的命令是 rc。上面的初始化文件会指示 rc 先运行 /etc/rc.d/rcS.d 目录中所有文件名以 S 开头的脚本,再运行 /etc/rc.d/rc?.d 目录中所有文件名以 S 开头的脚本。这里问号表示 initdefault 值指定的默认运行级别。
为了方便起见,rc 脚本从 /lib/lsb/init-functions 中读取脚本函数库。该函数库又会读取一个可选的配置文件,/etc/sysconfig/rc.site。如果希望将所有系统参数集中到一个文件中,可以将后续各节描述的系统配置文件参数都写入这个文件。
为了方便调试,脚本函数会将所有输出记录到 /run/var/bootlog。由于 /run 是 tmpfs,这个文件在重新启动时不会被保留;然而,在引导过程结束时,该文件的内容会被附加到更持久的日志文件 /var/log/boot.log 的末尾。
9.6.2.1 切换运行级别
通过命令 init
在 /etc/rc.d 中有一些名字如同 rc?.d 的目录 (这里 ? 是运行级别编号),以及一个目录 rcS.d,这些目录都包含一些符号链接。它们的文件名都以 K 或 S 开头,且文件名中这两个字母之后一定有两位数字。K 表示停止 (杀死,kill) 一个服务,而 S 表示启动 (start) 一个服务。两位数字决定脚本运行的顺序,从 00 到 99 —— 数字更小的脚本更早执行。当 init 切换到另一个运行级别时,它会执行这些脚本,从而适当地启动或停止服务,满足选择的运行级别要求。
上述符号链接实际指向的脚本文件位于 /etc/rc/init.d。它们完成实际的工作。一对 K 链接和 S 链接指向 /etc/rc/init.d 中的同一脚本,这是因为脚本接受不同的参数,如 start,stop,restart,reload,以及 status。在发现 K 链接时,会以 stop 参数运行脚本。在发现 S 链接时,会以 start 参数运行脚本。
下面是脚本接受的不同参数及其解释:
start
启动服务。
stop
停止服务。
restart
停止服务,再重新启动它。
reload
更新服务配置。当服务的配置文件被修改后,如果不需要重新启动它,就使用该参数。
status
报告服务是否正在运行中,如果正在运行,报告其 PID。
您可以自由修改引导过程的工作方式 (毕竟这是您自己的 LFS 系统)。我们给出的文件只是示例,展示完成引导过程的一种方式。
9.6.3 Udev 引导脚本
/etc/rc.d/init.d/udev 初始化脚本启动 udevd,触发内核已经创建的“冷插拔”设备,并等待所有 udev 规则执行完毕。该脚本也会取消默认的 uevent 处理程序 /sbin/hotplug。这是因为内核不再需要调用外部二进制程序。相反,udevd 会监听一个 netlink 套接字,以获取内核发出的 uevent 事件。
/etc/rc.d/init.d/udev_retry 脚本处理一些子系统的重新触发事件,这些子系统的规则可能依赖于 mountfs 脚本运行时才会挂载的文件系统 (特别地,独立挂载的 /usr 和 /var 文件系统可能导致这种依赖)。该脚本在 mountfs 脚本之后运行,因此这些规则 (如果被重新触发) 这一次应该能够成功执行。/etc/sysconfig/udev_retry 文件配置该脚本,其中除注释外的每个单词都被认为是一个需要重新触发的子系统名。为了找到某个设备的子系统,可以使用命令 udevadm info --attribute-walk <device>,这里
参阅第 9.3.2.3 节 “模块加载”了解更多关于模块加载和 udev 的信息。
9.6.4 配置系统时钟
setclock 脚本从硬件时钟读取时间,硬件时钟也常被称为 BIOS 时钟或互补金属氧化物半导体 (CMOS) 时钟。如果硬件时钟被设为 UTC 时间,该脚本会根据 /etc/localtime 文件 (它告知 hwclock 程序用户处于哪个时区),将硬件时钟的时间转换成本地时间。不存在确定硬件时钟是否为 UTC 的方法,因此这必须手动设置。
setclock 在引导后,由内核检测硬件功能时通过 udev 执行。可以用 stop 参数手动调用它,以将系统时间写入 CMOS 时钟。
如果您不确定您的硬件时钟是否设置为 UTC,运行 hwclock --localtime --show 命令,它会显示硬件时钟给出的当前时间。如果这个时间和您的手表显示的一致,则说明硬件时钟被设定为本地时间。相反,如果 hwclock 输出的时间不是本地时间,则硬件时钟很可能被设定为 UTC 时间。根据您的时区,在 hwclock 显示的时间上加减对应的小时数,进行进一步的验证。例如,如果您现在处于北美山地时区,即 GMT -0700,则需要在本地时间上加 7 小时,再进行比较。
将下面的配置文件中的 UTC 变量值改为 0 (零),表示硬件时钟没有设为 UTC。
执行以下命令,创建新的 /etc/sysconfig/clock 文件:
cat > /etc/sysconfig/clock << "EOF"
# Begin /etc/sysconfig/clock
UTC=1
# Set this to any options you might need to give to hwclock,
# such as machine hardware clock type for Alphas.
CLOCKPARAMS=
# End /etc/sysconfig/clock
EOF
在 https://www.linuxfromscratch.org/hints/downloads/files/time.txt 可以找到一个较好地介绍了如何在 LFS 中处理时间问题的 hint。它解释了与时区,UTC,以及 TZ 环境变量相关的问题。
注意
CLOCKPARAMS 和 UTC 参数也可以在 /etc/sysconfig/rc.site 文件中设置。
9.6.5 配置 Linux 控制台
本节讨论如何配置 console 引导脚本,使之正确设定键盘映射,控制台字体,以及控制台内核日志级别。如果不使用非 ASCII 字符 (如版权符号,英镑符号,或者欧元符号),而且使用美式键盘,则可以忽略本节的大多数内容。如果不创建本节的配置文件 (且 rc.site 中没有对应的设置),则 console 脚本什么也不做。
console 脚本读取 /etc/sysconfig/console 中的配置信息。它根据配置决定使用何种键映射和控制台字体。一些与特定语言相关的 HOWTO 文档可以帮助您进行配置;参阅 https://tldp.org/HOWTO/HOWTO-INDEX/other-lang.html。如果仍然有疑问,在/usr/share/keymaps 和 /usr/share/consolefonts 中寻找可用的键映射和控制台字体,并阅读 loadkeys(1) 和 setfont(8) man 页面,以确认应该传递给这两个程序的正确参数。
/etc/sysconfig/console 文件应该包含若干行,每一行的的格式都是:变量名=”值”。下列变量名会被识别:
LOGLEVEL
该变量指定被发送到控制台的内核消息日志级别,正如使用 dmesg 设置的那样。有效的级别是“1” (不输出内核消息) 到“8”之间的某个数。默认的日志级别是“7”。
KEYMAP
该变量指定传递给 loadkeys 程序的参数,它通常是需要加载的键映射名,例如“it”。如果这个变量没有设定,引导脚本不会运行 loadkeys,系统将使用内核的默认键映射。注意某些键映射有文件名相同的不同版本 (例如 cz 的和它的变体在 qwerty/ 和 qwertz/ 中,es 在 olpc/ 和 qwerty/ 中,以及 trf 在 fgGIod/ 和 qwerty/ 中)。在这种情况下,父目录名也要指定 (例如 qwerty/es),以保证加载正确的键映射。
KEYMAP_CORRECTIONS
这个 (很少使用的) 变量指定第二次调用 loadkeys 程序时使用的参数。如果现有的键映射不完全符合要求,需要进行微调,这个变量是很有用的。例如,如果需要为通常不包含欧元符号的键映射添加它,将这个变量设为“euro2”。
FONT
该变量指定传递给 setfont 程序的参数。通常它包含字体名,“-m”,以及需要加载的应用程序字符映射名。例如,为了加载 “lat1-16”字体和“8859-1”应用程序字符映射 (适用于美国),可以将该变量设置为“lat1-16 -m 8859-1”。在 UTF-8 模式下,内核使用应用程序字符映射将键映射中编组的 8 位键码转化为 UTF-8,因此 “-m” 参数的值应该被设定为键映射中编组的键码编码。
UNICODE
将该变量设为“1”,“yes”,或者“true”,可以将控制台设置于 UTF-8 模式。这对于基于 UTF-8 的 locale 很有用,但对于其他 locale 有害。
LEGACY_CHARSET
对于许多键盘布局,Kbd 软件包没有提供 Unicode 键映射。如果该变量被设定为一个可用的非 UTF-8 键映射的编码,console 引导脚本会在需要时将可用的键映射转换成 UTF-8。
一些例子:
- 对于非 Unicode 配置,一般只需要设置 KEYMAP 和 FONT 两个变量。例如,下面是一个波兰语配置:
cat > /etc/sysconfig/console << "EOF"
# Begin /etc/sysconfig/console
KEYMAP="pl2"
FONT="lat2a-16 -m 8859-2"
# End /etc/sysconfig/console
EOF
- 正如前文所述,有时需要微调一个现有的键映射。下面的例子为德语键映射添加欧元符号:
cat > /etc/sysconfig/console << "EOF"
# Begin /etc/sysconfig/console
KEYMAP="de-latin1"
KEYMAP_CORRECTIONS="euro2"
FONT="lat0-16 -m 8859-15"
UNICODE="1"
# End /etc/sysconfig/console
EOF
- 下面是一个使用 Unicode 的白俄罗斯语配置,白俄罗斯语有现有的 UTF-8 键映射:
cat > /etc/sysconfig/console << "EOF"
# Begin /etc/sysconfig/console
UNICODE="1"
KEYMAP="bg_bds-utf8"
FONT="LatArCyrHeb-16"
# End /etc/sysconfig/console
EOF
- 由于在上面的例子中使用了 512 个字形的 LatArCyrHeb-16 字体,在 Linux 控制台中不能使用明亮的颜色,除非使用了帧缓冲。如果希望在没有帧缓冲的情况下继续使用明亮的颜色,且不需要那些不属于自己母语的字符,可以使用专用于一种语言的 256 字形字体,配置文件如下:
cat > /etc/sysconfig/console << "EOF"
# Begin /etc/sysconfig/console
UNICODE="1"
KEYMAP="bg_bds-utf8"
FONT="cyr-sun16"
# End /etc/sysconfig/console
EOF
- 下面的例子展示了从 ISO-8859-1 到 UTF-8 的键映射自动转换,同时在 Unicode 模式下启用了死键:
cat > /etc/sysconfig/console << "EOF"
# Begin /etc/sysconfig/console
UNICODE="1"
KEYMAP="de-latin1"
KEYMAP_CORRECTIONS="euro2"
LEGACY_CHARSET="iso-8859-15"
FONT="LatArCyrHeb-16 -m 8859-15"
# End /etc/sysconfig/console
EOF
-
某些键映射有死键 (即,这些键本身不产生字符,而是在下一次按键产生的字符上附加音调) 或定义了组合规则 (例如在默认键映射中,“按下 Ctrl+. A E 得到 Æ”)。Linux-6.1.11 只有在被组合的不是多字节字符的情况下,才能正常解析死键和组合规则。这个缺陷不影响欧洲语言的键映射,因为在欧洲语言中要么是一个音调被附加到不带音调的 ASCII 字符上,要么是两个 ASCII 字符被组合在一起。然而,在 UTF-8 模式中,以希腊语为例,当某人要在“alpha”字符上附加音调时,就会出现问题。解决方法是要么不使用 UTF-8,要么安装 X 窗口系统,它处理输入时没有这个限制。
-
对于中文,日文,韩文,以及其他一些语言文字,无论如何配置 Linux 控制台,都不可能正常显示所需要的字符。这些语言的用户应该安装 X 窗口系统,能够覆盖所需要的字符的字体,以及合适的输入法 (如 SCIM 支持许多语言的输入)。
注意
/etc/sysconfig/console 文件只控制 Linux 字符控制台的本地化。它和 X 窗口系统,ssh 连接,或者串口终端中的键盘布局设置和终端字体毫无关系。在这些情况下,不存在上述的两项限制。
9.6.6 在引导时创建文件
有时,我们希望在引导时创建一些文件,例如可能需要 /tmp/.ICE-unix 目录。为此,可以在 /etc/sysconfig/createfiles 配置脚本中创建一项。该文件的格式包含在默认配置文件的注释中。
9.6.7 配置 Sysklogd 脚本
sysklogd 脚本启动 sysklogd 程序,这是 System V 初始化过程的一部分。-m 0 选项关闭 sysklogd 每 20 分钟写入日志文件的时间戳。如果您希望启用这个周期性时间戳标志,编辑 /etc/sysconfig/rc.site,将 SYSKLOGD_PARMS 定义为您希望的值。例如,如果要删除所有参数,将该变量设定为空:
SYSKLOGD_PARMS=
参阅 man syslogd 了解更多可用选项。
9.6.8 rc.site 文件
可选的 /etc/sysconfig/rc.site 文件包含了为每个 System V 引导脚本自动设定的配置。/etc/sysconfig/ 目录中 hostname,console,以及 clock 文件中的变量值也可以在这里设定。如果这些分立的文件和 rc.site 包含相同的变量名,则分立文件中的设定被优先使用。
rc.site 也包含自定义引导过程其他属性的参数。设定 IPROMPT 变量会启用引导脚本的选择性执行。其他选项在文件注释中描述。该文件的默认版本如下:
# rc.site
# Optional parameters for boot scripts.
# Distro Information
# These values, if specified here, override the defaults
#DISTRO="Linux From Scratch" # The distro name
#DISTRO_CONTACT="lfs-dev@lists.linuxfromscratch.org" # Bug report address
#DISTRO_MINI="LFS" # Short name used in filenames for distro config
# Define custom colors used in messages printed to the screen
# Please consult `man console_codes` for more information
# under the "ECMA-48 Set Graphics Rendition" section
#
# Warning: when switching from a 8bit to a 9bit font,
# the linux console will reinterpret the bold (1;) to
# the top 256 glyphs of the 9bit font. This does
# not affect framebuffer consoles
# These values, if specified here, override the defaults
#BRACKET="\\033[1;34m" # Blue
#FAILURE="\\033[1;31m" # Red
#INFO="\\033[1;36m" # Cyan
#NORMAL="\\033[0;39m" # Grey
#SUCCESS="\\033[1;32m" # Green
#WARNING="\\033[1;33m" # Yellow
# Use a colored prefix
# These values, if specified here, override the defaults
#BMPREFIX=" "
#SUCCESS_PREFIX="${SUCCESS} * ${NORMAL} "
#FAILURE_PREFIX="${FAILURE}*****${NORMAL} "
#WARNING_PREFIX="${WARNING} *** ${NORMAL} "
# Manually set the right edge of message output (characters)
# Useful when resetting console font during boot to override
# automatic screen width detection
#COLUMNS=120
# Interactive startup
#IPROMPT="yes" # Whether to display the interactive boot prompt
#itime="3" # The amount of time (in seconds) to display the prompt
# The total length of the distro welcome string, without escape codes
#wlen=$(echo "Welcome to ${DISTRO}" | wc -c )
#welcome_message="Welcome to ${INFO}${DISTRO}${NORMAL}"
# The total length of the interactive string, without escape codes
#ilen=$(echo "Press 'I' to enter interactive startup" | wc -c )
#i_message="Press '${FAILURE}I${NORMAL}' to enter interactive startup"
# Set scripts to skip the file system check on reboot
#FASTBOOT=yes
# Skip reading from the console
#HEADLESS=yes
# Write out fsck progress if yes
#VERBOSE_FSCK=no
# Speed up boot without waiting for settle in udev
#OMIT_UDEV_SETTLE=y
# Speed up boot without waiting for settle in udev_retry
#OMIT_UDEV_RETRY_SETTLE=yes
# Skip cleaning /tmp if yes
#SKIPTMPCLEAN=no
# For setclock
#UTC=1
#CLOCKPARAMS=
# For consolelog (Note that the default, 7=debug, is noisy)
#LOGLEVEL=7
# For network
#HOSTNAME=mylfs
# Delay between TERM and KILL signals at shutdown
#KILLDELAY=3
# Optional sysklogd parameters
#SYSKLOGD_PARMS="-m 0"
# Console parameters
#UNICODE=1
#KEYMAP="de-latin1"
#KEYMAP_CORRECTIONS="euro2"
#FONT="lat0-16 -m 8859-15"
#LEGACY_CHARSET=
9.6.8.1 自定义引导和关机脚本
LFS 引导脚本能够较为高效地引导和关闭系统,但是您仍然可以微调 rc.site 文件以进一步提高速度,或根据您的个人品味调整引导消息。为此,需要修改上面给出的/etc/sysconfig/rc.site 文件。
-
在引导脚本
udev的执行过程中,它会调用 udev settle,该命令需要一段时间才能完成。您的系统可能真的需要这段时间,也可能实际上并不需要,这和系统中的设备有关。如果您只有简单分区,且只有一块网卡,引导过程可能并不需要等待该命令。设定 OMIT_UDEV_SETTLE=y 可以跳过它。 -
引导脚本
udev_retry在默认配置下也会执行 udev settle。这只有在/var目录是独立挂载的文件系统时才有必要,因为系统时钟需要/var/lib/hwclock/adjtime文件。其他的自定义配置也可能需要等待 udev 完成,但在大多数配置中并没有这种必要。设定 OMIT_UDEV_RETRY_SETTLE=y 可以跳过它。 -
默认配置中,文件系统检查是静默的。这可能看上去像引导过程中的时延。设定变量 VERBOSE_FSCK=y 可以显示 fsck 的输出。
-
在重新启动系统时,您可能希望完全跳过进行文件系统检查的 fsck 命令。为此,可以创建一个文件
/fastboot,或者使用命令/sbin/shutdown -f -r now。反之,如果您希望在重新启动系统时强制检查所有文件系统,可以创建文件/forcefsck,或者在前面的 shutdown 命令中使用 -F 参数代替 -f。 -
设定变量 FASTBOOT=y 会在引导过程中完全禁止 fsck,直到移除该变量。从长远角度看,不推荐使用它。
-
通常,
/tmp目录中的所有文件都会在引导过程中被删除。如果其中有较多的文件或目录,可能导致引导过程中出现可观的延时。设定变量 SKIPTMPCLEAN=y 可以跳过删除这些文件的引导步骤。 -
在关机时,init 程序会向它启动的每个程序 (例如 agetty) 发送 TERM 信号,等待一段固定的时间 (默认是 3 秒),然后向每个进程发送 KILL 信号,再等待一次。对于所有未被自己的引导脚本关闭的进程,sendsignals 脚本会重复以上过程。init 的等待时间可以通过传递参数进行设定。例如,如果要完全取消 init 的等待时间,可以在关机或重新启动时传递 -t0 参数 (如同
/sbin/shutdown -t0 -r now)。sendsignals 中的等待时间可以通过设定参数 KILLDELAY=0 取消。
9.7 The Bash Shell Startup Files
Shell 程序 /bin/bash (之后简称 “shell”) 使用一组启动文件,以帮助创建运行环境。每个文件都有专门的用途,它们可能以不同方式影响登录和交互环境。/etc 中的文件提供全局设定。如果在用户主目录中有对应的文件存在,它们可能覆盖全局设定。
在成功登录后,/bin/login 读取 /etc/passwd 中的 shell 命令行,启动一个交互式登录 shell。通过命令行 (如 [prompt]$/bin/bash) 启动的 shell 是交互式非登录 shell。非交互 shell 通常在运行 shell 脚本时存在,它处理脚本,在执行命令的过程中不等待用户输入,因此是非交互的。
如果需要更多信息,阅读 Bash info 页面 (info bash) Bash Features 一章中的 Bash Startup Files 和 Interactive Shells 这两节。
登录 shell 会读取文件 /etc/profile 和 ~/.bash_profile。
下面给出最基本的 /etc/profile 文件,它设定本地语言支持所需的一些环境变量。正确设定它们有以下好处:
-
程序输出被翻译成本地语言
-
字符被正确分类为字母,数字,以及其他类型。在非英文 locale 中,这对于使 bash 正确接受命令行中的非 ASCII 字符是必要的。
-
根据所在地区惯例排序字母
-
适用于所在地区的默认纸张尺寸
-
正确格式化货币,时间,以及日期值
将下面的 *
Glibc 支持的所有 locale 可以用以下命令列出:
locale -a
字符映射可能有多个别名,例如 “ISO-8859-1” 也可以称为 “iso8859-1” 或者 “iso88591”。某些程序不能正确处理一些别名 (例如,“UTF-8”必须写作“UTF-8”才能识别,而不能识别“utf8”),因此在多数情况下,为了保险起见,最好使用 locale 的规范名称。为了确定规范名称,执行以下命令,将 *
LC_ALL=<locale 名> locale charmap
对于 “en_GB.iso88591” locale,以上命令输出:
ISO-8859-1
这样就最终确定 locale 应设置为 “en_GB.ISO-8859-1”。在将以上启发方法获得的 locale 添加到 Bash 启动文件之前,一定要进行下列测试:
LC_ALL=<locale 名> locale language
LC_ALL=<locale 名> locale charmap
LC_ALL=<locale 名> locale int_curr_symbol
LC_ALL=<locale 名> locale int_prefix
以上命令应该输出语言名称,选定 locale 使用的字符编码,本地货币符号,以及所在国家或地区的国际电话区号。如果以上某个命令失败并输出类似下面这样的消息,意味着您的 locale 在第 8.5 节 “Glibc-2.37”中没有安装,或者不被 Glibc 的默认安装支持。
locale: Cannot set LC_* to default locale: No such file or directory
如果出现了这种消息,您应该用 localedef 命令安装所需的 locale,或重新选择一个不同的 locale。后文假设 Glibc 没有输出类似错误消息。
其他软件包在 locale 名不符合它们的期望时可能工作不正常 (但未必输出错误消息)。在这种情况下,可以通过调研其他 Linux 发行版如何支持您的 locale,以获得一些有用的信息。
在确定了正确的 locale 设置后,创建 /etc/profile 文件:
cat > /etc/profile << "EOF"
# Begin /etc/profile
export LANG=<ll>_<CC>.<charmap><@modifiers>
# End /etc/profile
EOF
“C” (默认 locale) 和“en_US”(推荐美式英语用户使用的 locale) 是不同的。“C” locale 使用 US-ASCII 7 位字符集,并且将最高位“被设定”的字节视为无效字符。因此,ls 等命令会将它们显示为问号。另外,如果试图用 Mutt 或 Pine 发送包含这些字符的邮件,会发出不符合 RFC 标准的消息 (发出邮件的字符集会被标为“未知 8 位”)。因此,您只能在确信自己永远不需要 8 位字符时才能使用“C” locale。
一些程序不能很好地支持基于 UTF-8 的 locale。我们正在努力记录并 (如果可能的话) 修复它们。参阅 https://www.linuxfromscratch.org/blfs/view/11.3/introduction/locale-issues.html。
9.8 Creating the /etc/inputrc File
inputrc 文件是 Readline 库的配置文件,该库在用户从终端输入命令行时提供编辑功能。它的工作原理是将键盘输入翻译为特定动作。Readline 被 Bash 和大多数其他 shell,以及许多其他程序使用。
多数人不需要 Readline 的用户配置功能,因此以下命令创建全局的 /etc/inputrc 文件,供所有登录用户使用。如果您之后决定对于某个用户覆盖掉默认值,您可以在该用户的主目录下创建 .inputrc 文件,包含需要修改的映射。
关于更多如何编写 inputrc 文件的信息,参考 info bash 中 Readline Init File 一节。info readline 也是一个很好的信息源。
下面是一个通用的全局 inputrc 文件,包含解释一些选项含义的注释。注意注释不能和命令写在同一行。执行以下命令创建该文件:
cat > /etc/inputrc << "EOF"
# Begin /etc/inputrc
# Modified by Chris Lynn <roryo@roryo.dynup.net>
# Allow the command prompt to wrap to the next line
set horizontal-scroll-mode Off
# Enable 8-bit input
set meta-flag On
set input-meta On
# Turns off 8th bit stripping
set convert-meta Off
# Keep the 8th bit for display
set output-meta On
# none, visible or audible
set bell-style none
# All of the following map the escape sequence of the value
# contained in the 1st argument to the readline specific functions
"\eOd": backward-word
"\eOc": forward-word
# for linux console
"\e[1~": beginning-of-line
"\e[4~": end-of-line
"\e[5~": beginning-of-history
"\e[6~": end-of-history
"\e[3~": delete-char
"\e[2~": quoted-insert
# for xterm
"\eOH": beginning-of-line
"\eOF": end-of-line
# for Konsole
"\e[H": beginning-of-line
"\e[F": end-of-line
# End /etc/inputrc
EOF
9.9 Creating the /etc/shells File
shells 文件包含系统登录 shell 的列表,应用程序使用该文件判断 shell 是否合法。该文件中每行指定一个 shell,包含该 shell 相对于目录树根 (/) 的路径。
例如 chsh 使用该文件判断一个非特权用户是否可以修改自己的登录 shell。如果命令没有在 /etc/shell 中找到,就会拒绝修改操作。
这个文件对某些程序是必要的。例如 GDM 在找不到 /etc/shells 时不会填充登录界面,FTP 守护进程通常禁止那些使用未在此文件列出的终端的用户登录。
cat > /etc/shells << "EOF"
# Begin /etc/shells
/bin/sh
/bin/bash
# End /etc/shells
EOF