这个软件包的细节在 第 6.16.2 节 “Binutils 的内容” 中可以找到。
版权所有 © 1999-2019 Gerard Beekmans
版权所有 © 1999-2019, Gerard Beekmans
保留所有权利。
本书依照 Creative Commons License 许可证发布。
从本书中提取的计算机命令按照 MIT License 许可证发布。
Linux® 是 Linus Torvalds 的注册商标。
从 1998 年起,我踏上了学习和深入理解 Linux 的旅程。 当时我刚刚安装了我的第一个 Linux 发行版, 并迅速被 Linux 背后的整个设计理念和哲学所折服。
为了完成一项工作,人们总是能提出很多不同的方法。 对于 Linux 发行版来说,情况也是这样。多年来诞生了许多发行版, 其中一些仍然生存,另外一些已经被其他发行版吸收,还有的已经消亡, 成为我们的回忆。这些发行版各有特色,以满足它们的目标人群的需求。 因为这些发行版都是已经存在的,能够达成同一目的的手段, 我开始意识到并不需要将自己的思维约束在发行版这一种实现方法上。 在发现 Linux 之前,我们只能忍受其他操作系统的种种不足, 因为我们没有其他选择,操作系统的行为不以我们的意志为转移。 然而,自由选择的理念随着 Linux 的诞生而出现。如果你不喜欢某种行为, 就可以自由地改变它。这在 Linux 世界中甚至是受到鼓励的。
我曾经尝试了许多发行版,但无法做出最终决定。它们各有特色, 都是很不错的系统。这里不存在对与错的问题,而是系统是否符合个人口味的问题。 在各种选择中,看上去并没有一种发行版能完美地符合我的要求。 因此我开始创造自己的 Linux 系统,以完全符合我的个人品味。
为了构建出真正属于我自己的系统,我决定从源代码编译所有东西, 而不使用预先编译的二进制包。 这个“完美的” Linux 系统将会兼具不同系统的优点, 同时扬弃它们的不足。这个想法初听上去非常可怕。 然而,我仍然坚信这个系统可以被构建出来。
在整理并解决了循环依赖和编译错误等问题后, 我终于构建出自己定制的 Linux 系统。 它完全可以工作,并且像当时的其他 Linux 系统一样完美可用。 不同的是,这是我自己的创造,亲手组装出这样的系统是非常有成就感的。 唯一能够让我更开心的事情是亲自编写一个软件系统。
当我向其他 Linux 社区成员推广我的目标和经验时, 大家似乎对这些想法很有兴趣。显而易见, 这些自行定制的 Linux 系统不仅能够满足用户的特殊需求, 而且对于程序员和系统管理员来说是提高 Linux 技能的理想学习机会。 随着越来越多的人对这一主题的关注, Linux From Scratch 项目诞生了。
这本 Linux From Scratch 手册是这一项目的核心内容, 它将提供亲自设计和构建系统所需的背景知识和操作步骤。 本书提供了一个构建能够正常工作的系统的样板,您可以自由地调整本书中的命令, 来满足您自己的要求,这也是本项目的重要组成部分。您始终掌握自己的系统, 我们只是在您起步时提供微小的帮助。
我真诚地祝愿您能够在您自己的 Linux From Scratch 系统上体验快乐, 并享受拥有这样一个真正属于自己的系统所带来的各种乐趣。
--
Gerard Beekmans
gerard AT linuxfromscratch D0T org
您可能有许多阅读本书的理由。许多人首先会问: “为什么要不辞辛苦地手工从头构建一个 Linux 系统, 而不是直接下载并且安装一个现成的?”
本项目存在的一项重要原因就是,它能够帮助您学习 Linux 系统的内部是如何运作的。 构建 LFS 系统的过程将展示 Linux 系统的工作原理, 以及其各组成部分的协作和依赖关系。最棒的是,有了这些经验, 您将能够定制 Linux 系统,使其满足您独一无二的需求。
LFS 的另一个关键优势是,它允许您更好地控制您的系统, 而不用依赖于其他人的 Linux 实现。您就像坐在驾驶座上一样, 完全掌控系统的各个方面。
LFS 允许您创建非常紧凑的 Linux 系统。在安装传统的 Linux 发行版时, 您往往不得不安装一大堆可能永远不会用到,甚至完全无法理解其必要性的程序。 它们会浪费系统资源。您可能以为,有了现代的大容量硬盘和高速 CPU, 就不需要考虑资源耗费的问题。然而,在一些情况下,即使不考虑其他问题, 仅仅存储空间的约束就十分紧张。可引导 CD ,USB 启动盘或者嵌入式系统就是典型代表。 在这些领域中, LFS 是十分有用的。
自行定制的 Linux 系统在安全方面也优势很大。在从源码编译整个系统的过程中, 您有机会审核所有的代码,并安装您需要的安全补丁。 您不需要像往常那样等待其他人编译一个修复了安全漏洞的二进制包。 另外,除非您亲自检查并应用了补丁,您无法保证新的二进制包在编译过程中没有出问题, 并且正确修补了安全漏洞。
Linux From Scratch 的目标是构建一个完整并基本可用的系统。 如果您不想从零构建您自己的 Linux 系统,那么您可能不会从本书提供的信息中受益。
此外,构建 LFS 系统还有很多好处,这里就不一一列举了。在所有原因中, 最重要的是,在您编译和使用 LFS 的实践中,您将了解很多威力巨大的信息和知识。
LFS 的主要目标架构是 AMD/Intel 的 x86 (32 位) 和 x86_64 (64 位) CPU 。此外, 如果对本书中的一些指令作适当的修改,它们也应该适用于 Power PC 和 ARM 架构的 CPU。为了在一块这样的 CPU 上成功构建 LFS 系统,您首先需要一个能够在 CPU 上正常运行的 Linux 系统。例如,一个已经构建好的 LFS 系统,或者 Ubuntu、Red Hat/Fedora、SuSE 等支持您的硬件架构的发行版。 另外,32 位发行版也能够在 64 位的 AMD/Intel 计算机上正常运行, 并作为 LFS 的构建环境。
关于 64 位系统,我们需要说明,与 32 位系统相比,64 位系统所需的空间稍大一些, 对于大多数程序来说,运行速度也仅仅稍快一些,没有特别明显的优势。例如, 在一块 Core2Duo CPU 上构建 LFS-6.5 版本时,我们得到的实验数据为:
架构 构建时间 系统大小
32 位 198.5 分钟 648 MB
64 位 190.6 分钟 709 MB
可以看出,64 位系统仅仅比 32 位系统快 4% ,体积则大了 9% 。因此, 并不需要特意追求 64 位系统。然而如果您拥有超过 4GB 的内存, 或需要操作大于 4GB 的数据,64 位系统的优势就相当关键了。
以上讨论仅适用于当时的硬件。现代的 64 位系统比以前快得多, 因此 LFS 作者推荐尽量使用 64 位系统构建 LFS。
完全按照本书构建的 LFS 系统是一个“纯粹的” 64 位系统。 换句话说,它只能运行 64 位可执行程序。 构建一个“multi-lib” 系统需要将许多程序编译两次,一次编译为 32 位, 另一次编译为 64 位。本书不涉及这方面的内容, 因为它与本书提供一个最基本的 Linux 系统的教育目标相冲突。 您可以参考 Cross Linux From Scratch 项目获得关于这个高级话题的更多信息。
这个页面存在一些问题,在中文环境中无法正常显示。 需要将浏览器的语言设置改成英文才能阅读该页面。
LFS 的结构尽可能遵循 Linux 的各项标准。主要的标准有:
Linux Standard Base (LSB) Version 5.0 (2015)
LSB 由 4 个独立的标准组成:Core、Desktop、Runtime Language 和 Imaging。除了通用要求外,还有架构特定的要求。另外, 还有两个用于测试用途的标准:Gtk3 和 Graphics。 LFS 试图遵循 LSB 对前一节讨论的那些架构的要求。
许多人不认同 LSB 的要求。 定义 LSB 的主要目的是保证专有软件能够在满足 LSB 的系统上正常运行。 然而 LFS 是基于源代码的,用户拥有完全的控制权, 有权选择不安装 LSB 要求的软件包。
创建一个能够通过 LSB 认证测试的完整 LFS 系统是可行的, 但需要安装大量超过 LFS 范畴的额外软件包。 在 BLFS 中可以找到这些软件包的安装说明。
LSB Core: |
Bash, Bc, Binutils, Coreutils, Diffutils, File, Findutils, Gawk, Grep, Gzip, M4, Man-DB, Ncurses, Procps, Psmisc, Sed, Shadow, Tar, Util-linux, Zlib |
LSB Desktop: |
无 |
LSB Runtime Languages: |
Perl |
LSB Imaging: |
无 |
LSB Gtk3 和 LSB Graphics (试用): |
无 |
LSB Core: |
At, Batch (At 的一部分), Cpio, Ed, Fcrontab, Initd-tools, Lsb_release, NSPR, NSS, PAM, Pax, Sendmail (或 Postfix,或 Exim), time |
LSB Desktop: |
Alsa, ATK, Cairo, Desktop-file-utils, Freetype, Fontconfig, Gdk-pixbuf, Glib2, GTK+2, Icon-naming-utils, Libjpeg-turbo, Libpng, Libtiff, Libxml2, MesaLib, Pango, Xdg-utils, Xorg |
LSB Runtime Languages: |
Python, Libxml2, Libxslt |
LSB Imaging: |
CUPS, Cups-filters, Ghostscript, SANE |
LSB Gtk3 和 LSB Graphics (试用): |
GTK+3 |
我们之前指出,LFS 的目标是构建一个完整且基本可用的系统。 这包含所有重复构建 LFS 系统所需的软件包, 以及在 LFS 提供的相对小的基础上根据用户需求, 继续定制更完备的系统所必须的软件包。 因此,LFS 并不是最小可用系统。 LFS 中一些重要的软件包甚至不是必须安装的。下面列出了选择每个软件包的理由。
Acl
这个软件包包含管理访问控制列表(ACL)的工具, 用来对文件和目录提供更细粒度的访问权限控制。
Attr
这个软件包包含管理文件系统对象的扩展属性的程序。
Autoconf
这个软件包包含能根据软件开发者提供的模板,自动生成配置源代码的 shell 脚本的程序。如果修改了软件包的构建过程, 一般需要该软件包的支持才能重新构建被修改的软件包。
Automake
这个软件包包含能根据软件开发者提供的模板,自动生成 Makefile 的程序。 如果修改了软件包的构建过程, 一般需要该软件包的支持才能重新构建被修改的软件包。
Bash
这个软件包为系统提供一个 LSB core 要求的 Bourne Shell 接口。 与其他 shell 软件包相比,它更加常用, 且在基本 shell 功能的基础上有更好的扩展能力, 因此在各种 shell 软件包中选择了它。
Bc
这个软件包提供了一个任意精度数值处理语言。 在编译 Linux 内核时需要该软件包。
Binutils
该软件包包含链接器、汇编器,以及其他处理目标文件的工具。 编译 LFS 系统以及运行在 LFS 之上的大多数软件包都需要该软件包中的程序。
Bison
这个软件包提供了 yacc (Yet Another Compiler Compiler) 的 GNU 版本。 一些 LFS 程序的编译过程需要该软件包。
Bzip2
这个软件包包含用于压缩和解压缩文件的程序。 许多 LFS 软件包的解压需要该软件包。
Check
这个软件包包含用于其他程序的测试控制工具。它只安装在临时工具链中。
Coreutils
这个软件包包含一些用于查看和操作文件和目录的基本程序。 这些程序被用于在命令行下管理文件,以及每个 LFS 软件包的安装过程。
DejaGNU
这个软件包包含一个测试其他程序的框架。它只安装在临时工具链中。
Diffutils
这个软件包包含用于显示文件或目录之间的差异的程序。 这些程序可以被用于创建补丁,很多软件包的编译过程也需要该软件包。
E2fsprogs
这个软件包包含用于处理 ext2, ext3 和 ext4 文件系统的工具。 它们是 Linux 支持的最常用且久经考验的文件系统。
Eudev
这个软件包是一个设备管理器,它随着系统中硬件设备的增加或移除, 动态地控制 /dev 目录中的设备文件。
Expat
这个软件包包含一个相对轻量级的 XML 解析库。 Perl 模块 XML::Parser 需要该软件包。
Expect
这个软件包包含一个自动和其他交互程序交互的脚本执行程序。 一般用它测试其他程序。该软件包只被安装在临时工具链中。
File
这个软件包包含用于判定给定文件的类型的工具。 一些软件包需要它才能被编译。
Findutils
这个软件包包含用于在文件系统中寻找文件的程序。 它被许多软件包的编译脚本使用。
Flex
这个软件包包含用于生成词法分析器的程序。它是 lex (lexical analyzer) 程序的 GNU 版本。许多 LFS 软件包的编译过程需要该软件包。
Gawk
这个软件包包含用于操作文本文件的程序。它是 awk (Aho-Weinberg-Kernighan) 的 GNU 版本。它被许多其他软件包的编译脚本使用。
Gcc
这个软件包是 GNU 编译器的集合。它包含 C 和 C++ 的编译器, 以及其他一些在 LFS 中不会涉及的编译器。
GDBM
这个软件包包含 GNU 数据库管理库。LFS 的另一个软件包 Man-DB 需要该软件包。
Gettext
这个软件包包含用于许多其他软件包的国际化和本地化的工具和库。
Glibc
这个软件包包含主要的 C 语言库。 Linux 程序没有该软件包的支持根本无法运行。
GMP
这个软件包包含一些数学库,提供了许多用于任意精度算术的有用的函数。 编译 Gcc 需要该软件包。
Gperf
这个软件包包含一个能够根据键值集合生成完美散列函数的程序。 Eudev (或 systemd) 需要该软件包。
Grep
这个软件包包含在文本中搜索指定模式的程序。 它被多数软件包的编译脚本所使用。
Groff
这个软件包包含用于处理和格式化文本的程序。它们的一项重要功能是生成 man 页面。
GRUB
这个软件包是 Grand Unified Boot Loader。Linux 可以使用其他引导加载器, 但 GRUB 最灵活。
Gzip
这个软件包包含用于压缩和解压缩文件的程序。 许多 LFS 软件包的解压需要该软件包。
Iana-etc
这个软件包包含网络服务和协议的描述数据。 网络功能的正确运作需要该软件包。
Inetutils
这个软件包包含基本网络管理程序。
Intltool
这个软件包包含能够从源代码中提取可翻译字符串的工具。
IProute2
这个软件包提供了用于 IPv4 和 IPv6 网络的基础和高级管理程序。 和另一个常见的网络工具包 net-tools 相比, 它具有管理 IPv6 网络的能力。
Kbd
这个软件包包含键盘映射文件,用于非美式键盘的键盘工具, 以及一些控制台字体。
Kmod
这个软件包包含用于管理 Linux 内核模块的程序。
Less
这个软件包包含一个很好的文本文件查看器,它支持在查看文件时上下滚动。 此外, Man-DB 使用该软件包来显示 man 页面。
Libcap
这个软件包实现了用于访问 Linux 内核中 POSIX 1003.1e 权能字功能的用户空间接口。
Libelf
Elfutils 项目提供了用于 ELF 文件和 DWARF 数据的工具和库。 该软件包的大多数工具已经由其他软件包提供, 但使用默认(也是最高效的)配置构建 Linux 内核时, 需要使用该软件包的库。
Libffi
这个软件包实现了一个可移植的高级编程接口,用于处理不同的调用惯例。 某些程序在编译时并不知道如何向函数传递参数, 例如解释器在运行时才得到函数的参数个数和类型信息。它们可以使用 libffi 作为解释语言和编译语言之间的桥梁。
Libpipeline
Libpipeline 包含一个能够灵活、方便地操作子进程流水线的库。Man-DB 软件包要求这个库。
Libtool
这个软件包包含 GNU 通用库支持脚本。它将共享库的使用封装成一个一致、 可移植的接口。在其他 LFS 软件包的测试套件中需要该软件包。
Linux Kernel
这个软件包就是操作系统。我们平常说的“GNU/Linux” 环境中的“Linux”就指的是它。
M4
这个软件包包含通用的文本宏处理器。它被其他程序用于构建工具。
Make
这个软件包包含用于指导软件包编译过程的程序。 LFS 中几乎每个软件包都需要它。
Man-DB
这个软件包包含用于查找和浏览 man 页面的程序。与 man 软件包相比, 该软件包的国际化功能更为强大。该软件包提供了 man 程序。
Man-pages
这个软件包包含基本的 Linux man 页面的实际内容。
Meson
这个软件包提供一个自动编译软件的工具。 它的设计目标是最小化软件开发者不得不用于配置构建系统的时间。
MPC
这个软件包包含用于复数算术的函数。Gcc 需要该软件包。
MPFR
这个软件包包含用于多精度算术的函数。Gcc 需要该软件包。
Ninja
这个软件包包含一个注重执行速度的小型构建系统。 它被设计为读取高级构建系统输出的配置文件,并以尽量高的速度运行。
Ncurses
这个软件包包含用于处理字符界面的不依赖特定终端的库。 它一般被用于为菜单系统提供光标控制。一些 LFS 软件包需要该软件包。
Openssl
这个软件包包含关于密码学的管理工具和库, 它们被用于为 Linux 内核等其他软件包提供密码学功能。
Patch
这个软件包包含一个通过 补丁 文件修改或创建文件的程序。补丁文件通常由 diff 程序创建。 一些 LFS 软件包的编译过程需要该软件包。
Perl
这个软件包是运行时语言 PERL 的解释器。 几个 LFS 软件包的安装和测试过程需要该软件包。
Pkg-config
这个软件包提供一个查询已经安装的库和软件包的元数据信息的程序。
Procps-NG
这个软件包包含用于监控系统进程的程序,对系统管理非常有用。另外 LFS 启动脚本也需要该软件包。
Psmisc
这个软件包包含一些显示当前运行的系统进程信息的程序, 对系统管理非常有用。
Python 3
这个软件包提供了一种解释性语言支持,它围绕代码可读性这一重点而设计。
Readline
这个软件包包含一组库,提供命令行编辑和历史记录支持。 Bash 需要该软件包。
Sed
这个软件包可以在没有文本编辑器的情况下编辑文本文件。另外,大多数 LFS 软件包的配置脚本需要该软件包。
Shadow
这个软件包包含用于安全地处理密码的程序。
Sysklogd
这个软件包包含用于记录系统消息的程序, 这些消息包括内核或者守护进程在异常事件发生时的提示。
Sysvinit
这个软件包提供init程序, 在 Linux 系统中它是其他所有进程的祖先。
Tar
这个软件包提供存档和提取功能,几乎每个 LFS 软件包都需要它才能被提取。
Tcl
这个软件包包含在 LFS 软件包的测试套件中广泛使用的工具控制语言 (Tool Command Language)。该软件包只安装在临时工具链中。
Texinfo
这个软件包包含用于阅读、编写和转换 info 页面的程序。它被用于许多 LFS 软件包的安装过程中。
Util-linux
这个软件包包含许多工具程序,其中有处理文件系统、终端、 分区和消息的工具。
Vim
这个软件包包含一个编辑器,由于它与经典的 vi 编辑器相兼容, 且拥有许多强大的功能,我们选择这个编辑器。编辑器的选择是非常主观的, 如果希望的话,读者可以选择其他编辑器。
XML::Parser
这个软件包是和 Expat 交互的 Perl 模块。
XZ Utils
这个软件包包含用于压缩和解压缩文件的程序。在所有这类程序中, 该软件包提供了最高的压缩率。 该软件包被用于解压 XZ 或 LZMA 格式的压缩文件。
Zlib
这个软件包包含一些程序使用的压缩和解压缩子程序。
构建 LFS 系统不是一项简单的任务。它需要您运用足够丰富的 Unix 系统管理知识来解决构建过程中的问题,并正确执行本书给出的命令。特别是, 您至少需要拥有使用命令行 (shell) 来复制或移动文件和目录, 列出目录或文件的内容,以及切换当前工作目录的能力。 另外,我们希望您能够拥有一定水平的使用和安装 Linux 软件的知识。
由于本书假定您至少具备上述基本技能, 任何 LFS 支持论坛不太可能在这些领域为您提供支援。 如果您的问题是关于这些基础知识的,一般会被忽略,或者被提供一份 LFS 基本知识预习书单作为答案。
在您开始构建 LFS 系统之前,我们建议您阅读下列材料:
Software-Building-HOWTO http://www.tldp.org/HOWTO/Software-Building-HOWTO.html
这是一份关于在 Linux 环境编译和安装“一般的” Unix 软件包的详细指南。 尽管这份文档比较老,它仍然较好地总结了编译和安装软件的基本技巧。
Beginner's Guide to Installing from Source http://moi.vonos.net/linux/beginners-installing-from-source/
这份指南很好地总结了从源代码编译软件的基本技能和技巧。
由于译者水平、时间有限,可能不会翻译这两篇文档。
为了您能更容易地跟上进度,首先说明本书的排版惯例。 本节包含本书中若干排版格式的示例。
./configure --prefix=/usr
类似上面这样排版的文本应该被绝对准确地输入,除非上下文另有说明。 在解释命令的含义时,我们也用这种格式引用被解释的命令。
某些情况下,我们会将逻辑上的一行文本拆分成两行或多行, 此时行末会添加一个反斜线。
CC="gcc -B/usr/bin/" ../binutils-2.18/configure \ --prefix=/tools --disable-nls --disable-werror
注意反斜线后面必须紧跟回车。反斜线后如果存在空格和制表符之类的空白字符, 将会得到不正确的结果。
install-info: unknown option '--dir-file=/mnt/lfs/usr/info/dir'
以上文本(等宽字体)展示屏幕输出,一般是某个命令运行的结果。 等宽字体也被用于显示文件名,例如 /etc/ld.so.conf
。
强调文本
这样的文本在书中有一些不同目的。主要目的是强调重点。
http://www.linuxfromscratch.org/
以上格式被用于超链接,无论是链接到 LFS 社区内还是社区外。 链接可能指向教程、下载链接或其他网站。
cat > $LFS/etc/group << "EOF"
root:x:0:
bin:x:1:
......
EOF
以上格式被用于创建配置文件。第一行指令告诉系统,创建文件 $LFS/etc/group
并把用户输入的所有东西写入文件内容, 直到用户输入文件结束符
(EOF) 。因此,这些文本一般按原状输入。
<需要替换的文本>
尖括号包含的文本不应该直接输入,而应该替换成合适内容。 在复制粘贴到命令行时要注意替换。
[可选的文本]
方括号包含的文本是可选的,根据您的需要决定是否输入。
passwd(5)
以上格式被用于引用特定的手册 (man) 页面。数字表明系统手册中的某一节。例如, passwd 有两个 man 页面。LFS
安装命令将会把它们安装在 /usr/share/man/man1/passwd.1
和 /usr/share/man/man5/passwd.5
。当本书使用 passwd(5)
时,我们特指 /usr/share/man/man5/passwd.5
。 man passwd
会显示它找到的第一个匹配“passwd” 的 man 页面,即 /usr/share/man/man1/passwd.1
。 对于本例,您需要执行
man 5 passwd
才能阅读指定的 man 页面。 多数 man 页面在各个章节中并不存在重名,因此 man <program
name>
一般是足够的。
这是一个译注,就是原文中并不存在,由译者添加的额外说明。 注意原文有时也使用这种框, 只有标题是“译注”的框才是译注。
本书被分为以下三个部分。
第一部分解释了一些安装 LFS 时的重要注意事项。同时,提供了本书的基本信息。
第二部分描述了如何进行构建的准备工作,包括分区、下载软件包、 编译临时工具链等。
第三部分引导用户完成构建 LFS 系统的整个过程, 包括逐个编译和安装所有的软件包,设置启动脚本,以及安装内核。 得到的 Linux 系统是一个基本系统,在它之上可以继续编译其他软件, 以扩展系统,更好地满足用户需求。在本书的最后,给出了一个便于使用的引用列表, 包括本书中安装的所有软件、库和其他重要文件。
用于构建 LFS 系统的软件处于不断更新和改进的过程中。在本书发布后, 一些软件可能公布安全警告和漏洞修复补丁。 为了确认本书提供的软件包版本或者构建命令是否需要修正, 以反映最新的安全补丁或其他漏洞修复,请在开始构建 LFS 之前阅读 http://www.linuxfromscratch.org/lfs/errata/9.0/ 。您应该关注勘误表列出的所有修正项, 并在构建过程中注意对本书的相关章节进行修正。
LFS 系统必须在一个已经安装好的 Linux 发行版 (如 Debian、 OpenMandriva、Fedora 或者 openSUSE) 中构建。这个安装好的 Linux 系统 (称为宿主) 提供包括编译器、链接器和 shell 在内的必要程序, 作为构建新系统的起点。 请在安装发行版的过程中选择“development” (开发) 选项,以使用这些工具。
您也可以选择不安装一个单独的发行版,而是使用某个商业发行版的 LiveCD 。
本书的第 2 章描述了如何创建一个新的 Linux 本地分区和文件系统,新的 LFS 系统将在该文件系统中被编译和安装。 第 3 章 列举了在构建 LFS 系统的过程中必须下载的软件包和补丁, 并解释了在新文件系统中存储它们的方法。 第 4 章讨论工作环境的正确配置。 请仔细阅读第 4 章,因为它解释了您在开始 第 5 章 及后续章节的工作前必须了解的一些重要问题。
第 5 章 解释了安装大量软件包以形成基本开发套件 (或称为工具链) 的过程, 之后在 第 6 章 中将用工具链构建真正的系统。其中一些包需要解决循环依赖问题 ——例如,为了编译一个编译器,您首先需要一个编译器。
另外, 第 5 章 也展示了构建第一轮工具链的过程,包括 Binutils 和 GCC (“第一轮”表示这两个核心软件包会被重新安装一次)。 下一步是构建 Glibc ,即 C 运行库。我们将用第一轮工具链中的程序编译 Glibc 。之后,我们构建第二轮工具链, 第二轮工具链将会动态链接到新编译的 Glibc 上。 我们将使用第二轮工具链构建 第 5 章 中的其余软件包。 在这些工作完成后,除正在运行的内核外, LFS 的安装过程将不再依赖于作为宿主的发行版。
我们努力将新构造的系统从宿主发行版分离出来。这个过程看上去很繁琐, 我们将会在 第 5.2 节 “工具链技术说明” 完整地从技术上解释这样做的必要性。
在第 6 章中, 我们将构建完整的 LFS 系统。我们使用 chroot (改变根目录) 命令进入一个虚拟化环境,其中根目录被设置为 LFS 文件系统。 这与重启计算机并指示内核将 LFS 文件系统挂载为根文件系统很相似, 然而由于创建一个可引导的系统需要一些尚未完成的额外工作, 我们选择使用 chroot 。“换根” 的最大好处是,我们在构建 LFS 的过程中,可以继续使用宿主系统。 在等待软件包编译的过程中,您可以继续正常使用计算机。
为了完成安装,我们在 第 7 章 中进行系统的基本设置,在 第 8 章 中配置内核和引导加载器。最后,第 9 章 包含在阅读完本书后继续体验 LFS 的相关信息。在完成本书的所有流程后, 重启计算机即可进入新的 LFS 系统。
以上是 LFS 构建过程的简要介绍, 针对特定步骤的详细信息将在之后章节以及软件包的简介中讨论。 在您踏上 LFS 的构建之旅后,就能逐步理清这些看上去很复杂的步骤, 每一步都将变得非常清晰。
以下是自本书上一次发布之后,发生变化的软件包的清单。
升级:
Bc 2.1.3
Bison-3.4.1
Bzip2-1.0.8
Coreutils-8.31
E2fsprogs-1.45.3
Eudev-3.2.8
Expat-2.2.7
File-5.37
Gawk-5.0.1
GCC-9.2.0
Gettext-0.20.1
Glibc-2.30
GRUB-2.04
IPRoute2-5.2.0
Kbd-2.2.0
Less-551
LFS-Bootscripts-20190524
Libcap-2.27
Libelf-0.177 (from elfutils)
Linux-5.2.8
Man-DB-2.8.6.1
Man-pages-5.02
Meson-0.51.1
Openssl-1.1.1c
Perl-5.30.0
Python-3.7.4
Shadow-4.7
SysVinit-2.95
Tar-1.32
Texinfo-6.6
Tzdata-2019b
Util-Linux-2.34
Vim-8.1.1846
增加:
移除:
这是 Linux From Scratch 手册的 9.0 版本,发布于 2019 年 9 月 1 日。 如果这个版本已经发布了六个月或更久, 可能已经发布了更好的新版本。通过 http://www.linuxfromscratch.org/mirrors.html 访问一个 LFS 镜像站, 即可查询是否有新版本。。
下面是本书自上一版本发布以来的更新日志。
更新日志记录:
2019-09-01
[bdubbs] - LFS-9.0 发布。
2019-08-14
2019-08-04
[bdubbs] - 向一个 glibc 头文件添加包含文件预处理指令, 修复 Linux-5.2 引入的一个问题。
2019-08-03
2019-07-21
2019-07-14
[bdubbs] - 修复 binutils-2.32 gold 链接器测试套件。修复 #4498。
[bdubbs] - 更新到 tzdata-2019b。修复 #4492。
[bdubbs] - 更新到 python3-3.7.4。修复 #4496。
[bdubbs] - 更新到 meson-0.51.1。修复 #4497。
[bdubbs] - 更新到 iproute2-5.2.0。修复 #4495。
[bdubbs] - 更新到 grub-2.04。修复 #4494。
[bdubbs] - 更新到 linux-5.2.1。修复 #4493。
[bdubbs] - 更新到 bc-2.1.0。修复 #4436。
[bdubbs] - 更新到 bzip2-1.0.8。修复 #4499。
2019-06-29
2019-06-24
[renodr] - 修复将 Check 的文档安装到带有版本号的目录时出现的问题。 感谢 Ryan Marsaw 报告该问题。修复方法是删除未被识别的 --docdir 选项,用 make install 命令的 "docdir=" 变量代替它。
2019-06-18
[renodr] - 更新到 linux-5.1.11。修复 SOCK PANIC 漏洞。修复 #4485。
2019-06-16
2019-06-08
[renodr] - 使得在第 6 章中构建 Util-linux 前删除符号链接的命令只出现在 systemd 版本中。
2019-06-03
2019-05-24
[dj] - 改进 LFS 启动脚本的外观。
2019-05-19
2019-05-03
2019-04-23
[bdubbs] - 修改 Perl 代码,使得最新版本的 gcc 能正确构建它。
2019-04-20
2019-04-15
2019-03-27
[bdubbs] - 降级到 meson-0.49.2。
2019-03-26
2019-03-25
2019-03-13
[xry111] - 更新软件包的内容和简要描述。修复 #4443。
2019-03-12
2019-03-05
[bdubbs] - 更新到 linux-5.0。修复 #4437。
2019-03-01
2019-03-01
[bdubbs] - LFS-8.4 发布。
如果在构建 LFS 的过程中您遇到了任何问题,或是存在疑问, 或者觉得书中存在拼写错误,请先参考常见问题列表 (FAQ)。它位于 http://www.linuxfromscratch.org/faq/。
服务器 linuxfromscratch.org
管理了若干用于 LFS
项目开发过程的邮件列表,其中有主要的开发列表和技术支持列表, 以及其他辅助列表。如果 FAQ 不能解决您的问题,
您可以访问 http://www.linuxfromscratch.org/search.html
在邮件列表中进行搜索。
如果希望了解各个邮件列表的信息,如订阅方法、过往邮件存档等,访问 http://www.linuxfromscratch.org/mail.html。
LFS 社区的几个成员通过因特网中继聊天系统 (IRC) 提供支援。 在使用这一渠道之前,首先保证您的问题并没有被 LFS
FAQ 和邮件列表解决。您可以在 irc.freenode.net
找到
IRC 网络, 支持频道的名字是 #LFS-support。
LFS 项在全世界分布着若干镜像站, 您可以通过这些镜像站更容易地访问 LFS 网站,并下载需要的软件包。 请访问 LFS 网站 http://www.linuxfromscratch.org/mirrors.html 来得到最新的镜像站点列表。
请直接将您的问题和评论发送到某个 LFS 邮件列表(上面已经给出)。
西安电子科技大学的 LFS 软件包镜像站位于 https://linux.xidian.edu.cn/mirrors/lfs (只提供校内访问),中国科学技术大学的 LFS 软件包镜像站位于 https://mirrors.ustc.edu.cn/lfs。请在 https://linux.xidian.edu.cn/git/xry111/LFS-book/issues 报告翻译错误。
如果您在按照本书工作的过程中遇到任何问题或者疑问,请先阅读位于 http://www.linuxfromscratch.org/faq/#generalfaq 的常见问题列表,一般来说可以找到答案。 如果您的问题没有被 FAQ 解决,试着找到问题的根源。 这个指南指出了一些疑难问题的排查思路:http://www.linuxfromscratch.org/hints/downloads/files/errors.txt。
如果 FAQ 中没有您的问题,访问 http://www.linuxfromscratch.org/search.html, 在邮件列表中搜索。
我们也有一个吸引人的 LFS 社区,社区成员愿意通过邮件列表和 IRC (见 第 1.4 节 “相关资源”) 提供支援。然而, 我们每天都会得到一大堆明明在 FAQ 或者邮件列表中能找到答案的问题。 因此,为了使得技术支持的效能最大化,您需要自己先对问题进行一些研究。这样, 我们就能够集中精力解决最特殊的支援需求。如果您的研究得不到结果, 请您在求助时附带下面列出的全部相关信息。
除了简要描述您遇到的问题外,您应该在求助邮件中附带下列必要信息。
LFS 手册的版本 (这本书的版本是 9.0 )
构建 LFS 时使用的宿主发行版名称和版本
宿主需求检查 脚本的输出
出现问题的软件包或书内章节
程序输出的原始错误消息,或者出现的症状
您是否进行了超出本书内容的操作
有超出本书内容的操作, 并不意味着我们就不会协助您。 无论如何, LFS 强调个人体验。 在求助信中说明您对本书给出构建过程的改动, 有助于我们猜测和确定问题的可能原因。
如果在运行 configure
脚本的过程中出现问题, 请阅读日志文件 config.log
。它可能包含 configure 运行时没有输出到屏幕的具体问题。
求助时请附带日志文件中与问题 相关 的部分。
屏幕上的输出和一些文件的内容对于确认编译错误的原因都很有用。 屏幕输出来自于 configure 脚本和 make 命令。您不用附带所有输出内容,只要包含足够相关信息即可。例如,下面是从 make 的屏幕输出中截取的一段:
gcc -DALIASPATH=\"/mnt/lfs/usr/share/locale:.\"
-DLOCALEDIR=\"/mnt/lfs/usr/share/locale\"
-DLIBDIR=\"/mnt/lfs/usr/lib\"
-DINCLUDEDIR=\"/mnt/lfs/usr/include\" -DHAVE_CONFIG_H -I. -I.
-g -O2 -c getopt1.c
gcc -g -O2 -static -o make ar.o arscan.o commands.o dir.o
expand.o file.o function.o getopt.o implicit.o job.o main.o
misc.o read.o remake.o rule.o signame.o variable.o vpath.o
default.o remote-stub.o version.o opt1.o
-lutil job.o: In function `load_too_high':
/lfs/tmp/make-3.79.1/job.c:1565: undefined reference
to `getloadavg'
collect2: ld returned 1 exit status
make[2]: *** [make] Error 1
make[2]: Leaving directory `/lfs/tmp/make-3.79.1'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/lfs/tmp/make-3.79.1'
make: *** [all-recursive-am] Error 2
对于本例来说,许多人会只附带靠下的一行:
make [2]: *** [make] Error 1
这一行只告诉我们某些事情出问题了, 而完全没有说明哪里出了问题。 而上面的一段输出包含了出现问题的命令行和相关的错误消息, 这是我们需要的。
网上有一篇关于如何在网络上提问的精彩文章: http://ctab.org/~esr/faqs/smart-questions.html。 在您提问时,阅读并遵从这篇文章的建议,可以增加您得到帮助的可能性。
smart-questions.html
的中文翻译版见
https://lug.ustc.edu.cn/wiki/doc/smart-questions。
在本章中,我们会检查那些构建 LFS 系统必须的宿主工具,如果必要的话就安装它们。 之后我们会准备一个容纳 LFS 系统的分区。我们将亲自建立这个分区, 在分区上建立文件系统,并挂载该文件系统。
您的宿主系统必须拥有下列软件,且版本不能低于我们给出的最低版本。 对于大多数现代 Linux 发行版来说这不成问题。要注意的是, 很多发行版会把软件的头文件放在单独的软件包中,这些软件包的名称往往是 “<package-name>-devel” 或者 “<package-name>-dev”。 如果您的发行版为下列软件提供了这类软件包,一定要安装它们。
比下列最低版本更古老的版本可能正常工作,但作者没有进行测试。
Bash-3.2 (/bin/sh 必须是到 bash 的符号链接或硬连接)
Binutils-2.25 (比 2.32 更新的版本未经测试,不推荐使用)
Bison-2.7 (/usr/bin/yacc 必须是到 bison 的链接,或者是一个执行 bison 的小脚本)
Bzip2-1.0.4
Coreutils-6.9
Diffutils-2.8.1
Findutils-4.2.31
Gawk-4.0.1 (/usr/bin/awk 必须是到 gawk 的链接)
GCC-6.2 包括 C++ 编译器, g++ (比 9.2.0 更新的版本未经测试, 不推荐使用)
Glibc-2.11 (比 2.30 更新的版本未经测试,不推荐使用)
Grep-2.5.1a
Gzip-1.3.12
Linux Kernel-3.2
内核版本的要求是为了符合 第 6 章 中编译 glibc 时开发者推荐的配置选项。 udev 也要求一定的内核版本。
如果宿主内核比 3.2 更早,您需要将内核升级到较新的版本。 升级内核有两种方法,如果您的发行版供应商提供了 3.2 或更新的内核软件包,您可以直接安装它。 如果供应商没有提供一个足够新的内核包,或者您不想安装它, 您可以自己编译内核。编译内核和配置启动引导器 (假设宿主使用 GRUB) 的步骤在 第 8 章 中。
M4-1.4.10
Make-4.0
Patch-2.5.4
Perl-5.8.8
Python-3.4
Sed-4.1.5
Tar-1.22
Texinfo-4.7
Xz-5.0.0
上面要求的符号链接是根据本书构建 LFS 的充分条件,不是必要条件。 链接指向其他软件 (如 dash 或 mawk 等) 可能不会引发问题,但 LFS 开发团队没有尝试过这种做法,也无法提供帮助。对于一些软件包来说, 您可能需要修改本书中的指令或者使用额外的补丁, 才能在这类宿主环境成功构建。
为了确定您的宿主系统拥有每个软件的合适版本,且能够编译程序, 请运行下列脚本。
cat > version-check.sh << "EOF"
#!/bin/bash
# Simple script to list version numbers of critical development tools
export LC_ALL=C
bash --version | head -n1 | cut -d" " -f2-4
MYSH=$(readlink -f /bin/sh)
echo "/bin/sh -> $MYSH"
echo $MYSH | grep -q bash || echo "ERROR: /bin/sh does not point to bash"
unset MYSH
echo -n "Binutils: "; ld --version | head -n1 | cut -d" " -f3-
bison --version | head -n1
if [ -h /usr/bin/yacc ]; then
echo "/usr/bin/yacc -> `readlink -f /usr/bin/yacc`";
elif [ -x /usr/bin/yacc ]; then
echo yacc is `/usr/bin/yacc --version | head -n1`
else
echo "yacc not found"
fi
bzip2 --version 2>&1 < /dev/null | head -n1 | cut -d" " -f1,6-
echo -n "Coreutils: "; chown --version | head -n1 | cut -d")" -f2
diff --version | head -n1
find --version | head -n1
gawk --version | head -n1
if [ -h /usr/bin/awk ]; then
echo "/usr/bin/awk -> `readlink -f /usr/bin/awk`";
elif [ -x /usr/bin/awk ]; then
echo awk is `/usr/bin/awk --version | head -n1`
else
echo "awk not found"
fi
gcc --version | head -n1
g++ --version | head -n1
ldd --version | head -n1 | cut -d" " -f2- # glibc version
grep --version | head -n1
gzip --version | head -n1
cat /proc/version
m4 --version | head -n1
make --version | head -n1
patch --version | head -n1
echo Perl `perl -V:version`
python3 --version
sed --version | head -n1
tar --version | head -n1
makeinfo --version | head -n1 # texinfo version
xz --version | head -n1
echo 'int main(){}' > dummy.c && g++ -o dummy dummy.c
if [ -x dummy ]
then echo "g++ compilation OK";
else echo "g++ compilation failed"; fi
rm -f dummy.c dummy
EOF
bash version-check.sh
LFS 被设计为在一次会话中构建完成。换句话说,本书的指令假设, 在整个编译过程中,系统不会关闭或重启。当然, 构建过程不需要严格地一气呵成,只要注意在重新启动后,继续编译 LFS 时,根据构建进度的不同,可能需要再次进行某些操作。
这些章节是在宿主系统完成的。在重启后,注意下列事项:
在第 2.4 节 “创建新的分区”之后, 以 root 用户身份执行的步骤要求 LFS 环境变量已经 为 root 用户设置好。
/mnt/lfs 分区需要重新挂载。
第 5 章的所有步骤必须由用户 lfs 完成。在执行第 5 章的任务时, 必须先执行su - lfs命令。
第 5.3 节 “通用构建说明” 中的要求是关键的。如果在安装软件包时感觉不对劲, 确认之前解压的源码包已经被删除,然后重新解压源码包的文件, 重新执行相应一节的所有命令。
/mnt/lfs 分区需要重新挂载。
在进入 chroot 环境时,LFS 环境变量必须为 root 设置好。 之后就不需要 LFS 变量。
虚拟文件系统必须挂载好,在进入 chroot 环境之前,请执行 第 6.2.2 节 “挂载和填充 /dev” 和 第 6.2.3 节 “挂载虚拟内核文件系统” 中的命令。
像其他操作系统那样,LFS 一般也被安装在一个指定的分区。 我们推荐您为 LFS 选择一个可用的空分区, 或者在有充足未划分空间的情况下,创建一个新分区。
一个最小的系统需要大小约 6 吉字节 (GB) 的分区。 这足够保存所有源代码压缩包,并且编译所有软件包。 然而,如果希望用 LFS 作为日常的 Linux 系统,很可能需要安装额外软件, 需要更多空间。一个 20 GB 的分区是比较合理的, LFS 系统本身用不了太多空间,但大分区可以提供足够的临时存储空间, 以及在 LFS 构建完成后增添附加功能需要的空间。 另外,编译软件包可能需要大量磁盘空间, 但在软件包安装完成后可以回收这些空间。
计算机未必有足够满足编译过程要求的内存 (RAM) 空间, 因此可以使用一个小的磁盘分区作为 swap
空间。 内核使用此分区存储很少使用的数据,从而为活动进程留出更多内存。
LFS 的 swap
分区可以和宿主系统共用,这样就不用专门为
LFS 创建一个。
启动一个磁盘分区程序,例如 cfdisk 或者fdisk。在启动分区程序时需要一个命令行参数,
表示希望创建新分区的硬盘,例如主硬盘 /dev/sda
。 创建一个
Linux 本地分区,如果有必要的话再创建一个 swap
分区。 请参考 cfdisk(8)
或者 fdisk(8)
来学习如何使用分区程序。
有经验的用户可以尝试其他分区架构。 LFS 系统可以被构建在软件 RAID 阵列或 LVM 逻辑卷上。然而,一些分区架构需要 initramfs, 这是一个比较复杂的话题。对于初次构建 LFS 的用户来说, 不推荐采用这些分区方法。
牢记新分区的代号 (例如 sda5
)。 本书将这个分区称为
LFS 分区。还需要记住 swap
分区的代号。 之后在设置
/etc/fstab
文件时要用到这些代号。
经常有人在 LFS 邮件列表询问如何进行系统分区, 这是一个相当主观的问题。许多发行版在默认情况下会使用整个磁盘,
只留下一个小的 swap
分区。对于 LFS
来说,这往往不是最好的方案。它削弱了系统的灵活性, 使得我们难以在多个发行版或 LFS
系统之间共享数据,增加系统备份时间, 同时导致文件系统结构的不合理分配,浪费磁盘空间。
一个 LFS 根分区 (不要与 /root
目录混淆)
一般分配 10 GB 的空间就足以保证多数系统的运行。它提供了构建 LFS 以及 BLFS
的大部分软件包的充足空间,但又不太大,因此能够创建多个分区, 多次尝试构建 LFS 系统。
许多发行版自动创建交换空间。 一般来说,推荐采用两倍于物理内存的交换空间,然而这几乎没有必要。 如果磁盘空间有限,可以创建 2GB 的交换空间,并注意它的使用情况。
交换到磁盘从来就不是一件好事。一般来说,听硬盘的工作噪声, 同时观察系统的响应速度,就能说出系统是否在交换。一旦发生交换, 首先检查是否输入了不合理的命令,例如试图编辑一个 5GB 的文件。 如果交换时常发生,最好的办法是为你的系统添置内存。
如果 启动磁盘 采用 GUID 分区表 (GPT), 那么必须创建一个小的,一般占据 1MB 的分区,除非它已经存在。 这个分区不能格式化,在安装启动引导器时必须能够被 GRUB 发现。 这个分区在 fdisk 下显示为 'BIOS Boot' 分区, 在 gdisk 下显示分区类型代号为 EF02。
Grub Bios 分区必须位于 BIOS 引导系统使用的磁盘上。 这个磁盘未必是 LFS 根分区所在的磁盘。 不同磁盘可以使用不同分区表格式,只有引导盘采用 GPT 时才必须创建该分区。
使用 GPT 磁盘的读者也可以选择使用 UEFI 引导系统, 但需要划分 EFI 分区,以及额外的软件包和配置。参见 http://www.linuxfromscratch.org/hints/downloads/files/lfs-uefi.txt。
还有其他几个并非必须,但在设计磁盘布局时应当考虑的分区。 下面的列表并不完整,但可以作为一个参考。
/boot – 高度推荐。这个分区可以存储内核和其他引导信息。 为了减少大磁盘可能引起的问题, 建议将 /boot 分区设为第一块磁盘的第一个分区。为它分配 100 MB 就绰绰有余。
/home – 高度推荐。独立的 /home 分区可以在多个发行版或 LFS 系统之间共享 home 目录和用户设置。它的尺寸一般很大, 取决于硬盘的可用空间。
/usr – 一个独立的 /usr 分区一般被用于瘦客户端或无盘站的服务器,LFS 一般不需要。 为它划分 5GB 足够满足多数系统的要求。
/opt – 这个目录往往被用于在 BLFS 中安装 Gnome 或 KDE 等大型软件,以免把大量文件塞进 /usr 目录树。 如果将它划分为独立分区,5 到 10 GB 一般就足够了。
/tmp – 一个独立的 /tmp 分区是很少见的, 但在配置瘦客户端时很有用。如果分配了这个分区, 大小一般不会超过几个 GB 。
/usr/src – 将它划分为独立分区,可以用于存储 BLFS 源代码, 并在多个 LFS 系统之间共享它们。 它也可以用于编译 BLFS 软件包。30-50 GB 的分区可以提供足够的空间。
如果您希望在启动时自动挂载任何一个独立的分区,就要在 /etc/fstab
文件中说明。有关指定分区的细节将在 第 8.2 节 “创建
/etc/fstab 文件” 中讨论。
现在我们建立好了空白分区,可以在分区上建立文件系统。 LFS 可以使用 Linux 内核能够识别的任何文件系统, 最常见的是 ext3 和 ext4。 文件系统的选型是一个复杂的问题,要综合考虑分区的大小, 以及其中所存储文件的特征。例如:
适用于不经常更新的小分区,例如 /boot。
是 ext2 的升级版本,拥有日志系统, 能够在非正常关机的情况下恢复分区的正常状态。 它被广泛用于一般场合。
是 ext 文件系统家族的最新成员,它具有纳秒精度时间戳、 超大 (16 TB) 文件支持等新功能,速度也更快。
其他文件系统,包括 FAT32, NTFS, ReiserFS, JFS 和 XFS 在特定场合也很有用。关于这些文件系统的更多信息,可以在 http://en.wikipedia.org/wiki/Comparison_of_file_systems 找到。
LFS 假设根文件系统 (/) 采用 ext4 文件系统。 输入以下命令在 LFS 分区创建一个 ext4 文件系统:
mkfs -v -t ext4 /dev/<xxx>
如果您拥有一个现成的 swap
分区,
就不需要格式化它。如果新创建了一个 swap
分区,
需要执行以下命令以初始化它:
mkswap /dev/<yyy>
命令中 <yyy>
应该替换成
swap
分区的名称。
在本书中,我们经常使用环境变量 LFS
。您应该保证, 在构建 LFS
的全过程中,该变量都被定义且设置为您构建 LFS 使用的目录 —— 我们使用 /mnt/lfs
作为例子, 但您可以选择其他目录。如果您在一个独立的分区上构建 LFS
, 那么这个目录将成为该分区的挂载点。选择一个目录, 然后用以下命令设置环境变量:
export LFS=/mnt/lfs
设置该环境变量的好处是,我们可以直接输入书中的命令,例如 mkdir -v $LFS/tools 。 Shell 在解析命令时会自动将 “$LFS” 替换成 “/mnt/lfs”(或是您设置的其他值)。
如果您离开并重新进入了工作环境 (例如使用 su 切换到 root
或者其他用户), 请执行以下命令,检查 LFS
的设置是否正确:
echo $LFS
确认该命令的输出是您构建 LFS 的位置,如果您使用本书提供的例子, 那么输出应该是 /mnt/lfs
。 如果输出不正确,使用前文给出的命令,将 $LFS
设置成正确的目录名。
确保 LFS
始终正确的一种方法是:编辑您的主目录中的
.bash_profile
以及 root
用户的 /root/.bash_profile
,为它们加入上述设置并导出
LFS
变量的 export 命令。还要确认 /etc/passwd
中为每个需要使用 LFS
变量的用户指定的 shell 都是 bash,以保证每次登录时都执行
.bash_profile
中的命令。
另外还要考虑登录宿主系统的方式,如果您使用图形显示管理器登录, 再启动虚拟终端,那么 .bash_profile
一般不会被虚拟终端执行。此时,应该将
export
命令加入到您使用的用户和 root
的
.bashrc
文件中。另外,如果以非交互模式启动 bash,
有的发行版不会执行 .bashrc
中的指令。
此时一定要在使用环境变量前添加 export 命令。
我们已经在分区上建立了文件系统,为了访问分区, 我们需要把分区挂载到选定的挂载点上。正如前一节所述, 本书假设将文件系统挂载到
LFS
环境变量指定的目录中。
输入以下命令以创建挂载点,并挂载 LFS 文件系统:
mkdir -pv $LFS
mount -v -t ext4 /dev/<xxx>
$LFS
将 <xxx>
替换成 LFS
分区的代号。
如果为 LFS 创建了多个分区 (例如一个作为 /
,另一个作为
/usr
),那么它们都需要被挂载:
mkdir -pv $LFS mount -v -t ext4 /dev/<xxx>
$LFS mkdir -v $LFS/usr mount -v -t ext4 /dev/<yyy>
$LFS/usr
将 <xxx>
和
<yyy>
替换成对应的分区代号。
请确认在挂载新分区时没有使用过于严格的安全限制 (比如 nosuid
或者 nodev
等选项) 。 直接执行不带任何参数的
mount 命令, 检查挂载好的
LFS 分区被指定了哪些选项。如果 nodev
或者
nosuid
被设置了,就必须重新挂载分区。
上面的命令假设您在构建 LFS 的过程中不会重启计算机。 如果您关闭了系统,那么您要么在继续构建过程时重新挂载分区,
要么修改宿主系统的 /etc/fstab
文件,
使得系统在引导时自动挂载它们。例如:
/dev/<xxx>
/mnt/lfs ext4 defaults 1 1
如果您使用了多个分区,它们都需要添加到fstab
中。
如果您使用了 swap
分区,使用 swapon 命令启用它:
/sbin/swapon -v /dev/<zzz>
将 <zzz>
替换成
swap
分区的名称。
现在我们准备好了工作环境,可以下载软件包了。
本章包含了构建基本的 Linux 系统时需要下载的软件包列表。 我们给出的版本号对应于已经确定可以正常工作的版本, 本书是基于这些版本编写的。我们强烈反对使用更新的版本, 因为特定版本可用的构建命令未必适用于新版本。 最新版本的软件包可能有需要排查的问题, 我们会在本书的开发过程中进行排查,将解决方案找到并固定下来。
本书列出的下载位置可能失效。如果本书发布后, 某个下载位置发生变化,可以用 Google (http://www.google.com/) 提供的搜索引擎找到大多数软件包。如果搜索不到,尝试 http://www.linuxfromscratch.org/lfs/packages.html#packages 给出的备用地址。
下载好的软件包和补丁需要保存在一个适当的位置, 使得在整个构建过程中都能容易地访问它们。另外,还需要一个工作目录,
以便解压和编译软件包。我们可以将 $LFS/sources
既用于保存软件包和补丁,又作为工作目录。 这样,我们需要的所有东西都在 LFS 分区中, 因此在整个构建过程中都能够访问。
为了创建这个目录,在开始下载软件包之前, 以root
身份执行:
mkdir -v $LFS/sources
下面为该目录添加写入权限和 sticky 标志。“Sticky” 标志使得即使有多个用户对该目录有写入权限, 也只有文件所有者能够删除其中的文件。输入以下命令, 启用写入权限和 sticky 标志:
chmod -v a+wt $LFS/sources
一个简便地下载所有软件包和补丁的方式是使用本书附带的 wget-list 作为 wget 命令的输入。例如:
wget --input-file=wget-list --continue --directory-prefix=$LFS/sources
这些文件分布在国外的不同站点上,可能下载很慢。 如果您正在使用本书的稳定版或预发布版, 可以使用一个国内镜像站:
sed 's@.*/@ftp://mirrors.ustc.edu.cn
/lfs/lfs-packages/9.0/@' wget-list |
wget -i- --continue --directory-prefix=$LFS/sources
命令中的中国科学技术大学镜像站 (斜体) 可以换成其他镜像站, 推荐西安电子科技大学校内用户使用内网镜像站 https://linux.xidian.edu.cn/mirrors。
另外,自 LFS-7.0 以来,本书提供一个单独的文件 md5sums,用来检查所有软件包的正确性。 将该文件复制到$LFS/sources
, 运行以下命令即可得到检查结果:
pushd $LFS/sources md5sum -c md5sums popd
wget-list
和 md5sum
文件是 sysvinit 版本和 systemd 版本共用的,
下载到的少数几个“多余”软件包属于另一个版本。
下载或者用其他方法获取下列软件包:
下载地址: http://download.savannah.gnu.org/releases/acl/acl-2.2.53.tar.gz
MD5 校验和: 007aabf1dbb550bcddde52a244cd1070
主页: https://savannah.nongnu.org/projects/attr
下载地址: http://download.savannah.gnu.org/releases/attr/attr-2.4.48.tar.gz
MD5 校验和: bc1e5cb5c96d99b24886f1f527d3bb3d
主页: http://www.gnu.org/software/autoconf/
下载地址: http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.xz
MD5 校验和: 50f97f4159805e374639a73e2636f22e
主页: http://www.gnu.org/software/automake/
下载地址: http://ftp.gnu.org/gnu/automake/automake-1.16.1.tar.xz
MD5 校验和: 53f38e7591fa57c3d2cee682be668e5b
主页: http://www.gnu.org/software/bash/
下载地址: http://ftp.gnu.org/gnu/bash/bash-5.0.tar.gz
MD5 校验和: 2b44b47b905be16f45709648f671820b
主页: https://github.com/gavinhoward/bc
下载地址: https://github.com/gavinhoward/bc/archive/2.1.3/bc-2.1.3.tar.gz
MD5 校验和: 2a882dc39c0fb8e36c12f590d54cc039
主页: http://www.gnu.org/software/binutils/
下载地址: http://ftp.gnu.org/gnu/binutils/binutils-2.32.tar.xz
MD5 校验和: 0d174cdaf85721c5723bf52355be41e6
主页: http://www.gnu.org/software/bison/
下载地址: http://ftp.gnu.org/gnu/bison/bison-3.4.1.tar.xz
MD5 校验和: 201286a573b12da109df96282fe4ff4a
下载地址: https://www.sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz
MD5 校验和: 67e051268d0c475ea773822f7500d0e5
主页: https://libcheck.github.io/check
下载地址: https://github.com/libcheck/check/releases/download/0.12.0/check-0.12.0.tar.gz
MD5 校验和: 31b17c6075820a434119592941186f70
主页: http://www.gnu.org/software/coreutils/
下载地址: http://ftp.gnu.org/gnu/coreutils/coreutils-8.31.tar.xz
MD5 校验和: 0009a224d8e288e8ec406ef0161f9293
主页: http://www.gnu.org/software/dejagnu/
下载地址: http://ftp.gnu.org/gnu/dejagnu/dejagnu-1.6.2.tar.gz
MD5 校验和: e1b07516533f351b3aba3423fafeffd6
主页: http://www.gnu.org/software/diffutils/
下载地址: http://ftp.gnu.org/gnu/diffutils/diffutils-3.7.tar.xz
MD5 校验和: 4824adc0e95dbbf11dfbdfaad6a1e461
主页: http://e2fsprogs.sourceforge.net/
下载地址: https://downloads.sourceforge.net/project/e2fsprogs/e2fsprogs/v1.45.3/e2fsprogs-1.45.3.tar.gz
MD5 校验和: 447a225c05f0a81121f6ddffbf55b06c
主页: https://sourceware.org/ftp/elfutils/
下载地址: https://sourceware.org/ftp/elfutils/0.177/elfutils-0.177.tar.bz2
MD5 校验和: 0b583722f911e1632544718d502aab87
下载地址: https://dev.gentoo.org/~blueness/eudev/eudev-3.2.8.tar.gz
MD5 校验和: ce166b3fdd910c2a4a840378f48fedaf
主页: https://libexpat.github.io/
下载地址: https://prdownloads.sourceforge.net/expat/expat-2.2.7.tar.xz
MD5 校验和: 3659bc0938db78815b5f5a9c24d732aa
主页: https://core.tcl.tk/expect/
下载地址: https://prdownloads.sourceforge.net/expect/expect5.45.4.tar.gz
MD5 校验和: 00fce8de158422f5ccd2666512329bd2
主页: https://www.darwinsys.com/file/
下载地址: ftp://ftp.astron.com/pub/file/file-5.37.tar.gz
MD5 校验和: 80c29aca745466c6c24d11f059329075
File (5.37) 可能已经不能从列表中给出的位置下载。 该地址的网站管理员在发布新版本后有时会删除旧版本。访问 http://www.linuxfromscratch.org/lfs/download.html#ftp 中的备用下载地址,可能找到正确的版本。
主页: http://www.gnu.org/software/findutils/
下载地址: http://ftp.gnu.org/gnu/findutils/findutils-4.6.0.tar.gz
MD5 校验和: 9936aa8009438ce185bea2694a997fc1
主页: https://github.com/westes/flex
下载地址: https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz
MD5 校验和: 2882e3179748cc9f9c23ec593d6adc8d
主页: http://www.gnu.org/software/gawk/
下载地址: http://ftp.gnu.org/gnu/gawk/gawk-5.0.1.tar.xz
MD5 校验和: f9db3f6715207c6f13719713abc9c707
下载地址: http://ftp.gnu.org/gnu/gcc/gcc-9.2.0/gcc-9.2.0.tar.xz
MD5 校验和: 3818ad8600447f05349098232c2ddc78
主页: http://www.gnu.org/software/gdbm/
下载地址: http://ftp.gnu.org/gnu/gdbm/gdbm-1.18.1.tar.gz
MD5 校验和: 988dc82182121c7570e0cb8b4fcd5415
主页: http://www.gnu.org/software/gettext/
下载地址: http://ftp.gnu.org/gnu/gettext/gettext-0.20.1.tar.xz
MD5 校验和: 9ed9e26ab613b668e0026222a9c23639
主页: http://www.gnu.org/software/libc/
下载地址: http://ftp.gnu.org/gnu/glibc/glibc-2.30.tar.xz
MD5 校验和: 2b1dbdf27b28620752956c061d62f60c
主页: http://www.gnu.org/software/gmp/
下载地址: http://ftp.gnu.org/gnu/gmp/gmp-6.1.2.tar.xz
MD5 校验和: f58fa8001d60c4c77595fbbb62b63c1d
主页: http://www.gnu.org/software/gperf/
下载地址: http://ftp.gnu.org/gnu/gperf/gperf-3.1.tar.gz
MD5 校验和: 9e251c0a618ad0824b51117d5d9db87e
主页: http://www.gnu.org/software/grep/
下载地址: http://ftp.gnu.org/gnu/grep/grep-3.3.tar.xz
MD5 校验和: 05d0718a1b7cc706a4bdf8115363f1ed
主页: http://www.gnu.org/software/groff/
下载地址: http://ftp.gnu.org/gnu/groff/groff-1.22.4.tar.gz
MD5 校验和: 08fb04335e2f5e73f23ea4c3adbf0c5f
主页: http://www.gnu.org/software/grub/
下载地址: https://ftp.gnu.org/gnu/grub/grub-2.04.tar.xz
MD5 校验和: 5aaca6713b47ca2456d8324a58755ac7
主页: http://www.gnu.org/software/gzip/
下载地址: http://ftp.gnu.org/gnu/gzip/gzip-1.10.tar.xz
MD5 校验和: 691b1221694c3394f1c537df4eee39d3
主页: http://freecode.com/projects/iana-etc
下载地址: http://anduin.linuxfromscratch.org/LFS/iana-etc-2.30.tar.bz2
MD5 校验和: 3ba3afb1d1b261383d247f46cb135ee8
主页: http://www.gnu.org/software/inetutils/
下载地址: http://ftp.gnu.org/gnu/inetutils/inetutils-1.9.4.tar.xz
MD5 校验和: 87fef1fa3f603aef11c41dcc097af75e
主页: https://freedesktop.org/wiki/Software/intltool
下载地址: https://launchpad.net/intltool/trunk/0.51.0/+download/intltool-0.51.0.tar.gz
MD5 校验和: 12e517cac2b57a0121cda351570f1e63
主页: https://www.kernel.org/pub/linux/utils/net/iproute2/
下载地址: https://www.kernel.org/pub/linux/utils/net/iproute2/iproute2-5.2.0.tar.xz
MD5 校验和: 0cb2736e7bc2f56254a363d3d23703b7
主页: http://ftp.altlinux.org/pub/people/legion/kbd
下载地址: https://www.kernel.org/pub/linux/utils/kbd/kbd-2.2.0.tar.xz
MD5 校验和: d1d7ae0b5fb875dc082731e09cd0c8bc
下载地址: https://www.kernel.org/pub/linux/utils/kernel/kmod/kmod-26.tar.xz
MD5 校验和: 1129c243199bdd7db01b55a61aa19601
主页: http://www.greenwoodsoftware.com/less/
下载地址: http://www.greenwoodsoftware.com/less/less-551.tar.gz
MD5 校验和: 4ad4408b06d7a6626a055cb453f36819
下载地址: http://www.linuxfromscratch.org/lfs/downloads/9.0/lfs-bootscripts-20190524.tar.xz
MD5 校验和: c91b11e366649c9cec60c2552820fed5
主页: https://sites.google.com/site/fullycapable/
下载地址: https://www.kernel.org/pub/linux/libs/security/linux-privs/libcap2/libcap-2.27.tar.xz
MD5 校验和: 2e8f9fab32eb5ccb37969fe317fd17aa
主页: https://sourceware.org/libffi/
下载地址: ftp://sourceware.org/pub/libffi/libffi-3.2.1.tar.gz
MD5 校验和: 83b89587607e3eb65c70d361f13bab43
主页: http://libpipeline.nongnu.org/
下载地址: http://download.savannah.gnu.org/releases/libpipeline/libpipeline-1.5.1.tar.gz
MD5 校验和: 4c8fe6cd85422baafd6e060f896c61bc
主页: http://www.gnu.org/software/libtool/
下载地址: http://ftp.gnu.org/gnu/libtool/libtool-2.4.6.tar.xz
MD5 校验和: 1bfb9b923f2c1339b4d2ce1807064aa5
下载地址: https://www.kernel.org/pub/linux/kernel/v5.x/linux-5.2.8.tar.xz
MD5 校验和: 602dd0ecb8646e539fefb2beb6eb6fe0
Linux 内核的更新相对较为频繁, 多数情况下是为了解决新发现的安全问题。 除非勘误页明确说明,应该使用最新的 5.2.x 版本内核。
对于那些上网很慢或者流量很贵的用户来说, 可以分别下载内核的基线版本和补丁。这可以节省内核补丁版本 (patch level) 升级时的下载时间和网费。
西安电子科技大学校园网完全满足 “上网很慢” 和 “ 流量很贵 ” 的条件。 甚至对于内核的增量补丁,译者仍然强烈建议使用校内内核镜像 https://linux.xidian.edu.cn/mirrors/linux-kernel 下载。
主页: http://www.gnu.org/software/m4/
下载地址: http://ftp.gnu.org/gnu/m4/m4-1.4.18.tar.xz
MD5 校验和: 730bb15d96fffe47e148d1e09235af82
主页: http://www.gnu.org/software/make/
下载地址: http://ftp.gnu.org/gnu/make/make-4.2.1.tar.gz
MD5 校验和: 7d0dcb6c474b258aab4d54098f2cf5a7
主页: https://www.nongnu.org/man-db/
下载地址: http://download.savannah.gnu.org/releases/man-db/man-db-2.8.6.1.tar.xz
MD5 校验和: 22e82fe1127f4ca95de7100168a927d1
主页: https://www.kernel.org/doc/man-pages/
下载地址: https://www.kernel.org/pub/linux/docs/man-pages/man-pages-5.02.tar.xz
MD5 校验和: 136e5e3380963571a079693d8ae38f52
下载地址: https://github.com/mesonbuild/meson/releases/download/0.51.1/meson-0.51.1.tar.gz
MD5 校验和: 48787e391ec5c052799a3dd491f73909
主页: http://www.multiprecision.org/
下载地址: https://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz
MD5 校验和: 4125404e41e482ec68282a2e687f6c73
下载地址: http://www.mpfr.org/mpfr-4.0.2/mpfr-4.0.2.tar.xz
MD5 校验和: 320fbc4463d4c8cb1e566929d8adc4f8
下载地址: https://github.com/ninja-build/ninja/archive/v1.9.0/ninja-1.9.0.tar.gz
MD5 校验和: f340be768a76724b83e6daab69009902
主页: http://www.gnu.org/software/ncurses/
下载地址: http://ftp.gnu.org/gnu/ncurses/ncurses-6.1.tar.gz
MD5 校验和: 98c889aaf8d23910d2b92d65be2e737a
下载地址: https://www.openssl.org/source/openssl-1.1.1c.tar.gz
MD5 校验和: 15e21da6efe8aa0e0768ffd8cd37a5f6
主页: https://savannah.gnu.org/projects/patch/
下载地址: http://ftp.gnu.org/gnu/patch/patch-2.7.6.tar.xz
MD5 校验和: 78ad9937e4caadcba1526ef1853730d5
下载地址: https://www.cpan.org/src/5.0/perl-5.30.0.tar.xz
MD5 校验和: 037c35000550bdcb47552ad0f6d3064d
主页: https://www.freedesktop.org/wiki/Software/pkg-config
下载地址: https://pkg-config.freedesktop.org/releases/pkg-config-0.29.2.tar.gz
MD5 校验和: f6e931e319531b736fadc017f470e68a
主页: https://sourceforge.net/projects/procps-ng
下载地址: https://sourceforge.net/projects/procps-ng/files/Production/procps-ng-3.3.15.tar.xz
MD5 校验和: 2b0717a7cb474b3d6dfdeedfbad2eccc
主页: http://psmisc.sourceforge.net/
下载地址: https://sourceforge.net/projects/psmisc/files/psmisc/psmisc-23.2.tar.xz
MD5 校验和: 0524258861f00be1a02d27d39d8e5e62
下载地址: https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tar.xz
MD5 校验和: d33e4aae66097051c2eca45ee3604803
下载地址: https://docs.python.org/ftp/python/doc/3.7.4/python-3.7.4-docs-html.tar.bz2
MD5 校验和: c410337e954dbba2d04fe169c355a6a2
主页: https://tiswww.case.edu/php/chet/readline/rltop.html
下载地址: http://ftp.gnu.org/gnu/readline/readline-8.0.tar.gz
MD5 校验和: 7e6c1f16aee3244a69aba6e438295ca3
主页: http://www.gnu.org/software/sed/
下载地址: http://ftp.gnu.org/gnu/sed/sed-4.7.tar.xz
MD5 校验和: 777ddfd9d71dd06711fe91f0925e1573
下载地址: https://github.com/shadow-maint/shadow/releases/download/4.7/shadow-4.7.tar.xz
MD5 校验和: f7ce18c8dfd05f1a009266cb604d58b7
主页: http://www.infodrom.org/projects/sysklogd/
下载地址: http://www.infodrom.org/projects/sysklogd/download/sysklogd-1.5.1.tar.gz
MD5 校验和: c70599ab0d037fde724f7210c2c8d7f8
主页: https://savannah.nongnu.org/projects/sysvinit
下载地址: http://download.savannah.gnu.org/releases/sysvinit/sysvinit-2.95.tar.xz
MD5 校验和: 66f488c952c70ff4245774fc7e87e529
主页: http://www.gnu.org/software/tar/
下载地址: http://ftp.gnu.org/gnu/tar/tar-1.32.tar.xz
MD5 校验和: 83e38700a80a26e30b2df054e69956e5
主页: http://tcl.sourceforge.net/
下载地址: https://downloads.sourceforge.net/tcl/tcl8.6.9-src.tar.gz
MD5 校验和: aa0a121d95a0e7b73a036f26028538d4
主页: http://www.gnu.org/software/texinfo/
下载地址: http://ftp.gnu.org/gnu/texinfo/texinfo-6.6.tar.xz
MD5 校验和: 5231da3e6aa106cd0532b8609e5b3702
主页: https://www.iana.org/time-zones
下载地址: https://www.iana.org/time-zones/repository/releases/tzdata2019b.tar.gz
MD5 校验和: b26b5d7d844cb96c73ed2fb6d588daaf
下载地址: http://anduin.linuxfromscratch.org/LFS/udev-lfs-20171102.tar.xz
MD5 校验和: 27cd82f9a61422e186b9d6759ddf1634
主页: http://freecode.com/projects/util-linux
下载地址: https://www.kernel.org/pub/linux/utils/util-linux/v2.34/util-linux-2.34.tar.xz
MD5 校验和: a78cbeaed9c39094b96a48ba8f891d50
下载地址: https://github.com/vim/vim/archive/v8.1.1846/vim-8.1.1846.tar.gz
MD5 校验和: 4f129a05254d93c739fcede843df87df
主页: https://github.com/chorny/XML-Parser
下载地址: https://cpan.metacpan.org/authors/id/T/TO/TODDR/XML-Parser-2.44.tar.gz
MD5 校验和: af4813fe3952362451201ced6fbce379
下载地址: https://tukaani.org/xz/xz-5.2.4.tar.xz
MD5 校验和: 003e4d0b1b1899fc6e3000b24feddf7c
下载地址: https://zlib.net/zlib-1.2.11.tar.xz
MD5 校验和: 85adef240c5f370b308da8c938951a68
这些软件包的总计大小: 约 391 MB
除了软件包外,我们还需要一些补丁。 有些补丁解决了本应由维护者修复的问题,有些则对软件包进行微小的修改, 使得它们更容易使用。构建 LFS 系统需要下列补丁:
下载地址: http://www.linuxfromscratch.org/patches/lfs/9.0/bzip2-1.0.8-install_docs-1.patch
MD5 校验和: 6a5ac7e89b791aae556de0f745916f7f
下载地址: http://www.linuxfromscratch.org/patches/lfs/9.0/coreutils-8.31-i18n-1.patch
MD5 校验和: a9404fb575dfd5514f3c8f4120f9ca7d
下载地址: http://www.linuxfromscratch.org/patches/lfs/9.0/glibc-2.30-fhs-1.patch
MD5 校验和: 9a5997c3452909b1769918c759eff8a2
下载地址: http://www.linuxfromscratch.org/patches/lfs/9.0/kbd-2.2.0-backspace-1.patch
MD5 校验和: f75cca16a38da6caa7d52151f7136895
下载地址: http://www.linuxfromscratch.org/patches/lfs/9.0/sysvinit-2.95-consolidated-1.patch
MD5 校验和: 4900322141d493e74020c9cf437b2cdc
以上补丁的总大小: 约 186.8 KB
除了上述必要的补丁外,LFS 社区还创建了一些可选补丁。 它们有的解决了一些微小的问题,有的启用了一些默认没有启用的功能。 您可以浏览 http://www.linuxfromscratch.org/patches/downloads/ 查询 LFS 补丁库,并获取各种适合您需求的补丁。
在本章中,我们将为构建临时系统进行一些额外的准备工作。 我们将在 $LFS
中创建一个用于安装临时工具的目录,增加一个非特权用户以降低风险,
并为该用户建立合适的构建环境。 我们还将解释 LFS 软件包构建时间长度的测量单位,即 “SBU”
的概念,并给出关于软件包测试套件的一些信息。
第 5 章 中编译的所有程序都会被安装在
$LFS/tools
目录中,以便和 第 6 章 中编译的程序分离开来。因为前者是临时工具,
并不是最终的 LFS 系统的一部分,将它们保存在单独的目录树中, 就可以在使用完后容易地删除。
另外,这也可以防止它们被意外地安装在宿主环境中 (这种意外在 第 5 章 中容易发生)。
以 root
身份执行以下命令, 创建这个目录:
mkdir -v $LFS/tools
下一步是在宿主系统中创建一个 /tools
符号链接。
这个链接指向刚刚创建的,在 LFS 分区中的目录。以 root
身份执行以下命令:
ln -sv $LFS/tools /
这个命令是正确的,ln命令的语法有一些变种。 请先阅读
info coreutils
ln 和 ln(1)
,而不是直接发邮件断言这是一个错误。
这个符号链接使得临时工具链总是可以通过 /tools
这个路径访问, 这样,无论是在第 5 章(我们仍然需要使用宿主的一些工具)和第 6 章 (我们已经 “chroot” 到 LFS
分区)中,它们都能正常工作。
在作为 root
用户登录时,
一个微小的错误就可能损坏甚至摧毁整个系统。 因此,我们建议以非特权用户身份编译 第 5 章 中的软件包。
您可以使用自己的系统用户,但为了更容易地建立一个干净的工作环境, 最好创建一个名为 lfs
的新用户,以及它从属于的一个新组 (组名也是 lfs
) , 以便我们在编译过程中使用。为了创建新用户,以 root
身份执行以下命令:
groupadd lfs useradd -s /bin/bash -g lfs -m -k /dev/null lfs
命令行各选项的含义:
-s
/bin/bash
设置 bash 为用户
lfs
的默认 shell 。
-g
lfs
添加用户 lfs
到组 lfs
。
-m
为用户 lfs
创建一个主目录。
-k
/dev/null
将模板目录设置为空设备文件,从而不从默认模板目录 (/etc/skel
) 复制文件到新的主目录。
lfs
要创建的用户和组的名称。
为了以 lfs
身份登录系统 (尽管以
root
身份登录时可以不用输入密码,直接切换到用户
lfs
), 为 lfs
设置密码:
passwd lfs
将 lfs
设为 $LFS/tools
目录的所有者, 使 lfs
对这个目录拥有完全访问权:
chown -v lfs $LFS/tools
如果您按照本书的建议,建立了一个单独的工作目录, 那么将这个目录的所有者也设为 lfs
:
chown -v lfs $LFS/sources
下面以 lfs
的身份登录。
可以在显示管理器中的虚拟控制台登录,也可以使用下面的命令切换用户:
su - lfs
参数 “-
” 使得 su 启动一个登录 shell ,而不是非登录 shell
。 您可以阅读 bash(1)
和 info bash 详细了解它们的区别。
为了配置一个良好的工作环境,我们为 bash 创建两个新的启动脚本。以
lfs
的身份,执行以下命令, 创建一个新的
.bash_profile
:
cat > ~/.bash_profile << "EOF"
exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash
EOF
在以 lfs
用户登录时, 初始的 shell 一般是一个
登录 shell。它读取宿主系统的
/etc/profile
文件 (可能包含一些设置和环境变量),
然后读取 .bash_profile
。 我们在
.bash_profile
中使用 exec env -i.../bin/bash
命令,新建一个除了 HOME
, TERM
以及 PS1
外没有任何环境变量的 shell ,替换当前 shell , 防止宿主环境中不必要和有潜在风险的环境变量进入编译环境。
通过使用以上技巧,我们创建了一个干净环境。
新的 shell 实例是 非登录 shell
,它不会读取 /etc/profile
或者
.bash_profile
,而是读取 .bashrc
文件。现在我们就创建一个 .bashrc
文件:
cat > ~/.bashrc << "EOF"
set +h
umask 022
LFS=/mnt/lfs
LC_ALL=POSIX
LFS_TGT=$(uname -m)-lfs-linux-gnu
PATH=/tools/bin:/bin:/usr/bin
export LFS LC_ALL LFS_TGT PATH
EOF
set +h 命令关闭
bash
的散列功能。一般情况下,bash
使用一个散列表维护各个可执行文件的完整路径,这样就不用每次都在 PATH
指定的目录中搜索可执行文件。 然而,在构建 LFS 时,我们希望总是使用最新安装的工具。 因此,需要关闭散列功能,使得
shell 在运行程序时总是搜索 PATH
。这样,shell
总是能够找到 $LFS/tools
目录中那些最新编译的工具,而不是使用之前记忆的另一个目录中的程序。
将用户的文件创建掩码 (umask) 设定为 022 , 保证只有文件所有者可以写新创建的文件和目录,
但任何人都可读取、执行它们。 (如果 open(2)
系统调用使用默认模式, 则新文件将具有权限码 644 ,而新目录具有权限码 755)。
LFS
环境变量必须被设定为之前选择的挂载点。
LC_ALL
环境变量控制某些程序的本地化行为,
使得它们以特定国家的语言和惯例输出消息。将该变量设置为 “POSIX” 或者 “C” (这两种设置是等价的) 可以保证在
chroot 环境中所有命令的行为完全符合预期, 而与宿主的本地化设置无关。
LFS_TGT
变量设定了一个非默认, 但与宿主系统兼容的机器描述符。
该描述符被用于构建交叉编译器和交叉编译临时工具链。 第 5.2 节 “工具链技术说明”
包含了关于这个描述符的更多信息。
我们将 /tools/bin
附加在默认的
PATH
环境变量之前,这样在 第 5 章 中, 我们一旦安装了新的程序, shell
就能立刻使用它们。 这与关闭散列功能相结合, 降低了在第 5 章环境中新程序可用时错误地使用宿主系统中旧程序的风险。
最后,为了完全准备好编译临时工具的环境, 指示 shell 读取刚才创建的配置文件:
source ~/.bash_profile
许多人想在编译和安装各个软件包之前,了解这一过程大概需要多少时间。 由于 Linux From Scratch 可以在许多不同系统上构建, 我们无法直接给出估计时间。例如,最大的软件包 (Glibc) 在最快的系统上只要大约 20 分钟就能构建好, 而在一些较慢的系统上需要 3 天!因此,我们不提供实际时间, 而是以标准构建单位 (SBU) 衡量时间。
标准构建单位的定义如下:本书中构建的第一个软件包是 第 5 章 中的 Binutils , 定义编译它需要的时间为标准构建单位,缩写为 SBU 。 其他软件包的编译时间用 SBU 为单位表示。
例如,考虑一个编译时间是 4.5 SBU 的软件包。如果在某个系统上, 需要 10 分钟来编译和安装第一轮的 Binutils , 那么大概需要 45 分钟才能编译这个软件包。 幸运的是,多数软件包构建时间比 Binutils 少。
一般来说,SBU 不是完全准确的。这是由于它受到许多因素的影响, 包括宿主系统的 GCC 版本。 SBU 只能用来估计安装一个软件包可能需要的时间, 估计结果的误差在个别情况下可能达到几十分钟。
对于许多拥有多个处理器(或处理器核心)的现代系统, 可以进行并行构建,从而显著缩短软件包的编译时间。 设置环境变量,或者直接告诉 make 命令有多少个可用的处理器,都可以进行并行编译。例如, 在一个 Core2Duo 系统中,可以设置下列环境变量, 以支持用两个并行的进程进行编译:
export MAKEFLAGS='-j 2'
或者直接用以下命令构建:
make -j2
用这种方式使用多个处理器时,SBU 值将会发生变化, 有时甚至变得比正常值还大。某些情况下,还会导致 make 命令失败。 另外,分析构建过程的的输出也会变得困难, 因为不同进程的输出行会交错在一起。如果在构建过程中出现问题, 需要使用单处理器进行构建,才能更好地分析错误消息。
多数软件包提供测试套件, 一般来说,为新构建的软件包运行测试套件是个好主意, 这可以进行一次 “完整性检查” ,从而确认所有东西编译正确。 如果测试套件中的所有检验项目都能通过, 一般就可以证明这个软件包像开发者期望的那样运行。 然而,这并不保证软件包完全没有错误。
某些软件包的测试套件比其他的更为重要。 例如,组成核心工具链的几个软件包 — GCC、Binutils 和 Glibc 的测试套件就最为重要,因为这些软件包在系统的正常工作中发挥中心作用。 GCC 和 Glibc 的测试套件需要运行很长时间,特别是在较慢的硬件上, 但我们仍然强烈推荐运行它们。
经验表明,在 第 5 章 中运行测试套件几乎没有意义。在这一章中, 我们无法绝对避免宿主系统对测试过程的影响, 这往往导致不可避免的失败结果。再加上由于 第 5 章 中的工具是临时的, 最终会被丢弃,我们不建议一般读者在 第 5 章 中运行测试套件。 尽管我们为测试人员和开发人员考虑,提供了运行这些测试套件的方法, 但它们绝对不是必须运行的。
在运行 Binutils 和 GCC 的测试套件时,最普遍发生的问题是虚拟终端 (PTY)
被耗尽,这会导致大量测试出现失败结果。 这种现象有多种可能原因,但最常见的原因是宿主系统没有正确设置 devpts
文件系统。 关于这个问题的更多细节在 http://www.linuxfromscratch.org/lfs/faq.html#no-ptys
中进行了讨论。
一些软件包的测试套件总是失败,但开发者已经知道失败原因, 并判定这些失败并不重要。参照 http://www.linuxfromscratch.org/lfs/build-logs/9.0/ 中的构建日志,来检查这些失败是否符合预期。 本书中的所有测试都可以在该网址查询。
本章展示如何构建一个最小 Linux 系统,它只包含少量工具, 恰好能形成一个足以构建 LFS 的,比最小环境便利一些的工作环境。
我们分两步构建这个最小系统。第一步是构建一个新的和宿主无关的工具链 (包含编译器、汇编器、链接器、库和少量有用的工具)。 第二步是用这个工具链构建其他基本工具。
本章中编译的软件将被安装在 $LFS/tools
目录中,
这样可以把它们与下一章安装的软件以及宿主系统分离开来。 由于本章编译的软件包是临时性质的,我们不希望它们污染即将构建的 LFS
系统。
本节综合地解释构建方法中的逻辑和技术细节。 您现在并不需要立刻理解本节的所有内容,在实际进行构建的过程中, 可以更清晰地理解本节的信息。在整个构建过程中,您随时可以回来翻阅本节。
第 5 章 的总目标是构造一个临时环境,它包含一组可靠的, 能够与宿主系统完全分离的工具。这样,通过使用 chroot 命令,其余各章中执行的命令就被限制在这个临时环境中。 这确保我们能够干净、顺利地构建 LFS 系统。整个构建过程被精心设计, 以尽量降低新读者可能面临的风险,同时提供尽可能多的教育价值。
在继续阅读之前,请注意往往被称为目标三元组的工作平台名称。 获得它的一种简单方法是运行许多软件包附带的
config.guess
脚本。解压缩 Binutils 源码, 然后运行脚本:./config.guess
,观察输出。 例如,对于
32 位 Intel 处理器,输出应该是 i686-pc-linux-gnu,而对于 64 位系统输出应该是
x86_64-pc-linux-gnu。
另外注意平台的动态链接器的名称,它又被称为动态加载器 (不要和 Binutils 中的普通链接器 ld 混淆)。 动态链接器由 Glibc
提供,它寻找并加载程序所需的共享库, 为程序运行做好准备,然后运行程序。 在 32 位 Intel
机器上动态链接器的名称是 ld-linux.so.2
(在
64 位系统上是 ld-linux-x86-64.so.2
)。
一个确定动态链接器名称的准确方法是从宿主系统找一个二进制可执行文件, 然后执行: readelf -l <二进制文件名> | grep
interpreter
并观察输出。包含所有平台的权威参考可以在 Glibc
源码树根目录的 shlib-versions
文件中找到。
第 5 章 中构建方法的技术重点在于:
微调工作平台的名称,将目标三元组中的 “供应商” (vendor) 字段修改,得到 LFS_TGT
中的三元组。 这保证我们第一次构建 Binutils 和 GCC
时, 能够产生与宿主系统兼容的交叉链接器和交叉编译器。 它们不为其他架构生成二进制代码,
而是生成与当前硬件兼容的二进制代码。
交叉编译临时环境中的库。 由于交叉编译器从本质上不可能依赖于宿主环境中的任何东西, 我们可以降低宿主环境中头文件或库被嵌入新编译的工具的概率, 从而防止新编译的系统被污染。另外,交叉编译使得我们可能在 64 位硬件上同时编译出 32 位和 64 位的库。
小心地修改 GCC 源代码, 从而使得编译器使用我们指定的目标系统动态链接器。
我们首先安装 Binutils 。这是由于 GCC 和 Glibc 的 configure 脚本首先测试汇编器和链接器的一些特性, 以决定启用或禁用一些软件特性。初看起来这并不重要, 但没有正确配置的 GCC 或者 Glibc 可以导致工具链中潜伏的故障。 这些故障可能到整个构建过程快要结束时才突然爆发, 不过在花费大量无用功之前,测试套件的失败可以将这类错误凸显出来。
Binutils 将汇编器和链接器安装在两个位置,一个是 /tools/bin
, 另一个是 /tools/$LFS_TGT/bin
。
这两个位置中的工具互为硬链接。链接器的一个重要方面是它搜索库的顺序, 通过向 ld 命令加入 --verbose
参数,可以得到关于搜索路径的详细信息。例如,
ld --verbose | grep
SEARCH
会输出当前的搜索路径及其顺序。此外,通过编译一个样品 (dummy)
程序并向链接器传递 --verbose
参数,
可以知道哪些文件被链接。例如, gcc dummy.c
-Wl,--verbose 2>&1 | grep succeeded
将显示所有在链接过程中被成功打开的文件。
下一步安装 GCC。在执行它的 configure 脚本时, 您会看到类似下面这样的输出:
checking what assembler to use... /tools/i686-lfs-linux-gnu/bin/as
checking what linker to use... /tools/i686-lfs-linux-gnu/bin/ld
基于我们上面论述的原因,这些输出非常重要。 这说明 GCC 的配置脚本没有在 PATH 变量指定的目录中搜索工具。 然而,在
gcc 的实际运行中,
未必会使用同样的搜索路径。为了查询 gcc 会使用哪个链接器,需要执行以下命令:
gcc
-print-prog-name=ld
。
通过向 gcc 传递
-v
参数,可以知道在编译样品程序时发生的细节。例如, gcc -v
dummy.c
会输出预处理、编译和汇编阶段中的详细信息,包括 gcc 的包含文件搜索路径和顺序。
下一步安装“净化的” (sanitized) Linux API 头文件。 这允许 C 标准库 (Glibc) 与 Linux 内核提供的各种特性交互。
关于“净化的”一词的含义,见科普文章 http://www.jinbuguo.com/kernel/header_story.html。
下一步安装 Glibc 。在构建 Glibc 时最重要的考虑是编译器、 二进制工具和内核头文件。编译器一般不成问题,
Glibc 总是使用传递给配置脚本的 --host
参数相关的编译器。
例如,在我们的例子中,使用的编译器是 i686-lfs-linux-gnu-gcc。
但二进制工具和内核头文件的问题比较复杂,安全起见, 我们使用配置脚本提供的开关,保证正确的选择。 在 configure 脚本运行完成后,可以检查
glibc-build
目录中的 config.make
文件,了解全部重要的细节。 注意参数 CC=i686-lfs-linux-gnu-gcc
控制构建系统使用正确的二进制工具,而参数 -nostdinc
和 -isystem
控制编译器的头文件搜索路径。 这些事项凸显了
Glibc 软件包的一个重要性质 —— 它的构建机制是相当自给自足的,不依赖于工具链默认值。
在第二次构建 Binutils 时,我们在配置时使用开关 --with-lib-path
来控制 ld 的库文件搜索路径。
在第二次构建 GCC 时,需要修改其源码,使 GCC 使用新的动态链接器。 如果不这么做,GCC 程序中就会嵌入宿主
/lib
目录中的动态链接器名称,
从而破坏我们脱离宿主环境的目标。之后,核心工具链就是自包含、 不依赖宿主的。第 5 章 中的其他软件包都将在 /tools
中新的 Glibc 基础上构建。
在进入 第 6 章 中的 chroot
环境后,由于我们之前提到的自给性质,在全部重要软件包中首先安装 Glibc。 一旦 Glibc 被安装在
/usr
目录中,我们将会简便地修改工具链默认值,然后继续构建目标
LFS 系统的剩余部分。
在构建软件包时,本书提供的命令基于下列假设:
某些软件包在编译前需要打补丁,然而补丁只在绕过特定问题时才需要。 多数补丁在本章和下一章都要应用,然而个别时候只在其中一章使用。 因此,如果发现本书给出的指示中没有使用某个下载好的补丁, 这是正常的,不必担心。 在应用补丁时可能会出现关于 offset 或者 fuzz 的警告信息,不用担心这些警告, 补丁还是会成功应用到源码上的。
在编译大多数软件包时,屏幕上都会出现一些警告。这是正常的, 可以放心地忽略。这些警告就像它们描述的那样,表明一些过时的, 但并不是错误的 C 或 C++ 语法。C 标准经常改变, 一些软件包仍然在使用旧的标准。这并不是一个严重的问题, 但确实会触发警告。
最后确认 LFS
环境变量是否配置正确:
echo $LFS
确认上述命令输出 LFS 分区挂载点的路径,如果使用了本书的例子, 就是 /mnt/lfs
。
最后强调两个重要事项:
本书中的命令假设 第 2.2 节 “宿主系统需求” 中的所有内容, 包括符号链接,都被正确设置:
bash 是正在使用的 shell。
sh 是指向 bash的符号链接。
/usr/bin/awk 是指向 gawk 的符号链接。
/usr/bin/yacc 是指向 bison 的符号链接,或者一个执行 bison 的小脚本。
Binutils 包含汇编器、链接器以及其他用于处理目标文件的工具。
返回并重新阅读上一节。仔细理解那些标为 “重要” 的说明,以防止之后出现问题。
首先构建 Binutils 相当重要, 因为 Glibc 和 GCC 都会对可用的链接器和汇编器进行测试, 以决定可以启用它们自带的哪些特性。
Binutils 文档推荐在一个专用的目录中构建 Binutils:
mkdir -v build cd build
为了衡量本书其余部分使用的 SBU 值, 需要测量本软件包从配置开始直到第一次安装花费的时间。
为了容易地完成测量,可以将命令包装在 time 命令中,就像这样:
time {./configure ...
&& ... && make install; }
。
第 5 章中估计的 SBU 值和所需磁盘空间都不包含测试套件的数据。
现在,准备编译 Binutils :
../configure --prefix=/tools \ --with-sysroot=$LFS \ --with-lib-path=/tools/lib \ --target=$LFS_TGT \ --disable-nls \ --disable-werror
配置选项的含义:
--prefix=/tools
这告诉配置脚本准备将 Binutils 程序安装在 /tools
目录中。
--with-sysroot=$LFS
该选项告诉构建系统,交叉编译时在 $LFS 中寻找目标系统的库。
--with-lib-path=/tools/lib
该选项指定链接器应该使用的库文件搜索路径。
--target=$LFS_TGT
由于 LFS_TGT
变量中的机器描述和
config.guess
脚本的输出略有不同, 这个开关使得 configure 脚本调整
Binutils 的构建系统,以构建交叉链接器。
--disable-nls
该选项禁用临时工具不需要的国际化功能。
--disable-werror
该选项防止宿主系统编译器警告导致构建失败。
然后编译该软件包:
make
现在编译已经完成,一般来说我们应该执行测试套件, 但在当前的早期构建阶段,测试套件框架 (Tcl、Expect 和 DejaGNU) 尚不可用。另外,由于第一遍构建的程序很快会被第二遍构建的程序替代, 运行测试的收益极小。
如果在 x86_64 上编译,创建一个符号链接以保证工具链的完整性:
case $(uname -m) in x86_64) mkdir -v /tools/lib && ln -sv lib /tools/lib64 ;; esac
安装该软件包:
make install
这个软件包的细节在 第 6.16.2 节 “Binutils 的内容” 中可以找到。
GCC 软件包包含 GNU 编译器集合,其中有 C 和 C++ 编译器。
目前 GCC 依赖于 GMP、MPFR 和 MPC 这三个包。 由于宿主发行版未必包含它们,我们将它们和 GCC 一同构建。 将它们都解压到 GCC 源码目录中,并重命名解压出的目录, 这样 GCC 构建过程就能自动使用它们:
对于本章内容有一些很常见的误解。该软件包的构建过程就像之前 (软件包构建说明) 解释的那样, 首先解压 GCC 压缩包,切换到解压出的目录中,再执行下面的命令。
tar -xf ../mpfr-4.0.2.tar.xz mv -v mpfr-4.0.2 mpfr tar -xf ../gmp-6.1.2.tar.xz mv -v gmp-6.1.2 gmp tar -xf ../mpc-1.1.0.tar.gz mv -v mpc-1.1.0 mpc
下面的命令修改 GCC 的默认动态链接器位置,以使用 /tools
中的动态链接器。 同时,从 GCC 的头文件搜索路径中删除
/usr/include
。执行:
for file in gcc/config/{linux,i386/linux{,64}}.h do cp -uv $file{,.orig} sed -e 's@/lib\(64\)\?\(32\)\?/ld@/tools&@g' \ -e 's@/usr@/tools@g' $file.orig > $file echo ' #undef STANDARD_STARTFILE_PREFIX_1 #undef STANDARD_STARTFILE_PREFIX_2 #define STANDARD_STARTFILE_PREFIX_1 "/tools/lib/" #define STANDARD_STARTFILE_PREFIX_2 ""' >> $file touch $file.orig done
如果上面的命令看上去难以理解,把它分开来看。首先我们复制 gcc/config/linux.h
、 gcc/config/i386/linux.h
、 gcc/config/i386/linux64.h
, 将它们备份为名称是原文件名加上
“.orig”
后缀的文件。 然后第一个 sed 表达式将所有 “/lib/ld”、 “/lib64/ld” 或者
“/lib32/ld” 之前都加上 “/tools”,第二个将硬编码的
“/usr”
替换掉。然后,我们在文件末尾增加自己的宏定义, 修改默认的启动文件 (startfile) 前缀。注意,在
“/tools/lib/” 中,最后的 “/” 是必要的。 最后,使用
touch
命令更新被复制的文件的时间戳。 这与 cp
-u 命令结合起来, 防止我们不小心把命令输入两次,从而对文件造成非预期的修改。
对于 x86_64 平台,还要设置存放 64 位库的默认目录为 “lib”:
case $(uname -m) in x86_64) sed -e '/m64=/s/lib64/lib/' \ -i.orig gcc/config/i386/t-linux64 ;; esac
GCC 文档建议在一个专用目录中构建 GCC:
mkdir -v build cd build
准备编译 GCC:
../configure \ --target=$LFS_TGT \ --prefix=/tools \ --with-glibc-version=2.11 \ --with-sysroot=$LFS \ --with-newlib \ --without-headers \ --with-local-prefix=/tools \ --with-native-system-header-dir=/tools/include \ --disable-nls \ --disable-shared \ --disable-multilib \ --disable-decimal-float \ --disable-threads \ --disable-libatomic \ --disable-libgomp \ --disable-libquadmath \ --disable-libssp \ --disable-libvtv \ --disable-libstdcxx \ --enable-languages=c,c++
配置选项的含义:
--with-newlib
由于现在没有可用的 C 运行库,使用该选项保证构建 libgcc 时 inhibit_libc 常量被定义, 以防止编译任何需要 libc 支持的代码。
这个选项对于嵌入式系统工程师来说显得很奇怪, 因为 LFS 和嵌入式系统中常用的 C 运行库 newlib 毫无关系。 参见译者和 GCC 开发者的讨论: https://gcc.gnu.org/ml/gcc-help/2017-04/msg00029.html。
--without-headers
在创建完整的交叉编译器时, GCC 需要与目标系统兼容的标准头文件。 由于我们的特殊目的,这些头文件并不必要。 这个开关防止 GCC 查找它们。
--with-local-prefix=/tools
本地前缀是 GCC 在系统中寻找本地安装的包含文件的位置, 它的默认值是 /usr/local
。 将它设置为 /tools
, 从而将宿主系统的 /usr/local
排除在 GCC 搜索路径外。
--with-native-system-header-dir=/tools/include
默认情况下,GCC 在 /usr/include
中搜索系统头文件。由于我们使用了 sysroot 开关, 它会被改写成 $LFS/usr/include
。
然而,在之后的两节中,我们会把头文件安装在 $LFS/tools/include
, 本开关保证 GCC
能够正确地找到它们。 在第二遍编译 GCC 时,这个开关保证不会查找宿主系统的头文件。
--disable-shared
这个开关强制 GCC 静态链接它的内部库, 以防止宿主系统可能带来的问题。
--disable-decimal-float,
--disable-threads, --disable-libatomic,
--disable-libgomp, --disable-libquadmath,
--disable-libssp, --disable-libvtv,
--disable-libstdcxx
这些开关禁用对于十进制浮点数、线程、libatomic、libgomp、 libquadmath、libssp、libvtv 和 C++ 标准库的支持。 在构建交叉编译器时它们的编译会失败,而且在交叉编译临时 libc 时并不需要它们。
--disable-multilib
在 x86_64 平台上,LFS 目前不支持 multilib 配置。 这个开关对于 x86 来说可有可无。
--enable-languages=c,c++
这个选项保证只构建 C 和 C++ 编译器。目前只需要这两个语言。
执行以下命令编译 GCC:
make
现在我们编译完成了 GCC。此时,通常来说应该执行测试套件。 然而正如前文所述,测试套件框架还没有就位, 而且第一遍编译的 GCC 程序很快就会被替换,运行测试套件的收益极小。
安装该软件包:
make install
该软件包的更多细节在 第 6.21.2 节 “GCC 的内容” 中可以找到。
Linux API 头文件 (在 linux-5.2.8.tar.xz 中) 导出内核 API 供 Glibc 使用。
Linux 内核需要展示其应用程序编程接口 (API) 以供系统 C 运行库 (对于 LFS 来说就是 Glibc) 使用。 通过净化 Linux 内核源代码压缩包中的一些 C 头文件就可以做到这点。
确保软件包中没有陈旧的文件:
make mrproper
下面从源代码中提取用户可见的头文件。 它们被临时放置在一个本地目录中,然后复制到正确的位置, 因为提取过程会删除目标目录中的所有文件。
make INSTALL_HDR_PATH=dest headers_install cp -rv dest/include/* /tools/include
这个软件包的详细信息可以在 第 6.7.2 节 “Linux API 头文件的内容” 中找到。
Glibc 软件包包含主要的 C 语言库。它提供用于分配内存、检索目录、 打开和关闭文件、读写文件、字符串处理、模式匹配、 算术等用途的基本子程序。
Glibc 文档推荐在一个专用目录中构建 Glibc:
mkdir -v build cd build
然后准备编译 Glibc:
../configure \ --prefix=/tools \ --host=$LFS_TGT \ --build=$(../scripts/config.guess) \ --enable-kernel=3.2 \ --with-headers=/tools/include
配置选项的含义:
--host=$LFS_TGT,
--build=$(../scripts/config.guess)
在它们的共同作用下,Glibc 的构建系统将自身配置为使用 /tools
中的交叉链接器和交叉编译器,进行交叉编译。
--enable-kernel=3.2
该选项告诉 Glibc 编译出支持 3.2 版或者更新的 Linux 内核,这样就不会使用那些为更老内核准备的替代方案。
--with-headers=/tools/include
该选项告诉 Glibc 在刚刚安装到 tools 目录中的头文件基础上进行编译,这样它就知道内核拥有哪些特性, 并据此对自身进行优化。
在当前阶段,可能出现下列警告:
configure: WARNING: *** These auxiliary programs are missing or *** incompatible versions: msgfmt *** some features will be disabled. *** Check the INSTALL file for required versions.
msgfmt程序的缺失或不兼容一般是无害的。 msgfmt程序是 Gettext 软件包的一部分, 宿主发行版应该提供它。
有报告称该软件包在并行构建时可能失败,如果发生了这种情况, 加上 "-j1" 选项重新执行 make 命令。
编译该软件包:
make
安装该软件包:
make install
现在我们不可避免地要停下确认新工具链的各基本功能(编译和链接) 能如我们所预期的那样工作。执行以下命令进行完整性检查:
echo 'int main(){}' > dummy.c $LFS_TGT-gcc dummy.c readelf -l a.out | grep ': /tools'
如果所有东西正常工作,那么应该没有错误, 而且最后一行命令应该输出下列格式的内容:
[Requesting program interpreter: /tools/lib64/ld-linux-x86-64.so.2]
注意,对于 32 位机器,解释器的名字将会是 /tools/lib/ld-linux.so.2
。
如果输出不像上面描述的那样,或者根本没有输出,就说明出了问题。 检查并重新跟踪各个步骤,找到出问题的地方并修正它。 在继续构建之前,必须解决这个问题。
检验步骤顺利完成后,清理测试文件:
rm -v dummy.c a.out
在 第 5.9 节 “Binutils-2.32 - 第二遍” 中,构建 Binutils 能够对工具链是否正常构建进行额外检查。如果 Binutils 不能构建, 说明在之前安装 Binutils 、GCC 或者 Glibc 时出了问题。
该软件包的详细信息可以在 第 6.9.3 节 “Glibc 的内容” 中找到。
Libstdc++ 是 C++ 标准库,我们需要它才能编译 C++ 代码 (GCC 的一部分用 C++ 编写)。但在构建 第一遍的 GCC 时我们不得不暂缓安装它, 因为它依赖于当时还没有安装到 /tools 的 glibc。
Libstdc++ 是 GCC 源代码的一部分。
您应该先解压 GCC 源码包并切换到解压出来的 gcc-9.2.0
目录。
为 Libstdc++ 创建一个单独的构建目录,并进入它:
mkdir -v build cd build
准备编译 Libstdc++:
../libstdc++-v3/configure \ --host=$LFS_TGT \ --prefix=/tools \ --disable-multilib \ --disable-nls \ --disable-libstdcxx-threads \ --disable-libstdcxx-pch \ --with-gxx-include-dir=/tools/$LFS_TGT/include/c++/9.2.0
配置选项的含义:
--host=...
使用我们刚刚编译的交叉编译器,而不是 /usr/bin
中的宿主系统编译器。
--disable-libstdcxx-threads
由于我们目前还没有构建 C 线程库,C++ 线程库也不能构建。
--disable-libstdcxx-pch
这个开关防止安装预编译头文件,在这个阶段不需要它们。
--with-gxx-include-dir=/tools/$LFS_TGT/include/c++/9.2.0
这是 C++ 编译器搜索标准头文件的位置。在正常的构建过程中, 这项信息被顶层目录构建系统自动传递给 Libstdc++ configure 脚本。 然而我们这里没有使用顶层构建系统,因此必须明确给出这项信息。
运行以下命令编译 Libstdc++:
make
安装这个库:
make install
关于该软件包的详细信息可以在 第 6.21.2 节 “GCC 的内容” 中找到。
Binutils 包含汇编器、链接器以及其他用于处理目标文件的工具。
再一次地,创建一个单独的构建目录:
mkdir -v build cd build
准备安装 Binutils:
CC=$LFS_TGT-gcc \ AR=$LFS_TGT-ar \ RANLIB=$LFS_TGT-ranlib \ ../configure \ --prefix=/tools \ --disable-nls \ --disable-werror \ --with-lib-path=/tools/lib \ --with-sysroot
配置选项的含义:
CC=$LFS_TGT-gcc AR=$LFS_TGT-ar
RANLIB=$LFS_TGT-ranlib
这次编译 Binutils 是本地编译而不是交叉编译, 设置这些变量可以强制构建系统使用交叉编译器和相关工具, 而不是宿主系统的编译器和工具。
--with-lib-path=/tools/lib
这个选项使得配置脚本在编译 Binutils 时指定库文件搜索目录, 将 /tools/lib
传递给链接器。这保证链接器不会搜索宿主系统的库文件目录。
--with-sysroot
在链接时,某些共享对象可能要求其他共享对象,sysroot 特性允许链接器查找它们。如果不启用它,在某些宿主环境中, 一些软件包无法成功构建。
这个选项看上去很奇怪,而且解释不够清晰。 译者查资料得知, 从 Binutils 2.17
起,为保证和动态链接器的一致性, ld在搜索 DT_NEEDED
要求的共享对象时,先搜索 /etc/ld.so.conf
指定的目录,
再搜索配置时指定的默认目录。如果不启用 sysroot 功能, 且宿主系统使用了 /etc/ld.so.conf
,
那么宿主环境中的共享对象会污染工具链, 进而导致一系列严重问题。
如果您不想使用这个看上去奇怪的选项,可以执行 touch
/tools/etc/ld.so.conf
创建一个空的
ld.so.conf
文件,
覆盖宿主系统的设置。参阅
lfs-dev 列表中的讨论,以及
ld/2290。 另外 ld(1)
中关于这个搜索顺序的描述尚未更新。
编译该软件包:
make
安装该软件包:
make install
现在,为下一章的 “重新调整” 阶段准备链接器:
make -C ld clean make -C ld LIB_PATH=/usr/lib:/lib cp -v ld/ld-new /tools/bin
make 命令参数的含义:
-C ld
clean
该选项告诉 make 程序移除 ld
子目录中所有编译得到的文件。
-C ld
LIB_PATH=/usr/lib:/lib
该选项重新构建 ld
子目录中的一切。在命令行指定
LIB_PATH
Makefile
变量,即可覆盖临时工具的默认值, 并将其指向正确的最终路径。 该变量的值指定链接器的默认库文件搜索路径,
这个准备好的链接器将在下一章使用。
该软件包的更多信息可以在 第 6.16.2 节 “Binutils 的内容” 中找到。
GCC 软件包包含 GNU 编译器集合,其中有 C 和 C++ 编译器。
第一次构建的 GCC 安装了若干内部系统头文件,其中有一个 limits.h
。一般来说, 它应该包含对应的系统头文件,对于我们的特例而言,就是
/tools/include/limits.h
。
然而,在第一次构建 GCC 的时候,它还不存在, 因此 GCC 安装的内部头文件是一个不完整的、自给自足的文件,
不包含系统头文件提供的扩展特性。这对于构建临时的 libc 已经足够了, 但构建 GCC 需要完整的内部头文件。
使用以下命令创建一个完整版本的内部头文件, 该命令与 GCC 构建系统在一般情况下生成头文件的命令一模一样:
cat gcc/limitx.h gcc/glimits.h gcc/limity.h > \ `dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/include-fixed/limits.h
再一次地,改变 GCC 的默认动态链接器,使其使用 /tools
中的动态链接器:
for file in gcc/config/{linux,i386/linux{,64}}.h do cp -uv $file{,.orig} sed -e 's@/lib\(64\)\?\(32\)\?/ld@/tools&@g' \ -e 's@/usr@/tools@g' $file.orig > $file echo ' #undef STANDARD_STARTFILE_PREFIX_1 #undef STANDARD_STARTFILE_PREFIX_2 #define STANDARD_STARTFILE_PREFIX_1 "/tools/lib/" #define STANDARD_STARTFILE_PREFIX_2 ""' >> $file touch $file.orig done
如果是在 x86_64 上构建,修改 64 位库文件的默认目录名为 “lib”:
case $(uname -m) in x86_64) sed -e '/m64=/s/lib64/lib/' \ -i.orig gcc/config/i386/t-linux64 ;; esac
就像第一次构建 GCC 时一样,它需要 GMP、MPFR 和 MPC 三个包。 解压它们的源码包,并将它们移动到 GCC 要求的目录名:
tar -xf ../mpfr-4.0.2.tar.xz mv -v mpfr-4.0.2 mpfr tar -xf ../gmp-6.1.2.tar.xz mv -v gmp-6.1.2 gmp tar -xf ../mpc-1.1.0.tar.gz mv -v mpc-1.1.0 mpc
再次创建一个独立的构建目录:
mkdir -v build cd build
在开始构建 GCC 前,记得清除所有覆盖默认优化开关的环境变量。
现在准备编译 GCC:
CC=$LFS_TGT-gcc \ CXX=$LFS_TGT-g++ \ AR=$LFS_TGT-ar \ RANLIB=$LFS_TGT-ranlib \ ../configure \ --prefix=/tools \ --with-local-prefix=/tools \ --with-native-system-header-dir=/tools/include \ --enable-languages=c,c++ \ --disable-libstdcxx-pch \ --disable-multilib \ --disable-bootstrap \ --disable-libgomp
配置选项的含义:
--enable-languages=c,c++
该选项保证只构建 C 和 C++ 编译器。
--disable-libstdcxx-pch
不构建 libstdc++
的预编译头文件,它占据大量空间,而且我们用不到它。
--disable-bootstrap
对于 GCC 的本地构建,默认会进行自举 (bootstrap) 构建。 这种构建方式不仅编译 GCC ,还会将它编译多次。 它使用第一轮编译得到的程序,将自身再编译一次, 然后再用第二轮编译得到的程序将自身编译第三次。 第二次和第三次的结果被比较, 从而确认 GCC 可以没有缺陷地重新编译它自己, 这就表明编译过程准确无误。然而, LFS 的构建方法能够提供一个坚实的编译器,而不需要每次都进行自举。
编译该软件包:
make
安装该软件包:
make install
最后,还需要创建一个符号链接。许多程序和脚本运行 cc 而不是 gcc, 因为前者能够保证程序的通用性,使它可以在所有 UNIX 系统上使用, 无论是否安装了 GNU C 编译器。运行 cc 可以将安装哪种 C 编译器的选择权留给系统管理员。
ln -sv gcc /tools/bin/cc
在此时,很有必要暂停构建过程,确认新工具链的基本功能 (编译和链接)能够如同我们期望的那样工作。 执行以下命令,进行完整性检查:
echo 'int main(){}' > dummy.c cc dummy.c readelf -l a.out | grep ': /tools'
如果一切正常,这些命令应该不产生错误, 且最后一行命令的输出格式应该和下面相同:
[Requesting program interpreter: /tools/lib64/ld-linux-x86-64.so.2]
注意,在 32 位机器上,动态链接器为 /tools/lib/ld-linux-so.2
。
如果输出并不像上面展示的那样,或者根本没有输出, 则表明出现了问题。检查并重新跟踪各个步骤,找到问题的原因并纠正它。
这个问题在继续构建前必须解决。首先,使用 gcc 命令代替 cc,再次进行完整性检查。
如果这次编译器正常工作,则说明 cc
符号链接不存在,
按照之前的说明安装该符号链接。另外,还要确认 PATH
环境变量正确。运行 echo
$PATH 命令, 确认 /tools/bin
出现在列表的开头。如果 PATH
是错的, 表明你很可能没有以用户 lfs
的身份登录, 或者在 第 4.4 节 “配置环境” 的过程中出现了问题。
在一切检查顺利后,即可删除测试文件:
rm -v dummy.c a.out
关于本软件包的更多信息可以在 第 6.21.2 节 “GCC 的内容” 中找到。
Tcl 软件包包含工具命令语言。
为了支持 GCC 和 Binutils 等软件包测试套件的运行, 需要安装这个软件包和接下来的两个 (Expect 与 DejaGNU)。 为了测试目的安装三个软件包看似浪费, 但实际上,我们运行了测试,才能放心地确定多数重要工具可以正常工作, 即使测试不是必要的。另外,即使在本章中不运行测试套件 (本章中的测试并不必要),我们必须安装这三个软件包, 才能执行 第 6 章 中的测试套件。
注意,这里使用的 Tcl 软件包是一个最小版本,仅用于运行 LFS 的测试。 如果需要完整的软件包,阅读 BLFS Tcl 安装过程 。
准备编译 Tcl:
cd unix ./configure --prefix=/tools
构建该软件包:
make
现在编译已经完成。正如之前讨论的,对于本章中的临时工具而言, 没有必要运行测试套件。如果无论如何要运行测试套件, 执行以下命令:
TZ=UTC make test
在某些特定的宿主环境下,Tcl 测试套件可能失败, 失败原因还没有完全理解清楚。因此,测试套件的失败并不令人吃惊,
也不会造成严重问题。TZ=UTC
参数在测试套件的运行过程中将时区设为协调世界时 (UTC), 这保证时钟测试能够正常进行。TZ
环境变量的详细知识在 第 7
章 中讲解。
安装该软件包:
make install
将安装好的库加上写入权限,以便将来移除调试符号:
chmod -v u+w /tools/lib/libtcl8.6.so
安装 Tcl 的头文件,因为下一个软件包 Expect 需要它们才能构建。
make install-private-headers
创建一个必要的符号链接:
ln -sv tclsh8.6 /tools/bin/tclsh
Expect 软件包包含一个根据脚本,与其他交互程序进行对话的程序。
首先强制 Expect 的配置脚本使用 /bin/stty
,
而不是宿主系统上可能存在的 /usr/local/bin/stty
,
以保证测试套件工具在最终构建工具链时仍然完整:
cp -v configure{,.orig} sed 's:/usr/local/bin:/bin:' configure.orig > configure
现在准备编译 Expect:
./configure --prefix=/tools \ --with-tcl=/tools/lib \ --with-tclinclude=/tools/include
配置选项的含义:
--with-tcl=/tools/lib
这保证配置脚本找到临时工具中的 Tcl 安装, 而不是宿主系统中可能存在的 Tcl 安装。
--with-tclinclude=/tools/include
该选项明确告诉 Expect 去哪里寻找 Tcl 的内部头文件。 使用该选项可以避免某些情况下 configure 由于找不到 Tcl 头文件的位置而失败。
构建该软件包:
make
现在编译已经完成,正如之前所述, 并不需要运行本章中临时工具的测试套件。如果一定要运行 Expect 的测试套件,输入以下命令:
make test
注意,已经知道在特定宿主环境下 Expect 测试套件会失败。 我们无法控制宿主环境,因此这里的测试失败并不令人吃惊, 也不会造成严重问题。
安装该软件包:
make SCRIPTS="" install
make 命令参数的含义:
SCRIPTS=""
该选项使得一些不需要的 Expect 辅助脚本不被安装。
DejaGNU 软件包包含用于测试其他程序的框架。
准备编译 DejaGNU:
./configure --prefix=/tools
构建并安装该软件包:
make install
如果要测试该软件包,执行:
make check
M4 软件包包含一个宏处理器。
首先,进行 glibc-2.28 要求的一些修补:
sed -i 's/IO_ftrylockfile/IO_EOF_SEEN/' lib/*.c echo "#define _IO_IN_BACKUP 0x100" >> lib/stdio-impl.h
准备编译 M4:
./configure --prefix=/tools
编译该软件包:
make
现在,编译已经完成。正如前文所述, 在本章中运行临时工具的测试套件是不必要的,如果一定要运行 M4 测试套件,输入以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.14.2 节 “M4 的内容” 找到。
Ncurses 软件包包含终端无关的字符屏幕处理库。
首先,保证在配置时首先找到 gawk 命令:
sed -i s/mawk// configure
准备编译 Ncurses:
./configure --prefix=/tools \ --with-shared \ --without-debug \ --without-ada \ --enable-widec \ --enable-overwrite
配置选项的含义:
--without-ada
这保证不构建 Ncurses 的 Ada 编译器支持,宿主环境可能有 Ada 编译器,但进入 chroot 环境后 Ada 编译器就不再可用。
--enable-overwrite
这告诉 Ncurses 将头文件安装在 /tools/include
, 而不是 /tools/include/ncurses
,从而保证其他软件包可以成功找到 Ncurses 头文件。
--enable-widec
该选项使得宽字符库(例如 libncursesw.so.6.1
) 被构建,而不构建常规字符库(例如
libncurses.so.6.1
)。
宽字符库在多字节和传统 8 位 locale 中都能工作, 而常规字符库只能在 8 位 locale 中工作。
宽字符库和普通字符库在源码层面是兼容的,但二进制不兼容。
编译该软件包:
make
该软件包有测试套件,但必须在安装软件包后运行。测试文件位于 test/
目录中,阅读其中的 README
文件了解更多信息。
安装该软件包:
make install ln -s libncursesw.so /tools/lib/libncurses.so
该软件包的详细信息可以在 第 6.24.2 节 “Ncurses 的内容” 中找到。
Bash 软件包包含 Bourne-Again SHell。
准备编译 Bash:
./configure --prefix=/tools --without-bash-malloc
配置选项的含义:
--without-bash-malloc
该选项禁用 Bash 自己的内存分配 (malloc
)函数,因为已知它会导致段错误。 这样,Bash 就会使用
Glibc 的更加稳定的 malloc
函数。
编译该软件包:
make
现在编译已经完成。正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 Bash 的测试套件,执行以下命令:
make tests
安装该软件包:
make install
为那些使用 sh 命令运行 shell 的程序考虑, 创建一个链接:
ln -sv bash /tools/bin/sh
该软件包的详细信息可以在 第 6.34.2 节 “Bash 的内容” 中找到。
Bison 软件包包含语法分析器生成器。
准备编译 Bison:
./configure --prefix=/tools
编译该软件包:
make
如果希望测试编译结果,运行:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.31.2 节 “Bison 的内容” 中找到。
Bzip2 软件包包含用于压缩和解压缩文件的程序。使用 bzip2 压缩文本文件可以获得比传统的 gzip 高到不知哪里去的压缩率。
Bzip2 软件包没有 configure 脚本。 执行以下命令编译并测试它:
make
安装该软件包:
make PREFIX=/tools install
该软件包的详细信息可以在 第 6.22.2 节 “Bzip2 的内容” 中找到。
Coreutils 软件包包含用于显示和设定系统基本属性的工具。
准备编译 Coreutils:
./configure --prefix=/tools --enable-install-program=hostname
配置选项的含义:
--enable-install-program=hostname
该选项表示构建 hostname 程序并安装它 —— 默认情况下它被禁用,但 Perl 测试套件需要它。
编译该软件包:
make
现在编译已经完成,正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 Coreutils 测试套件,执行以下命令:
make RUN_EXPENSIVE_TESTS=yes check
参数 RUN_EXPENSIVE_TESTS=yes
告诉测试套件运行一些附加测试,在某些平台上它们被认为比较耗费 CPU 和内存资源,但在 Linux 上一般不成问题。
安装该软件包:
make install
该软件包的详细信息可以在 第 6.54.2 节 “Coreutils 的内容” 中找到。
Diffutils 软件包包含显示文件或目录之间差异的程序。
准备编译 Diffutils:
./configure --prefix=/tools
编译该软件包:
make
现在编译已经完成,正如前文所述, 在本章中没有必要运行临时工具的测试套件。如果一定要运行 Diffutils 的测试套件,执行以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.56.2 节 “Diffutils 的内容” 中找到。
File 软件包包含用于确定给定文件类型的工具。
准备编译 File:
./configure --prefix=/tools
编译该软件包:
make
现在编译已经完成,正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 File 的测试套件,执行以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.12.2 节 “File 的内容” 中找到。
Findutils 软件包包含用于查找文件的程序。 这些程序能够递归地搜索目录树,以及创建、维护和搜索数据库 (一般比递归搜索快,但在数据库最近没有更新时不可靠)。
首先,进行 glibc-2.28 要求的一些修补:
sed -i 's/IO_ftrylockfile/IO_EOF_SEEN/' gl/lib/*.c sed -i '/unistd/a #include <sys/sysmacros.h>' gl/lib/mountlist.c echo "#define _IO_IN_BACKUP 0x100" >> gl/lib/stdio-impl.h
准备编译 Findutils:
./configure --prefix=/tools
编译该软件包:
make
现在编译已经完成,正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 Findutils 的测试套件,执行以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.58.2 节 “Findutils 的内容” 中找到。
Gawk 软件包包含操作文本文件的程序。
准备编译 Gawk:
./configure --prefix=/tools
编译该软件包:
make
现在编译已经完成,正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 Gawk 的测试套件,执行以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.57.2 节 “Gawk 的内容” 中找到。
Gettext 软件包包含国际化和本地化工具, 它们允许程序在编译时加入 NLS (母语支持)功能, 使它们能够以用户的母语输出消息。
对于我们的临时工具,只要安装 Gettext 中的三个程序即可。
准备编译 Gettext:
./configure --disable-shared
配置选项的含义:
--disable-shared
现在我们不需要安装 Gettext 的任何共享库,因此不用构建它们。
编译该软件包:
make
由于环境的限制,不建议在这里运行该软件包的测试套件。
安装 msgfmt, msgmerge and xgettext 这三个程序:
cp -v gettext-tools/src/{msgfmt,msgmerge,xgettext} /tools/bin
该软件包的详细信息可以在 第 6.47.2 节 “Gettext 的内容” 中找到。
Grep 软件包包含在文件中进行搜索的程序。
准备编译 Grep:
./configure --prefix=/tools
编译该软件包:
make
现在编译已经完成,正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 Grep 的测试套件,执行以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.33.2 节 “Grep 的内容” 中找到。
Gzip 软件包包含压缩和解压缩文件的程序。
准备编译 Gzip:
./configure --prefix=/tools
编译该软件包:
make
现在编译已经完成,正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 Gzip 的测试套件,执行以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.62.2 节 “Gzip 的内容” 中找到。
Make 软件包包含用于编译软件包的程序。
首先解决一个由 Glibc-2.27 及更新版本导致的错误:
sed -i '211,217 d; 219,229 d; 232 d' glob/glob.c
准备编译 Make:
./configure --prefix=/tools --without-guile
配置选项的含义:
--without-guile
这保证 Make-4.2.1 不会链接到 Guile 库。 宿主系统可能拥有 Guile 库,但它在 chroot 环境中不可用。
编译该软件包:
make
现在编译已经完成,正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 Make 的测试套件,执行以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.66.2 节 “Make 的内容” 中找到。
Patch 软件包包含根据通常由 diff 程序创建的 “补丁” 文件,修改或创建文件的程序。
准备编译 Patch:
./configure --prefix=/tools
编译该软件包:
make
现在编译已经完成,正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 Patch 的测试套件,执行以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.67.2 节 “Patch 的内容” 中找到。
Perl 软件包包含实用报表提取语言。
准备编译 Perl:
sh Configure -des -Dprefix=/tools -Dlibs=-lm -Uloclibpth -Ulocincpth
配置选项的含义:
-des
这是三个选项的组合:-d 对于所有配置项目使用默认值; -e 确保所有配置任务完成;-s 使得配置脚本不输出不必要的信息。
-Uloclibpth
和 -Ulocincpth
取消一些变量的定义, 它们可能导致 Perl 配置搜索宿主系统 /usr/local 目录中的组件。
构建该软件包:
make
尽管 Perl 拥有测试套件,但最好到下一章安装完成 Perl 后再运行它。
现在,只有少数工具和库需要安装:
cp -v perl cpan/podlators/scripts/pod2man /tools/bin mkdir -pv /tools/lib/perl5/5.30.0 cp -Rv lib/* /tools/lib/perl5/5.30.0
该软件包的详细信息可以在 第 6.40.2 节 “Perl 的内容” 中找到。
Python 3 软件包包含 Python 开发环境。它被用于面向对象编程、 编写脚本、为大型程序建立原型或开发完整的应用。
该软件包包含两个以 “python” 开头的压缩包。我们应该解压的包是 Python-3.7.4.tar.xz
(注意首字母是大写的)。
该软件包首先构建 Python 解释器,然后构建一些标准 Python 模块。 构建模块使用的主要脚本是用 Python
语言编写的,其中包含指向宿主系统的, 硬编码的路径 /usr/include
和 /usr/lib
。为了避免使用它们, 执行命令:
sed -i '/def add_multiarch_paths/a \ return' setup.py
准备编译 Python:
./configure --prefix=/tools --without-ensurepip
配置选项的含义:
--without-ensurepip
该选项禁止构建 Python 软件包安装器,它在当前阶段没有必要。
编译该软件包:
make
现在编译已经完成,但测试套件需要 Tk 和 X 窗口系统,现在不能运行。
安装该软件包:
make install
关于该软件包的详细信息可以在 第 6.51.2 节 “Python 3 的内容” 中找到。
Sed 软件包包含一个流编辑器。
准备编译 Sed:
./configure --prefix=/tools
编译该软件包:
make
现在编译已经完成,正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 Sed 的测试套件,执行以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.28.2 节 “Sed 的内容” 中找到。
Tar 软件包包含一个归档程序。
准备编译 Tar:
./configure --prefix=/tools
编译该软件包:
make
现在编译已经完成,正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 Tar 的测试套件,执行以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.69.2 节 “Tar 的内容” 中找到。
Texinfo 软件包包含阅读、编写和转换 info 页面的程序。
准备编译 Texinfo:
./configure --prefix=/tools
在配置过程中,一项测试报告与 TextXS_la-TestXS.lo 相关的错误。 这和 LFS 没有关系,应该忽略该错误。
编译该软件包:
make
现在编译已经完成,正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 Texinfo 的测试套件,执行以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.70.2 节 “Texinfo 的内容” 中找到。
Xz 软件包包含文件压缩和解压缩工具,它能够处理 lzma 和新的 xz 压缩文件格式。使用 xz 压缩文本文件, 可以得到比传统的 gzip 或 bzip2 更好的压缩比。
准备编译 Xz:
./configure --prefix=/tools
编译该软件包:
make
现在编译已经完成,正如前文所述, 没有必要在本章中运行临时工具的测试套件。如果一定要运行 Xz 的测试套件,执行以下命令:
make check
安装该软件包:
make install
该软件包的详细信息可以在 第 6.45.2 节 “Xz 的内容” 中找到。
本节中的步骤是可选的,但如果 LFS 分区比较小, 您应该了解一下,有些无用的内容可以删除。 到现在为止,已经构建的可执行文件和库包含大约 70MB 的无用调试符号。执行以下命令,移除这些符号:
strip --strip-debug /tools/lib/* /usr/bin/strip --strip-unneeded /tools/{,s}bin/*
以上命令会跳过一些文件,并报告说无法识别它们的格式。 这些文件大多数都是脚本文件,而不是二进制文件。 这里使用了宿主系统的 strip 命令,因为它可以清理 /tools 中的 strip 二进制程序。
注意不要对库文件使用 --strip-unneeded
选项。这会损坏静态库,
结果整个工具链都要重新构建。
为了节约更多空间,删除文档:
rm -rf /tools/{,share}/{info,man,doc}
删除无用的文件:
find /tools/{lib,libexec} -name \*.la -delete
这里删除的是 libtool 档案文件。它们是为了相当过时的原因创建的, 在现代 Linux 系统上几乎没有用。参阅 BLFS 中的说明。
现在,您应该保证 $LFS
有至少 3 GB 的可用空间,
以在下一阶段构建和安装 Glibc 和 GCC 。如果空间足够构建和安装 Glibc, 那么构建和安装剩余的软件包就不成问题。
本书中剩余部分的命令都必须用 root
用户身份执行,
而不是 lfs
用户。 另外,记得再次检查
$LFS
在 root
的环境中被正确设定。
目前,$LFS/tools
目录的所有者是
lfs
,
这是一个仅在宿主系统上存在的用户。如果将它这样保留下去, 其中的文件将属于一个没有用户名的用户 ID。这是很危险的,
因为未来创建的一个用户名可能得到相同的用户 ID, 并获得 $LFS/tools
目录及其中文件的所有权,就有可能恶意操作它们。
为了避免这个问题,您可以在后面创建 /etc/passwd
时将
lfs
用户添加到新的 LFS
系统中,注意为它分配和宿主系统一样的用户 ID 和组 ID。更好的方式是,现在就把 $LFS/tools
目录的所有者改变为 root
。执行命令:
chown -R root:root $LFS/tools
尽管 $LFS/tools
目录可以在 LFS
系统构建完成后删除, 但也可以保留它用于构建更多的相同版本的 LFS 系统。用什么方法备份 $LFS/tools
取决于个人。
如果您有意保留临时工具,用来构建新的 LFS 系统, 现在就要保存好它们。 第 6 章中后续执行的命令将就地调整这些工具, 导致它们不能用于构建新系统。
在本章中,我们将进入构建环境,并真正开始构建 LFS 系统。 换句话说,我们 chroot 到前一章构建的临时迷你 Linux 系统中, 进行一些最后的准备,然后开始安装各软件包。
软件的安装过程是简单直接的。 尽管很多时候可以把安装说明写得更短、更通用, 我们还是选择为每个包提供完整的安装流程,以尽量减小出错的可能。 学习 Linux 系统工作原理的关键就是要知道每个包的作用, 以及您(或者系统)为什么需要它。
我们不推荐在编译中使用优化, 编译优化可以使程序跑得稍微快一点, 但也可能在编译或运行的过程中带来问题。
如果一个软件包在打开优化时无法编译,试着关闭优化再编译它。 即使一个软件包在打开优化时可以编译,
由于源代码和编译工具的复杂相互作用,仍然存在编译不正确的风险。 另外请注意,除本书明确说明外, 设定 -march
和 -mtune
是未经验证的, 它们可能在工具链软件包(Binutils、GCC 和 Glibc)中引发问题。
使用编译优化带来的微小性能增益往往不值得冒编译错误的风险, 因此我们建议第一次构建 LFS 的读者不要使用自定义的优化选项。
即使不用优化,得到的系统仍然会运行得很快,而且会很稳定。
软件包的配置系统会使用 -O2
或 -O3
等作为默认的基本优化参数, 它们是经软件包作者检验可用的。
因此,不使用自定义优化参数也不会编译出非常慢的系统。
必须严格按照本章给出的顺序安装软件包, 才能保证没有程序意外地获得指向 /tools
的路径, 甚至将这样的路径硬编码到程序中。因此,
不要同时构建多个软件包,这可能节约时间(特别是在双 CPU 机器上), 但往往会导致某个程序包含一个硬编码的,指向
/tools
的路径, 结果删除该目录后程序不能工作。
在安装指令之前,每个页面都提供了软件包的基本信息, 包括其内容的简要描述,以及构建过程大概需要的时间和磁盘空间。 在安装指令之后,有一个包含该软件包提供的所有程序和库的清单 (以及对它们的简要描述)。
对于拥有可用的测试套件的软件包,第 6 章中给出的 SBU 值和需要的磁盘空间包含了运行测试套件需要的时间和磁盘空间。
一般来说,LFS 作者不推荐构建和安装静态库,它们是为了某些在现代 Linux 系统中早已过时的原因而存在的。 另外,将静态库链接到程序中是有害的, 如果需要更新这个库以解决安全问题, 所有使用该静态库的程序都要重新链接。程序对静态库的使用并不是显然的, 甚至可能搞不清有哪些相关程序需要链接(或不知道如何重新链接)。
在第 6 章的安装过程中,我们删除或者禁止安装多数静态库。 一般来说,向 configure 传递 --disable-static
就可以禁用静态库,
但某些情况下需要其他手段。在极个别情况下, 特别是对于 Glibc 和 GCC,
静态库对于一般的软件包构建过程仍然很关键,就不能禁用静态库。
关于库的更详细讨论,可以参阅 BLFS 手册中的 Libraries: Static or shared? 一节。
内核对外提供了一些文件系统,以便自己和用户空间进行通信。 它们是虚拟文件系统,并不占用磁盘空间,其内容保留在内存中。
首先创建这些文件系统的挂载点:
mkdir -pv $LFS/{dev,proc,sys,run}
在内核引导系统时,它需要一些设备节点,特别是 console
和 null
两个设备。 它们需要创建在硬盘上,这样在
udevd
启动前即可使用,特别是在 Linux 使用 init=/bin/bash
内核选项启动的时候。
运行以下命令创建它们:
mknod -m 600 $LFS/dev/console c 5 1 mknod -m 666 $LFS/dev/null c 1 3
用设备文件填充 /dev
目录的推荐方法是挂载一个虚拟文件系统(例如 tmpfs
),
然后在设备被发现或访问时动态地创建设备文件。 这个工作通常由 Udev 在系统引导时完成,但我们的新系统还没有 Udev,
也没有被引导过,因此必须手工挂载和填充 /dev
。
这通过绑定挂载宿主系统的 /dev
目录就可以实现,绑定挂载是一种特殊挂载类型, 它允许在另外的位置创建某个目录或挂载点的映像。 运行以下命令进行绑定挂载:
mount -v --bind /dev $LFS/dev
现在挂载其他虚拟内核文件系统:
mount -vt devpts devpts $LFS/dev/pts -o gid=5,mode=620 mount -vt proc proc $LFS/proc mount -vt sysfs sysfs $LFS/sys mount -vt tmpfs tmpfs $LFS/run
devpts 挂载选项的含义:
gid=5
这保证 devpts 创建的所有设备节点都属于 ID 为 5 的组, 我们之后会把这个 ID 分配给
tty
组。这里使用组 ID 而不是名称,
因为宿主系统的 tty
组可能有不同的 ID
。
mode=0620
该选项确保 devpts 创建的所有设备节点具有权限码 0620 (所有者可读写,组成员可写)。与上一个选项结合使用, 可以保证 devpts 创建符合 grantpt() 要求的设备节点, 摆脱对 Glibc pt_chown 辅助程序 (默认不安装)的依赖。
在某些宿主系统上,/dev/shm
是一个指向
/run/shm
的符号链接。我们已经在 /run 下挂载了
tmpfs 文件系统, 因此在这里只需要创建一个目录。
if [ -h $LFS/dev/shm ]; then mkdir -pv $LFS/$(readlink $LFS/dev/shm) fi
软件包管理是一个经常被人请求添加到 LFS 手册中的内容。 包管理器可以跟踪软件包安装的文件, 这样就可以很容易地删除或者更新软件包。 和二进制程序、库文件一样,包管理器也会处理软件包安装的配置文件。 在您开始想入非非的时候,不! —— 本节不会讨论或推荐任何特定的包管理器。 本节对软件包管理的流行技术及其工作原理进行综述,对您来说, 完美的包管理器可能是以下的某个技术,或者可能是几个技术的结合。 本节还将简要提到在升级软件包时可能遇到的问题。
LFS 或 BLFS 不采用任何包管理器的原因有:
处理包管理器会偏离本书的重点目标 —— 讲述如何构建 Linux 系统。
存在多种软件包管理的解决方案,它们各有优缺点, 很难找到一种让所有读者满意的方案。
在 LFS 的 Hints Project 中有许多关于软件包管理的提示,您可以在其中查找, 看看有没有符合您需要的方案。
包管理器可以在软件包新版本发布后容易地完成升级。一般来说, 使用 LFS 或者 BLFS 手册中的指令即可升级软件包。 下面是您在升级时必须注意的重点,特别是升级正在运行的系统时。
如果需要升级 Glibc (例如从 Glibc-2.19 升级到 Glibc-2.20), 最安全的方法是重新构建 LFS 。尽管您或许 能按依赖顺序重新构建所有软件包,但我们不推荐这样做。
如果更新了一个包含共享库的软件包,而且共享库的名称发生改变, 那么所有动态链接到这个库的软件包都需要重新编译,
以链接到新版本的库。 (注意,软件包的版本和共享库的名称没有关系。) 例如,如果一个软件包 foo-1.2.3
安装了名为 libfoo.so.1
的共享库,您把该软件包升级到了新版本 foo-1.2.4,它安装了名为 libfoo.so.2
的共享库。那么,所有链接到
libfoo.so.1
的软件包都要重新编译以链接到 libfoo.so.2
。
注意,在重新编译这些软件包之前,您不能删除旧版本的库。
文中的“共享库的名称”应解释为 ELF 文件中的
DT_SONAME,即 ldd 命令在箭头前输出的内容。
根据语义化版本所述, 共享库主版本号的递增表示 API
与之前版本不兼容, 故主版本号往往被包含在共享库名称中,例如 libfoo.so.2
中的 2 。
以下是几种常见的软件包管理技术,在决定使用某种包管理器前, 请研读这些技术,特别是要了解特定技术的不足。
您没有看错,这是一种包管理技术。有些人觉得不需要管理软件包, 因为他们十分了解软件包,知道每个软件包安装了什么文件。 有的用户则计划每次有软件包发生变动时就重新构建系统, 所以不需要管理软件包。
这是一种最简单的软件包管理方式, 它不需要任何额外的软件来控制软件包的安装。 例如,软件包 foo-1.1
将会被安装在 /usr/pkg/foo-1.1
,
然后创建一个到该目录的符号链接 /usr/pkg/foo
。
在安装新版本 foo-1.2 的时候,把它安装到 /usr/pkg/foo-1.2
, 然后把之前的符号链接替换,使其链接到新版本。
PATH
、LD_LIBRARY_PATH
、 MANPATH
、INFOPATH
和 CPPFLAGS
等环境变量需要被扩充,以包含
/usr/pkg/foo
。一旦软件包的数量较多,
这种架构就会变得无法管理。
这是前一种软件包管理技术的变种。 将各个软件包同样安装在独立的目录中,但不建立目录的软链接,
而是把其中的每个文件链接到 /usr
目录树中对应的位置,
这样就不需要修改环境变量。这些链接可以由用户自己创建, 也可以自动化进行,一些流行的包管理器如
Stow、Epkg、Graft 和 Depot 使用这种管理方式。
安装过程需要伪装,使得软件包认为它处于 /usr
中,尽管它实际上被安装 在 /usr/pkg
目录结构中。
这样安装软件包一般不是简单的任务,例如考虑安装软件包 libfoo-1.1, 下面的指令可能不能正确安装该软件包:
./configure --prefix=/usr/pkg/libfoo/1.1 make make install
尽管安装本身可以进行, 但依赖于它的软件包可能不会像你期望的那样链接 libfoo 库。 如果要编译一个依赖于
libfoo 的软件包,您可能发现它链接到了 /usr/pkg/libfoo/1.1/lib/libfoo.so.1
而不是您期望的 /usr/lib/libfoo.so.1
。
正确的做法是使用 DESTDIR
策略伪装软件包的安装过程,
就像下面这样:
./configure --prefix=/usr make make DESTDIR=/usr/pkg/libfoo/1.1 install
多数软件包可以这样安装,但有些不能。对于那些不兼容的软件包,
您要么亲自动手安装,要么更简单地把一些出问题的软件包安装在 /opt
中。
在这个方案中,安装一个软件包之前,为它创建一个时间戳文件。 在安装后,用一行简单的 find 命令, 加上正确的参数,就能生成安装日志, 包含在时间戳文件创建以后安装的所有文件。 有一个采用这个方案的包管理器叫做 install-log 。
尽管这种方式很简单,但它有两个缺点。如果在安装过程中, 某些文件没有以当前时间作为时间戳安装,它们就不能被包管理器跟踪。 另外,只有每次只安装一个软件包时才能使用这种技术, 如果在两个终端中同时安装两个不同的软件包, 它们的安装日志就不可靠了。
在这种方式中,安装脚本执行的命令被记录下来。 有两种技术可以进行记录:
在安装前设置 LD_PRELOAD
环境变量,
将其指向一个库以在安装过程中预加载它。在安装过程中, 这个库将自身附加在cp、 install、mv 等可执行文件上,
跟踪修改文件系统的系统调用。如果要使用这种方法, 所有需要跟踪的可执行文件必须是动态链接的,且没有设定 suid 和
sgid 位。预加载动态库可能在安装过程中导致不希望的副作用, 因此建议先进行一些测试,以确保包管理器不会造成破坏,
并且记录了所有应该记录的文件。
第二种技术是使用 strace, 它能够记录安装脚本执行过程中的所有系统调用。
在这种架构中,软件包被伪装安装到一个独立的目录树中, 就像软链接风格的软件包管理那样。 在安装后,使用被安装的文件创建一个软件包存档, 它可以被用来在本地机器甚至其他机器上安装该软件包。
大多数商业发行版的包管理器采用这种策略,例如 RPM (值得一提的是,它被 Linux Standard Base 规则 所要求),pkg-utils、 Debian 的 apt、Gentoo 的 Portage 系统)等。 LFS Hint 中的一条提示描述了如何为 LFS 系统适用这种管理方式: http://www.linuxfromscratch.org/hints/downloads/files/fakeroot.txt。
创建包含依赖关系信息的软件包文件十分复杂,超过了 LFS 的范畴。
Slackware 使用一个基于 tar 的系统创建软件包档案。和更复杂的包管理器不同, 该系统有意地没有涉及软件包依赖关系。 如果想了解 Slackware 包管理器的详细信息,阅读 http://www.slackbook.org/html/package-management.html。
这种架构是 LFS 特有的,由 Matthias Benkmann 提出,可以在 Hints Project 查阅。 在该架构中,每个软件包都由一个单独的用户安装到标准位置。 只要检查文件所有者,就能找出属于一个软件包的所有文件。 它的优缺点十分复杂,不适合在本节讨论,详细信息请阅读 http://www.linuxfromscratch.org/hints/downloads/files/more_control_and_pkg_man.txt。
LFS 系统的一项优势是,没有依赖于磁盘系统中文件位置的文件。 将构建好的 LFS
系统复制到另一台具有相同硬件架构的计算机很简单, 只要用 tar 命令把包含根目录的 LFS 分区打包
(未压缩的情况下,一个基本的 LFS 系统需要 250 MB),
然后通过网络传输或者刻成光盘,复制到新的系统上,再展开即可。 这时,个别配置文件需要修改,可能需要更新的配置文件有:
/etc/hosts
, /etc/fstab
, /etc/passwd
, /etc/group
, /etc/shadow
,
/etc/ld.so.conf
, /etc/sysconfig/rc.site
, /etc/sysconfig/network
, 以及 /etc/sysconfig/ifconfig.eth0
。
由于系统硬件和原始内核配置的区别, 可能需要为新系统重新配置并构建内核。
有一些报告反映称, 在架构相近但不完全一致的计算机之间拷贝 LFS 系统时出现问题。 例如,Intel 系统使用的指令集和 AMD 处理器不完全相同, 且较新的处理器可能包含旧处理器没有的指令。
据译者所知,LFS 中默认进行机器相关优化的软件包有 GMP-6.1.2 和
libffi-$libffi-version。 在编译它们时遵循构建指示中的 “注意”
框,即可构建通用的库文件。 然后注意整个构建过程不要使用 -march
选项, 构建出的 LFS 系统就应该可以拷贝到架构相近
(uname -m
输出相同结果) 的机器上。
最后,按照 第 8.4 节 “使用 GRUB 设定引导过程” 中的指示, 为新系统配置引导加载器。
现在到了进入 chroot 环境,并开始构建和安装最终的 LFS 系统的时候。 以 root
身份, 执行以下命令,进入这个目前只包含临时工具的空间:
chroot "$LFS" /tools/bin/env -i \ HOME=/root \ TERM="$TERM" \ PS1='(lfs chroot) \u:\w\$ ' \ PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin \ /tools/bin/bash --login +h
传递给 env 的
-i
参数会清除 chroot
环境中的所有环境变量。 随后,只重新设定 HOME
、TERM
、
PS1
和 PATH
变量。 参数 TERM=$TERM
将 chroot 环境中的
TERM
变量设为和 chroot 环境外相同的值,
许多程序需要这个变量才能正常工作,例如 vim 和 less。如果需要设定其他变量,例如
CFLAGS
或 CXXFLAGS
, 在这里顺便设定比较合适。
从现在开始,就不再需要使用 LFS
环境变量, 因为所有工作都被局限在
LFS 文件系统内。这是由于 Bash 被告知 $LFS
现在是根目录 (/
)。
注意到 /tools/bin
处于 PATH
的末尾, 这意味着一旦安装了某个工具的最终版本,就不再使用对应的临时工具。
这还需要保证 shell 不 “记忆” 执行过的程序的位置 —— 因此需要传递 +h
参数给 bash 以关闭散列功能。
注意 bash 的提示符会包含
I have no name!
。
这是正常的,因为现在还没有创建 /etc/passwd
文件。
本章剩余部分和后续各章中的命令都要在 chroot 环境中运行。 如果您因为一些原因(如重新启动计算机)离开了该环境, 必须确认虚拟内核文件系统如 第 6.2.2 节 “挂载和填充 /dev” 和 第 6.2.3 节 “挂载虚拟内核文件系统” 所述挂载好, 然后重新进入 chroot 环境,才能继续安装 LFS 。
现在需要在 LFS 文件系统中创建一些目录结构。 执行以下命令,创建一棵标准目录树:
mkdir -pv /{bin,boot,etc/{opt,sysconfig},home,lib/firmware,mnt,opt} mkdir -pv /{media/{floppy,cdrom},sbin,srv,var} install -dv -m 0750 /root install -dv -m 1777 /tmp /var/tmp mkdir -pv /usr/{,local/}{bin,include,lib,sbin,src} mkdir -pv /usr/{,local/}share/{color,dict,doc,info,locale,man} mkdir -v /usr/{,local/}share/{misc,terminfo,zoneinfo} mkdir -v /usr/libexec mkdir -pv /usr/{,local/}share/man/man{1..8} mkdir -v /usr/lib/pkgconfig case $(uname -m) in x86_64) mkdir -v /lib64 ;; esac mkdir -v /var/{log,mail,spool} ln -sv /run /var/run ln -sv /run/lock /var/lock mkdir -pv /var/{opt,cache,lib/{color,misc,locate},local}
默认情况下,新创建的目录具有权限码 755 ,但这并不适合所有目录。 在以上命令中,两个目录的访问权限被修改 —— 一个是
root
的主目录, 另一个是包含临时文件的目录。
第一个修改能保证不是所有人都能进入 /root
——
一般用户也可以为他/她的主目录设置同样的 0750 权限码。 第二个修改保证任何用户都可写入 /tmp
和 /var/tmp
目录, 但不能从中删除其他用户的文件,因为所谓的 “粘滞位” (sticky bit),即八进制权限码 1777 的最高位 (1)
阻止这样做。
这个目录树是基于 Filesystem Hierarchy Standard (FHS) (可以在 https://wiki.linuxfoundation.org/en/FHS
查阅)建立的。 FHS 标准还规定了某些可选的目录, 例如 /usr/local/games
和 /usr/share/games
。
我们只创建了必要的目录,如果您需要的话可以自己创建可选这些可选目录。
有些程序使用硬编码的路径访问当前还不存在的程序。 为了满足它们的要求,需要创建一些符号链接。在本章的后续内容中, 它们将被安装好的软件包中真正的文件替代:
ln -sv /tools/bin/{bash,cat,chmod,dd,echo,ln,mkdir,pwd,rm,stty,touch} /bin ln -sv /tools/bin/{env,install,perl,printf} /usr/bin ln -sv /tools/lib/libgcc_s.so{,.1} /usr/lib ln -sv /tools/lib/libstdc++.{a,so{,.6}} /usr/lib ln -sv bash /bin/sh
每个链接的目的:
/bin/bash
许多 bash
脚本指定了 /bin/bash
。
/bin/cat
这个路径硬编码在 Glibc 配置脚本中。
/bin/dd
指向 dd
的路径会被硬编码在
/usr/bin/libtool
工具中。
/bin/echo
这是为了满足 Glibc 测试套件中的一项测试,它需要 /bin/echo
。
/usr/bin/env
这个路径被硬编码在一些软件包的构建过程中。
/usr/bin/install
指向 install
的路径会被硬编码到
/usr/lib/bash/Makefile.inc
文件中。
/bin/ln
指向 ln
的路径会被硬编码到
/usr/lib/perl5/5.30.0/<target-triplet>/Config_heavy.pl
文件中。
/bin/pwd
某些 configure 脚本,特别是 Glibc 的, 硬编码了这个路径。
/bin/rm
指向 rm
的路径会被硬编码到
/usr/lib/perl5/5.30.0/<target-triplet>/Config_heavy.pl
文件中。
/bin/stty
这个路径被硬编码到 Expect 中,创建该链接才能使得 Binutils 和 GCC 通过测试套件测试。
/usr/bin/perl
许多 Perl 脚本硬编码 perl 程序的路径。
/usr/lib/libgcc_s.so{,.1}
Glibc 需要它才能让 pthread 库正常工作。
/usr/lib/libstdc++{,.6}
Glibc 测试套件中的若干项测试需要它, 另外构建 GMP 的 C++ 支持也需要它。
/bin/sh
许多 shell 脚本硬编码路径 /bin/sh
。
历史上,Linux 在 /etc/mtab
维护已经挂载的文件系统的列表。现代内核在内部维护该列表,并通过 /proc
文件系统将它展示给用户。 为了满足那些需要 /etc/mtab
的工具, 执行以下命令,创建符号链接:
ln -sv /proc/self/mounts /etc/mtab
为了使得 root
能正常登录,而且它的用户名能被正常识别,必须在文件 /etc/passwd
和 /etc/groups
中写入相关的条目。
执行以下命令创建 /etc/passwd
文件:
cat > /etc/passwd << "EOF"
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/dev/null:/bin/false
daemon:x:6:6:Daemon User:/dev/null:/bin/false
messagebus:x:18:18:D-Bus Message Daemon User:/var/run/dbus:/bin/false
nobody:x:99:99:Unprivileged User:/dev/null:/bin/false
EOF
我们以后再设置 root
用户的实际密码(这里的
“x”
只是一个占位符)。
执行以下命令,创建 /etc/group
文件:
cat > /etc/group << "EOF"
root:x:0:
bin:x:1:daemon
sys:x:2:
kmem:x:3:
tape:x:4:
tty:x:5:
daemon:x:6:
floppy:x:7:
disk:x:8:
lp:x:9:
dialout:x:10:
audio:x:11:
video:x:12:
utmp:x:13:
usb:x:14:
cdrom:x:15:
adm:x:16:
messagebus:x:18:
input:x:24:
mail:x:34:
kvm:x:61:
wheel:x:97:
nogroup:x:99:
users:x:999:
EOF
这里创建的用户组并不属于任何标准 —— 它们一部分是为了满足本章中 Udev 配置的需要,另一部分借鉴了一些 Linux
发行版的通用惯例。 另外,某些测试套件需要特定的用户或组。Linux Standard Base (LSB,可以在
http://www.linuxbase.org 查看)
标准只推荐以组 ID 0 创建用户组 root
, 以及以组
ID 1 创建用户组 bin
, 其他组名和组 ID
由系统管理员自由分配,因为好的程序不会依赖组 ID 数字, 而是使用组名。
为了移除 “I have no
name!” 提示符,需要打开一个新 shell。由于在 第 5 章 中已经安装了一份完整的
Glibc,而且刚才创建了文件 /etc/passwd
和
/etc/group
, 用户名和组名现在就可以正常解析了。
exec /tools/bin/bash --login +h
注意这里使用了 +h
参数, 它告诉
bash
不要使用内部的路径散列机制。 如果没有指定该参数,bash 会记忆它执行过程序的路径。
为了在安装新编译好的程序后马上使用它们,在本章中总是使用 +h
。
login、agetty 和 init 等程序使用一些日志文件, 以记录登录系统的用户和登录时间等信息。 然而,这些程序不会创建不存在的日志文件。初始化日志文件, 并为它们设置合适的访问权限:
touch /var/log/{btmp,lastlog,faillog,wtmp} chgrp -v utmp /var/log/lastlog chmod -v 664 /var/log/lastlog chmod -v 600 /var/log/btmp
文件 /var/log/wtmp
记录所有的登录和登出, 文件
/var/log/lastlog
记录每个用户最后登录的时间,文件
/var/log/faillog
记录所有失败的登录尝试,文件
/var/log/btmp
记录所有错误的登录尝试。
文件 /run/utmp
记录当前登录的用户,
它由启动脚本动态创建。
Linux API 头文件 (在 linux-5.2.8.tar.xz 中) 导出内核 API 供 Glibc 使用。
Linux 内核需要导出一个应用程序编程接口 (API) 供系统的 C 运行库 (例如 LFS 中的 Glibc)使用。 这通过净化内核源码包中提供的若干 C 头文件完成。
确保在之前的活动中没有留下陈旧的文件和依赖关系:
make mrproper
现在从源码中提取用户可见的内核头文件。 它们被放置在一个临时本地目录中,然后再复制到需要的位置, 这是因为提取过程会删除目标目录中的所有文件。 另外,提取结果中有一些内核开发者使用的隐藏文件,LFS 不需要它们, 我们可以在临时目录中删除这些文件。
make INSTALL_HDR_PATH=dest headers_install find dest/include \( -name .install -o -name ..install.cmd \) -delete cp -rv dest/include/* /usr/include
Man-pages 软件包包含 2,200 多个 man 页面。
执行以下命令安装 Man-pages:
make install
Glibc 软件包包含主要的 C 语言库。它提供用于分配内存、检索目录、 打开和关闭文件、读写文件、字符串处理、模式匹配、 算术等用途的基本子程序。
Glibc 构建系统是自给自足的, 即使编译器 specs 文件和链接器仍然指向 /tools
, 也能完美地安装 Glibc 。在安装 Glibc
之前不能调整工具链, 否则 Glibc 的 autoconf 测试会给出错误结果, 结果无法达成干净地构建 Glibc
这一目的。
某些 Glibc 程序使用与 FHS 不兼容的 /var/db
目录存放运行时数据。应用下列补丁, 使得这些程序在 FHS 兼容的位置存储运行时数据:
patch -Np1 -i ../glibc-2.30-fhs-1.patch
修复 linux-5.2 内核引入的一个问题:
sed -i '/asm.socket.h/a# include <linux/sockios.h>' \ sysdeps/unix/sysv/linux/bits/socket.h
为了兼容 LSB 创建一个符号链接。另外, 对于 x86_64 ,还要创建一个动态链接器的兼容性符号链接, 使它能够正常工作:
case $(uname -m) in i?86) ln -sfv ld-linux.so.2 /lib/ld-lsb.so.3 ;; x86_64) ln -sfv ../lib/ld-linux-x86-64.so.2 /lib64 ln -sfv ../lib/ld-linux-x86-64.so.2 /lib64/ld-lsb-x86-64.so.3 ;; esac
Glibc 文档推荐在专用目录中构建它:
mkdir -v build cd build
准备安装 Glibc:
CC="gcc -ffile-prefix-map=/tools=/usr" \ ../configure --prefix=/usr \ --disable-werror \ --enable-kernel=3.2 \ --enable-stack-protector=strong \ --with-headers=/usr/include \ libc_cv_slibdir=/lib
新的配置选项和参数的含义:
CC="gcc
-ffile-prefix-map=/tools=/usr"
使 GCC 在编译结果中记录文件的路径时, 将 /tools 中的文件记录为 /usr 中的对应路径。 这可以避免调试符号中出现无效路径。
--disable-werror
该选项禁用 GCC 的 -Werror 选项, 这对于运行测试套件来说是必须的。
--enable-stack-protector=strong
该选项通过加入额外代码, 对栈溢出攻击等导致的缓冲区溢出进行检查,以提高系统安全性。
--with-headers=/usr/include
该选项指定构建系统搜索内核 API 头文件的位置。 默认情况下,该位置是 /tools/include
。
libc_cv_slibdir=/lib
这个变量纠正库文件安装位置, 我们不希望使用 lib64 目录。
编译该软件包:
make
在本节中, Glibc 的测试套件十分关键,在任何情况下都不能跳过。
通常来说,可能会有极少数测试不能通过, 下面列出的失败结果一般可以安全地忽略。执行以下命令进行测试:
case $(uname -m) in i?86) ln -sfnv $PWD/elf/ld-linux.so.2 /lib ;; x86_64) ln -sfnv $PWD/elf/ld-linux-x86-64.so.2 /lib ;; esac
我们需要上面的符号链接,以便在当前的 chroot 构建环境中运行测试套件。下面的安装过程将会覆盖它。
make check
您可能看到一些失败结果。 Glibc 的测试套件和宿主系统之间有某种依赖关系,以下是在一些版本的 LFS 上最常见的问题:
已知 misc/tst-ttyname 在 LFS chroot 环境中会失败。
已知 inet/tst-idna_name_classify 在 LFS chroot 环境中会失败。
已知 posix/tst-getaddrinfo4 和 posix/tst-getaddrinfo5 在某些硬件架构上会失败。
已知 nss/tst-nss-files-hosts-multi 可能失败,原因尚未查明。
rt/tst-cputimer{1,2,3} 测试依赖于宿主系统的内核。已知内核版本 4.14.91–4.14.96, 4.19.13–4.19.18,以及 4.20.0–4.20.5 会导致它们失败。
如果 CPU 不是较新的 Intel 或 AMD 处理器, 数学测试有时会失败。
在安装 Glibc 时,它会抱怨文件 /etc/ld.so.conf
不存在。尽管这是一条无害的消息,执行以下命令即可防止这个警告:
touch /etc/ld.so.conf
修正生成的 Makefile , 跳过一个在 LFS 的不完整环境中会失败的完整性检查:
sed '/test-installation/s@$(PERL)@echo not running@' -i ../Makefile
安装该软件包:
make install
安装 nscd 的配置文件和运行时目录:
cp -v ../nscd/nscd.conf /etc/nscd.conf mkdir -pv /var/cache/nscd
下面,安装一些 locale ,它们可以使得系统用不同语言响应用户请求。 这些 locale 都不是必须的,但是如果缺少了它们中的某些, 在将来运行软件包的测试套件时,可能跳过重要的测试。
可以用 localedef
程序安装单独的 locale 。 例如,下面的第一个 localedef 命令将 /usr/share/i18n/locales/cs_CZ
中的字符集无关
locale 定义和 /usr/share/i18n/charmaps/UTF-8.gz
中的字符映射定义组合起来,并附加到 /usr/lib/locale/locale-archive
文件。
以下命令将会安装能够覆盖测试所需的最小 locale 集合:
mkdir -pv /usr/lib/locale localedef -i POSIX -f UTF-8 C.UTF-8 2> /dev/null || true localedef -i cs_CZ -f UTF-8 cs_CZ.UTF-8 localedef -i de_DE -f ISO-8859-1 de_DE localedef -i de_DE@euro -f ISO-8859-15 de_DE@euro localedef -i de_DE -f UTF-8 de_DE.UTF-8 localedef -i el_GR -f ISO-8859-7 el_GR localedef -i en_GB -f UTF-8 en_GB.UTF-8 localedef -i en_HK -f ISO-8859-1 en_HK localedef -i en_PH -f ISO-8859-1 en_PH localedef -i en_US -f ISO-8859-1 en_US localedef -i en_US -f UTF-8 en_US.UTF-8 localedef -i es_MX -f ISO-8859-1 es_MX localedef -i fa_IR -f UTF-8 fa_IR localedef -i fr_FR -f ISO-8859-1 fr_FR localedef -i fr_FR@euro -f ISO-8859-15 fr_FR@euro localedef -i fr_FR -f UTF-8 fr_FR.UTF-8 localedef -i it_IT -f ISO-8859-1 it_IT localedef -i it_IT -f UTF-8 it_IT.UTF-8 localedef -i ja_JP -f EUC-JP ja_JP localedef -i ja_JP -f SHIFT_JIS ja_JP.SIJS 2> /dev/null || true localedef -i ja_JP -f UTF-8 ja_JP.UTF-8 localedef -i ru_RU -f KOI8-R ru_RU.KOI8-R localedef -i ru_RU -f UTF-8 ru_RU.UTF-8 localedef -i tr_TR -f UTF-8 tr_TR.UTF-8 localedef -i zh_CN -f GB18030 zh_CN.GB18030 localedef -i zh_HK -f BIG5-HKSCS zh_HK.BIG5-HKSCS
另外,安装适合您自己国家、语言和字符集的 locale 。
建议中文用户安装 zh_CN.UTF-8 作为日常使用的 locale。GB18030 是为了后向兼容古老的 GB2312 而设计的编码, 在现代 Linux 系统和互联网使用时会引起一些奇怪的问题。 中华人民共和国国家标准化管理委员会的官方网站已经切换到 UTF-8。
或者,也可以一次安装 glibc-2.30/localedata/SUPPORTED
中列出的所有
locale (包括上面列出的所有 locale,以及其他很多)。 执行下面这个需要很长时间的命令:
make localedata/install-locales
如果需要,再使用 localedef 命令创建和安装
glibc-2.30/localedata/SUPPORTED
中没有列出的 locale ,当然您不太可能需要它们。
目前 glibc 在解析国际化域名时使用 libidn2, 形成了一个运行时依赖关系。如果需要使用解析国际化域名的功能,参阅 BLFS libidn2 页面 安装 libidn2。
由于 Glibc 的默认值在网络环境下不能很好地工作, 需要创建配置文件 /etc/nsswitch.conf
。
执行以下命令创建新的 /etc/nsswitch.conf
:
cat > /etc/nsswitch.conf << "EOF"
# Begin /etc/nsswitch.conf
passwd: files
group: files
shadow: files
hosts: files dns
networks: files
protocols: files
services: files
ethers: files
rpc: files
# End /etc/nsswitch.conf
EOF
输入以下命令,安装并设置时区数据:
tar -xf ../../tzdata2019b.tar.gz ZONEINFO=/usr/share/zoneinfo mkdir -pv $ZONEINFO/{posix,right} for tz in etcetera southamerica northamerica europe africa antarctica \ asia australasia backward pacificnew systemv; do zic -L /dev/null -d $ZONEINFO ${tz} zic -L /dev/null -d $ZONEINFO/posix ${tz} zic -L leapseconds -d $ZONEINFO/right ${tz} done cp -v zone.tab zone1970.tab iso3166.tab $ZONEINFO zic -d $ZONEINFO -p America/New_York unset ZONEINFO
zic 命令的含义:
zic -L
/dev/null ...
该命令创建没有闰秒的 POSIX 时区。一般的惯例是将它们安装在 zoneinfo
和 zoneinfo/posix
两个目录中。前者是必须的,否则若干测试套件会报告错误。 在嵌入式系统上,如果存储空间十分紧张,
而且您永远不会更新时区信息,您可以不使用 posix
目录, 以节约 1.9
MB,但个别程序或测试套件可能会失败。
zic -L
leapseconds ...
该命令创建正确的,包含闰秒的时区。在嵌入式系统上, 如果存储空间十分紧张,而且您永远不会更新时区信息,
也不关心系统时间是否正确,您可以跳过 right
目录, 以节约 1.9 MB。
zic ...
-p ...
该命令创建 posixrule
文件。
我们使用纽约时区,因为 POSIX 要求与美国一致的夏令时规则。
一种确定本地时区的方法是运行脚本:
tzselect
在回答关于当前位置的若干问题后,脚本会输出对应时区的名字 (例如America/Edmonton)。 在
/usr/share/zoneinfo
中还有一些该脚本不能识别,但可以使用的时区,如 Canada/Eastern 或者 EST5EDT。
确定时区后,执行以下命令,创建 /etc/localtime
:
ln -sfv /usr/share/zoneinfo/<xxx>
/etc/localtime
将 <xxx>
替换成选定时区的名称 (例如 Canada/Eastern)。
默认情况下,动态加载器 (/lib/ld-linux.so.2
) 在 /lib
和 /usr/lib
中搜索程序运行时需要的动态库。然而,如果在其他目录中有动态库,
为了使动态加载器能够找到它们,需要把这些目录添加到文件 /etc/ld.so.conf
中。 有两个目录 /usr/local/lib
和 /opt/lib
经常包含附加的共享库,所以现在将它们添加到动态加载器的搜索目录中。
运行以下命令,创建一个新的 /etc/ld.so.conf
:
cat > /etc/ld.so.conf << "EOF"
# Begin /etc/ld.so.conf
/usr/local/lib
/opt/lib
EOF
如果希望的话,动态加载器也可以搜索一个目录,并将其中的文件包含在 ld.so.conf
中。
通常包含文件目录中的文件只有一行,指定一个期望的库文件目录。 如果需要这项功能,执行以下命令:
cat >> /etc/ld.so.conf << "EOF"
# Add an include directory
include /etc/ld.so.conf.d/*.conf
EOF
mkdir -pv /etc/ld.so.conf.d
在程序因为段错误而终止时创建栈跟踪 |
|
生成消息目录 |
|
显示文件系统指定的系统配置变量值 |
|
从管理数据库取得条目 |
|
转换给定文件的编码 |
|
创建快速装入 iconv 模块配置文件 |
|
设置运行时动态链接 |
|
报告给定程序或共享库依赖于哪些共享库 |
|
辅助 ldd 处理对象文件 |
|
给出当前区域的一些信息 |
|
编译 locale 规范 |
|
从文本输入创建简单的数据库 |
|
读取并解析内存跟踪文件,以人类可读的形式输出内存跟踪信息 |
|
一个缓存最常见命名服务请求的守护进程 |
|
显示基于程序计数器的性能剖析数据 |
|
列出正在运行的进程使用的共享库 |
|
静态链接的 ln 程序 |
|
跟踪特定命令对共享库中子程序的调用 |
|
读取并显示共享库性能剖析数据 |
|
询问用户系统所在的位置并报告对应的时区 |
|
显示正在执行的函数以跟踪程序执行 |
|
输出当前时间在多个时区中的表示 |
|
时区编译器 |
|
动态链接器/加载器 |
|
被 Glibc 内部用作使某些不正确的程序(例如某些 Motif 程序) 正常运行的粗糙手段,参阅
|
|
catchsegv 使用的段错误信号处理程序 |
|
异步的命名查找库 |
|
主要的 C 运行库 |
|
密码学库 |
|
动态链接接口库 |
|
没有功能的空库,曾经是 g++ 的运行库。 |
|
数学库 |
|
链接到该库时启用内存分配检查 |
|
被 memusage 用于收集程序内存使用信息 |
|
网络服务库 |
|
命名服务开关库,包含用于解析域名、用户名、组名、代号、 服务、协议等的函数。 |
|
可以预加载它,以对程序进行基于程序计数器的性能剖析 |
|
POSIX 线程库 |
|
包含用于创建、发送和解析因特网域名服务数据包的函数。 |
|
包含 POSIX.1b 实时扩展要求的多数接口 |
|
包含用于构建多线程程序调试的函数器 |
|
包含许多 Unix 工具使用的 “标准” 函数 |
现在最终的 C 运行库已经安装好了,这时就要调整工具链, 以便将新编译的任何程序都链接到新的 C 运行库。
首先,备份 /tools
中的链接器,把它替换成第 5
章准备的调整好的链接器。 我们也会把 /tools/$(uname
-m)-pc-linux-gnu/bin
中对应的链接器替换成一个符号链接:
mv -v /tools/bin/{ld,ld-old} mv -v /tools/$(uname -m)-pc-linux-gnu/bin/{ld,ld-old} mv -v /tools/bin/{ld-new,ld} ln -sv /tools/bin/ld /tools/$(uname -m)-pc-linux-gnu/bin/ld
下面修改 GCC 的 specs 文件,使其指向新的动态链接器。 把所有的 “/tools” 删除掉就会留下正确的路径。 另外,调整 specs 文件,使得 GCC 知道去哪里寻找正确的头文件和 Glibc 启动文件。一个 sed 命令即可完成以上工作:
gcc -dumpspecs | sed -e 's@/tools@@g' \ -e '/\*startfile_prefix_spec:/{n;s@.*@/usr/lib/ @}' \ -e '/\*cpp:/{n;s@$@ -isystem /usr/include@}' > \ `dirname $(gcc --print-libgcc-file-name)`/specs
这时最好浏览一下生成的 specs 文件,以确认确实进行了我们期望的修改。
现在的当务之急是保证调整过的工具链的基本功能(编译和链接) 能够像我们期望的那样工作。为此,进行下列完整性检查:
echo 'int main(){}' > dummy.c cc dummy.c -v -Wl,--verbose &> dummy.log readelf -l a.out | grep ': /lib'
上述命令不应该出现错误,最后一行命令输出的结果应该 (不同平台的动态链接器名称可能不同)是:
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
注意在 64 位系统上 /lib
是动态链接器的位置,但通过
/lib64 中的符号链接访问。
虽然可以把编译器查找动态链接器的位置修改到 /lib 中, 使得构建出的 LFS 系统完全不需要 /lib64 目录, 但动态链接器的位置是硬编码在 ELF 文件中的。 因此,为了运行其他机器编译出来的二进制文件 (例如 BLFS 中安装 JDK 时), 必须创建该符号链接以符合通用惯例。
在 32 位系统上,解释器是 /lib/ld-linux.so.2 。
下面确认我们的设定能够使用正确的启动文件:
grep -o '/usr/lib.*/crt[1in].*succeeded' dummy.log
以上命令应该输出:
/usr/lib/../lib/crt1.o succeeded
/usr/lib/../lib/crti.o succeeded
/usr/lib/../lib/crtn.o succeeded
确认编译器能正确查找头文件:
grep -B1 '^ /usr/include' dummy.log
该命令应当输出:
#include <...> search starts here:
/usr/include
下一步确认新的链接器使用了正确的搜索路径:
grep 'SEARCH.*/usr/lib' dummy.log |sed 's|; |\n|g'
那些包含 '-linux-gnu' 的路径应该忽略, 除此之外,以上命令应该输出:
SEARCH_DIR("/usr/lib")
SEARCH_DIR("/lib")
之后确认我们使用了正确的 libc:
grep "/lib.*/libc.so.6 " dummy.log
以上命令应该输出:
attempt to open /lib/libc.so.6 succeeded
最后,确认 GCC 使用了正确的动态链接器:
grep found dummy.log
以上命令应该输出(不同平台的动态链接器名称可能不同):
found ld-linux-x86-64.so.2 at /lib/ld-linux-x86-64.so.2
如果输出和以上描述不符,或者根本没有输出, 那么必然有什么地方出了严重错误。检查并重新跟踪以上步骤, 找到问题的原因,并修复它。 最可能的原因是修改 specs 文件的时候出了错误。 这里出现的任何问题在继续构建前都必须解决。
在确认一切工作良好后,删除测试文件:
rm -v dummy.c a.out dummy.log
Zlib 软件包包含一些程序使用的压缩和解压缩子程序。
准备安装 Zlib:
./configure --prefix=/usr
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
需要把共享库移动到 /lib
, 因此还需要重新创建
/usr/lib
中的 .so
符号链接:
根据 FHS 的要求, /bin
和
/sbin
中程序用到的共享库需要移动到
/lib
。 移动共享库的目的是,在无法挂载
/usr
(例如磁盘损坏或网络离线)的情况下,
仍然能使用最基本的系统功能。如果您没有使用单独的 /usr
文件系统,也不在意 FHS 兼容性,可以跳过移动共享库的步骤。 本书和 BLFS
手册中还有其他一些地方需要移动共享库。
mv -v /usr/lib/libz.so.* /lib ln -sfv ../../lib/$(readlink /usr/lib/libz.so) /usr/lib/libz.so
File 软件包包含用于确定给定文件类型的工具。
准备安装 File:
./configure --prefix=/usr
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
Readline 软件包包含一些提供命令行编辑和历史记录功能的库。
重新安装 Readline 会导致旧版本的库被重命名为 <库名称>.old 。这一般不是问题,但某些情况下会触发 ldconfig 的一个链接 bug 。 运行以下两条 sed 命令防止这种情况:
sed -i '/MV.*old/d' Makefile.in sed -i '/{OLDSUFF}/c:' support/shlib-install
准备安装 Readline:
./configure --prefix=/usr \ --disable-static \ --docdir=/usr/share/doc/readline-8.0
编译该软件包:
make SHLIB_LIBS="-L/tools/lib -lncursesw"
make 命令选项的含义
SHLIB_LIBS="-L/tools/lib
-lncursesw"
该选项强制 Readline 链接到 libncursesw
库。
该软件包不包含测试套件。
安装该软件包:
make SHLIB_LIBS="-L/tools/lib -lncursesw" install
下面将动态库移动到更合适的位置,并修正访问权限和符号链接:
mv -v /usr/lib/lib{readline,history}.so.* /lib chmod -v u+w /lib/lib{readline,history}.so.* ln -sfv ../../lib/$(readlink /usr/lib/libreadline.so) /usr/lib/libreadline.so ln -sfv ../../lib/$(readlink /usr/lib/libhistory.so ) /usr/lib/libhistory.so
如果您希望的话,可以安装文档:
install -v -m644 doc/*.{ps,pdf,html,dvi} /usr/share/doc/readline-8.0
M4 软件包包含一个宏处理器。
首先,进行 glibc-2.28 要求的一些修补:
sed -i 's/IO_ftrylockfile/IO_EOF_SEEN/' lib/*.c echo "#define _IO_IN_BACKUP 0x100" >> lib/stdio-impl.h
准备安装 M4:
./configure --prefix=/usr
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
Bc 软件包包含一个任意精度数值处理语言。
准备安装 Bc:
PREFIX=/usr CC=gcc CFLAGS="-std=c99" ./configure.sh -G -O3
配置选项的含义:
CC=gcc
CFLAGS="-std=c99"
该选项指定编译时使用的 C 编译器和标准。
-O3
该选项指定编译时使用的优化等级。
-G
忽略在没有 GNU bc 存在时无法工作的测试。
编译该软件包:
make
为了测试 bc,运行:
make test
安装该软件包:
make install
Binutils 包含汇编器、链接器以及其他用于处理目标文件的工具。
进行简单测试,确认伪终端(PTY)在 chroot 环境中能正常工作:
expect -c "spawn ls"
该命令应该输出:
spawn ls
如果输出不是上面这样,而是下面的消息,就说明环境没有为 PTY 的正常工作设置好。 在运行 Binutils 和 GCC 的测试套件前必须解决这个问题。
The system has no more ptys.
Ask your system administrator to create more.
删除一项导致测试套件无法完成的测试:
sed -i '/@\tincremental_copy/d' gold/testsuite/Makefile.in
Binutils 文档推荐在一个专用的构建目录中构建 Binutils:
mkdir -v build cd build
准备安装 Binutils:
../configure --prefix=/usr \ --enable-gold \ --enable-ld=default \ --enable-plugins \ --enable-shared \ --disable-werror \ --enable-64-bit-bfd \ --with-system-zlib
配置选项的含义:
--enable-gold
构建 gold 链接器,并且将它(和默认链接器一起)安装为 ld.gold。
--enable-ld=default
构建传统的 bfd 链接器,并且将它安装为 ld (默认链接器) 和 ld.bfd。
--enable-plugins
启用链接器插件支持。
--enable-64-bit-bfd
(在字长较小的宿主平台上)启用 64 位支持。 在 64 位平台上可能不需要,但无害。
--with-system-zlib
使用安装好的 zlib 库,而不是构建附带的版本。
编译该软件包:
make tooldir=/usr
make 命令选项的含义:
tooldir=/usr
一般来说,工具目录(最终存放该软件包中可执行文件的目录) 被设定为 $(exec_prefix)/$(target_alias)
。例如,在
x86_64 机器上,它将展开为 /usr/x86_64-unknown-linux-gnu
。因为 LFS
是定制系统,不需要 /usr
中的特定目标工具目录。如果系统用于交叉编译 (例如,在 Intel 机器上编译软件包, 生成可以在
PowerPC 机器上执行的代码),, 就会使用 $(exec_prefix)/$(target_alias)
目录。
本节中,Binutils 的测试套件被认为是十分关键的, 在任何情况下都不能跳过。
测试编译结果:
make -k check
已知 PC-relative offset 测试, 以及名为 debug_msg.sh 的一个测试可能失败。
安装该软件包:
make tooldir=/usr install
将程序中的地址翻译成文件名和行号; 给定一个内存地址以及可执行程序的名字, 该程序使用可执行文件中的调试信息, 确定与该地址相关的源代码文件和行号。 |
|
创建、修改、提取档案文件 |
|
一个能够汇编 gcc 输出的汇编代码并生成目标文件的汇编器 |
|
被链接器用于 demangle C++ 和 Java 符号, 防止重载函数冲突。 |
|
DWARF 封装工具 |
|
更改 ELF 文件的 ELF 头 |
|
显示函数调用图性能分析数据 |
|
一个链接器,将一些对象文件和档案文件组合为一个单独的文件, 重定位它们的数据,并绑定符号引用 |
|
ld 的一个裁减版,只支持 ELF 目标文件格式 |
|
ld 的硬链接 |
|
列出给定目标文件中的符号 |
|
将一种目标文件翻译成另一种 |
|
显示给定目标文件的信息,通过命令行选项指定要显示哪些信息; 这些信息对开发编译工具的程序员很有用 |
|
生成档案文件内容的索引,并将索引存入档案文件; 索引列出档案文件中所有可重定位目标文件定义的符号 |
|
显示 ELF 格式二进制文件的信息 |
|
列出给定文件各个段的大小和文件总大小 |
|
对于每个给定文件,输出其中长度不小于给定长度 (默认是 4)的可打印字符序列;对于目标文件, 它默认只输出可加载的已初始化数据段中的字符串, 对于其他文件,它扫描整个文件 |
|
移除目标文件中的符号 |
|
二进制文件描述符库 |
|
一个用于处理操作码 —— 处理器指令的 “可读文本”版本的库; 它被 objdump 等构建工具所使用 |
GMP 软件包包含提供任意精度算术函数的数学库。
如果您在为 32 位 x86 构建 LFS ,但您的 CPU 能够运行 64 位代码, 而且 您指定了 CFLAGS
环境变量,配置脚本会试图为 64 位 CPU
进行配置并且失败。为了避免这个问题,像下面这样执行 configure 命令:
ABI=32
./configure ...
GMP 的默认设定会生成为本机处理器优化的库。 如果您希望获得适合功能不如本机的 CPU 的库, 执行以下命令,以生成通用库:
cp -v configfsf.guess config.guess cp -v configfsf.sub config.sub
准备安装 GMP:
./configure --prefix=/usr \ --enable-cxx \ --disable-static \ --docdir=/usr/share/doc/gmp-6.1.2
新的配置选项的含义:
--enable-cxx
该参数启用 C++ 支持
--docdir=/usr/share/doc/gmp-6.1.2
该变量指定文档的正确位置
编译该软件包,并生成 HTML 文档:
make make html
本节中 GMP 的测试套件被认为是关键的,无论如何不能跳过。
测试编译结果:
make check 2>&1 | tee gmp-check-log
GMP 中的代码是针对本机处理器高度优化的。 在偶然情况下,检测处理器的代码会错误识别 CPU 的功能, 导致测试套件或使用 GMP 的其他程序输出消息 “Illegal instruction” (非法指令)。 如果发生这种情况,需要加入选项 --build=x86_64-unknown-linux-gnu 并重新构建 GMP 。
务必确认测试套件中的全部 190 个测试都被通过。 运行以下命令检验结果:
awk '/# PASS:/{total+=$3} ; END{print total}' gmp-check-log
安装该软件包和文档:
make install make install-html
MPFR 软件包包含多精度数学函数。
准备安装 MPFR:
./configure --prefix=/usr \ --disable-static \ --enable-thread-safe \ --docdir=/usr/share/doc/mpfr-4.0.2
编译该软件包,并生成 HTML 文档:
make make html
本节中 MPFR 的测试套件被认为是非常关键的,无论如何不能跳过。
测试编译结果,并确认所有测试都能通过:
make check
安装该软件包及其文档:
make install make install-html
MPC 软件包包含一个任意高精度,且舍入正确的复数算术库。
准备安装 MPC:
./configure --prefix=/usr \ --disable-static \ --docdir=/usr/share/doc/mpc-1.1.0
编译该软件包,并生成 HTML 文档:
make make html
运行以下命令以测试编译结果:
make check
安装该软件包
make install make install-html
Shadow 软件包包含安全地处理密码的程序。
如果您希望强制使用强密码,参考
http://www.linuxfromscratch.org/blfs/view/9.0/postlfs/cracklib.html
以在构建 Shadow 前安装 CrackLib,然后为下面的 configure 命令附加 --with-libcrack
参数。
禁止该软件包安装 groups 程序和它的 man 页面,因为 Coreutils 会提供更好的版本。 同样,避免安装 第 6.8 节 “Man-pages-5.02” 软件包已经提供的 man 页面:
sed -i 's/groups$(EXEEXT) //' src/Makefile.in find man -name Makefile.in -exec sed -i 's/groups\.1 / /' {} \; find man -name Makefile.in -exec sed -i 's/getspnam\.3 / /' {} \; find man -name Makefile.in -exec sed -i 's/passwd\.5 / /' {} \;
不使用默认的 crypt
加密方法,使用更安全的 SHA-512
方法加密密码, 该方法也允许长度超过 8 个字符的密码。另外,还需要把 Shadow 默认使用的用户邮箱位置
/var/spool/mail
改为当前普遍使用的
/var/mail
目录:
sed -i -e 's@#ENCRYPT_METHOD DES@ENCRYPT_METHOD SHA512@' \ -e 's@/var/spool/mail@/var/mail@' etc/login.defs
如果您选择构建有 Cracklib 支持的 Shadow,执行以下命令:
sed -i 's@DICTPATH.*@DICTPATH\t/lib/cracklib/pw_dict@' etc/login.defs
进行微小的改动,使 useradd 使用 1000 作为第一个组编号:
sed -i 's/1000/999/' etc/useradd
准备安装 Shadow:
./configure --sysconfdir=/etc --with-group-name-max-length=32
配置选项的含义:
--with-group-name-max-length=32
最长用户名可以有 32 个字符,设定组名称最大长度为相同值。
编译该软件包:
make
该软件包不包含测试套件。
安装该软件包:
make install
将一个安装位置不正确的程序移动到正确位置:
mv -v /usr/bin/passwd /bin
该软件包包含用于添加、修改、删除用户和组,设定和修改它们的密码, 以及进行其他管理任务的工具。如果希望查阅关于
password shadowing
的详细解释, 阅读解压得到源代码目录树中的 doc/HOWTO
文件。如果使用 Shadow 支持,记住所有需要验证密码的程序 (如显示管理器、FTP 程序、pop3
守护进程等)都必须和 Shadow 兼容。换句话说,它们必须能使用 Shadow 加密的密码。
大多数 Linux 程序要么本身支持 Shadow,要么通过 Linux PAM 支持 Shadow。为了提高安全性,建议启用 Shadow 加密。
执行以下命令,对用户密码启用 Shadow 加密:
pwconv
执行命令,对组密码启用 Shadow 加密:
grpconv
Shadow 为 useradd
提供的配置文件有一些需要解释的事项。首先,useradd
的默认操作是创建一个用户,以及一个名字和用户名相同的组。 默认情况下,用户 ID (UID)和组 ID (GID)会从
1000 开始。 这意味着,如果您不向 useradd 传递参数,
每个用户都会属于一个不同的组。如果您不希望这样, 就要向 useradd 传递 -g
参数。默认参数保存在 /etc/default/useradd
文件中,您可以编辑其中的两个参数,以满足您的特定需求:
/etc/default/useradd
参数解释
GROUP=1000
该参数设定 /etc/group 文件中使用的第一个组编号, 您可以将它修改为您希望的任何值。注意
useradd
绝不会重用 UID 或 GID, 如果该参数指定的数字已经被使用了,它就会使用下一个可用的数字。
另外,如果在您第一次不加 -g
参数使用 useradd 时没有编号 1000
的组, 您就会在终端看到一条消息: useradd:
unknown GID 1000
。 您可以忽略这条消息,它会使用组编号 1000 。
CREATE_MAIL_SPOOL=yes
该参数使得 useradd
为新创建的用户建立邮箱文件。useradd 会使得
mail
为拥有该文件的组,并为文件赋予
0660 权限码。 如果您不希望 useradd 创建这些邮箱文件,
执行以下命令:
sed -i 's/yes/no/' /etc/default/useradd
为用户 root 选择一个密码, 并执行以下命令设定它:
passwd root
用于修改强制性密码更新的最大天数 |
|
用于修改用户全名和其他信息 |
|
用于批量更新组密码 |
|
用于批量更新用户密码 |
|
用于改变用户的默认登录 shell |
|
检查并强制当前密码过期策略 |
|
用于检查失败登录日志,设定锁定账户的最大失败次数, 或重置失败次数 |
|
用于增加或删除组的用户和管理员 |
|
以指定名称创建组 |
|
删除指定的组 |
|
在不需要超级用户权限的情况下, 允许用户管理自己的组成员列表 |
|
用于修改给定的组名称或 GID |
|
验证组文件 |
|
根据普通组文件创建或更新加密组文件 |
|
根据 |
|
报告所有用户或给定用户最后一次登录的信息 |
|
被系统用于允许用户登录 |
|
是一个限制登录时间和端口的守护进程 |
|
用于设定一个用户命名空间的 gid 映射 |
|
用于在登录会话中修改当前 GID |
|
用于设定用户命名空间的 uid 映射 |
|
用于批量创建或更新用户账户 |
|
显示一条账户不可用的消息, 它被设计为用来当作被禁用的账户的默认 shell |
|
用于修改用户或组账户的密码 |
|
检验密码文件 |
|
从普通密码文件创建或更新加密密码文件 |
|
根据 |
|
在用户 GID 设为给定组 ID 的情况下,执行给定命令 |
|
用替换的用户和组 ID 运行 shell |
|
以指定名称创建新用户,或更新新用户默认信息 |
|
删除给定用户 |
|
修改给定用户的登录名称、用户 ID、shell、初始组、 home 目录等信息 |
|
编辑 |
|
编辑 |
GCC 软件包包含 GNU 编译器集合,其中有 C 和 C++ 编译器。
在 x86_64 上构建时,修改存放 64 位库的默认路径为 “lib”:
case $(uname -m) in x86_64) sed -e '/m64=/s/lib64/lib/' \ -i.orig gcc/config/i386/t-linux64 ;; esac
GCC 文档推荐在专用的构建目录中构建 GCC:
mkdir -v build cd build
准备安装 GCC:
SED=sed \ ../configure --prefix=/usr \ --enable-languages=c,c++ \ --disable-multilib \ --disable-bootstrap \ --with-system-zlib
请注意,对于其他语言,还有一些尚未满足的依赖项。 阅读BLFS 手册, 以了解如何构建 GCC 支持的所有语言。
新的配置选项的含义:
SED=sed
设定该环境变量,防止路径 /tools/bin/sed 被硬编码到编译结果中。
--with-system-zlib
该选项使得 GCC 链接到系统安装的 Zlib 库,而不是它自带的 Zlib 副本。
编译该软件包:
make
本节中 GCC 的测试套件被认为是关键的,无论如何不能跳过。
已知 GCC 测试套件中的一组测试可能耗尽栈空间, 因此运行测试前要增加栈空间:
ulimit -s 32768
以非特权用户身份测试编译结果,但出错时继续执行其他测试:
chown -Rv nobody . su nobody -s /bin/bash -c "PATH=$PATH make -k check"
输入以下命令查看测试结果的摘要:
../contrib/test_summary
如果只想看摘要,将输出用管道送至 grep -A7
Summ
。
可以将结果与 http://www.linuxfromscratch.org/lfs/build-logs/9.0/ 和 https://gcc.gnu.org/ml/gcc-testresults/ 的结果进行比较。
已知有 6 个关于 get_time 的测试会失败。 它们似乎与 en_HK locale 有关。
已知 experimental/net 目录中名为 lookup.cc 和 reverse.cc 的两个测试在 LFS chroot 环境中会失败,因为它们需要 /etc/hosts 和 iana-etc。
已知名为 pr57193.c 和 pr90178.c 的测试会失败。
少量意外的失败有时无法避免,GCC 开发者一般知道这类问题, 但尚未解决它们。 我们可以继续安全地构建系统,除非测试结果和以上 URL 的结果截然不同。
安装该软件包,并移除一个不需要的目录:
make install rm -rf /usr/lib/gcc/$(gcc -dumpmachine)/9.1.0/include-fixed/bits/
GCC 构建目录目前属于用户 nobody
,
这会导致安装的头文件目录(及其内容)具有不正确的所有权。 将所有者修改为 root
用户和组:
chown -v -R root:root \ /usr/lib/gcc/*linux-gnu/9.2.0/include{,-fixed}
创建一个 FHS 因 “历史原因” 要求的符号链接:
ln -sv ../usr/bin/cpp /lib
许多软件包使用 cc 调用 C 编译器。为了满足它们, 创建一个符号链接:
ln -sv gcc /usr/bin/cc
创建一个兼容性符号链接,以支持在构建程序时使用链接时优化(LTO):
install -v -dm755 /usr/lib/bfd-plugins ln -sfv ../../libexec/gcc/$(gcc -dumpmachine)/9.2.0/liblto_plugin.so \ /usr/lib/bfd-plugins/
现在最终的工具链已经就位, 重要的是再次确认编译和链接像我们期望的一样正常工作。 再一次执行之前进行过的完整性检查:
echo 'int main(){}' > dummy.c cc dummy.c -v -Wl,--verbose &> dummy.log readelf -l a.out | grep ': /lib'
上述命令不应该出现错误,最后一行命令输出的结果应该 (不同平台的动态链接器名称可能不同)是:
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
下面确认我们的设定能够使用正确的启动文件:
grep -o '/usr/lib.*/crt[1in].*succeeded' dummy.log
以上命令应该输出:
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib/crt1.o succeeded
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib/crti.o succeeded
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib/crtn.o succeeded
以上结果可能随您的机器体系结构不同而略微不同, 区别一般是 /usr/lib/gcc
之后的目录名。我们关注的重点是, gcc 应该在 /usr/lib
目录中找到所有三个 crt*.o
文件。
确认编译器能正确查找头文件:
grep -B4 '^ /usr/include' dummy.log
该命令应当输出:
#include <...> search starts here:
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include
/usr/local/include
/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include-fixed
/usr/include
同样要注意以您的目标三元组命名的目录根据您体系结构的不同, 可能和以上不同。
下一步确认新的链接器使用了正确的搜索路径:
grep 'SEARCH.*/usr/lib' dummy.log |sed 's|; |\n|g'
那些包含 '-linux-gnu' 的路径应该忽略, 除此之外,以上命令应该输出:
SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib64")
SEARCH_DIR("/usr/local/lib64")
SEARCH_DIR("/lib64")
SEARCH_DIR("/usr/lib64")
SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib")
SEARCH_DIR("/usr/local/lib")
SEARCH_DIR("/lib")
SEARCH_DIR("/usr/lib");
在 32 位系统上可能显示一些不同的目录,例如下面是 i686 机器上的输出:
SEARCH_DIR("/usr/i686-pc-linux-gnu/lib32")
SEARCH_DIR("/usr/local/lib32")
SEARCH_DIR("/lib32")
SEARCH_DIR("/usr/lib32")
SEARCH_DIR("/usr/i686-pc-linux-gnu/lib")
SEARCH_DIR("/usr/local/lib")
SEARCH_DIR("/lib")
SEARCH_DIR("/usr/lib");
之后确认我们使用了正确的 libc:
grep "/lib.*/libc.so.6 " dummy.log
以上命令应该输出:
attempt to open /lib/libc.so.6 succeeded
最后,确认 GCC 使用了正确的动态链接器:
grep found dummy.log
以上命令应该输出(不同平台的动态链接器名称可能不同):
found ld-linux-x86-64.so.2 at /lib/ld-linux-x86-64.so.2
如果输出和以上描述不符,或者根本没有输出, 那么必然有什么地方出了严重错误。检查并重新跟踪以上步骤, 找到问题的原因,并修复它。 最可能的原因是修改 specs 文件的时候出了错误。 这里出现的任何问题在继续构建前都必须解决。
在确认一切工作良好后,删除测试文件:
rm -v dummy.c a.out dummy.log
最后移动一个位置不正确的文件:
mkdir -pv /usr/share/gdb/auto-load/usr/lib mv -v /usr/lib/*gdb.py /usr/share/gdb/auto-load/usr/lib
C++ 编译器 |
|
C 编译器 |
|
C 预处理器,编译器使用它展开源文件中的 #include、#define 及类似指令 |
|
C++ 编译器 |
|
C 编译器 |
|
ar 的一个包装器, 它在命令行中添加一个插件。这个程序只被用于提供链接时优化功能, 对于默认的构建选项来说没有作用。 |
|
nm 的一个包装器, 它在命令行中添加一个插件。这个程序只被用于提供链接时优化功能, 对于默认的构建选项来说没有作用。 |
|
ranlib 的一个包装器, 它在命令行中添加一个插件。这个程序只被用于提供链接时优化功能, 对于默认的构建选项来说没有作用。 |
|
一个覆盖率测试工具; 用于分析程序并确定在哪里优化最有效 |
|
离线 gcda 和 gcno 性能剖析数据显示工具 |
|
离线 gcda 性能剖析预处理工具 |
|
地址完整性检查库 |
|
GCC 内建原子操作运行库 |
|
C 预处理库 |
|
包含 gcc 的运行时支持 |
|
在 GCC 被指示启动性能剖析时,这个库被链接到程序中 |
|
OpenMP API 的 GNU 实现,用于 C/C++ 和 Fortran 的跨平台共享内存并行编程 |
|
内存泄露清理检查库 |
|
GCC 的链接时优化 (LTO) 插件, 使得 GCC 可以进行跨越编译单元的优化 |
|
GCC 四精度数学 API 库 |
|
包含 GCC 的栈溢出保护功能支持子程序 |
|
C++ 标准库 |
|
ISO/IEC TS 18822:2015 文件系统库 |
|
包含 C++ 编程语言支持子程序 |
|
线程完整性检查库 |
|
未定义行为清理检查库 |
Bzip2 软件包包含用于压缩和解压缩文件的程序。使用 bzip2 压缩文本文件可以获得比传统的 gzip 高到不知哪里去的压缩率。
应用一个补丁,以安装该软件包的文档:
patch -Np1 -i ../bzip2-1.0.8-install_docs-1.patch
以下命令保证安装的符号链接是相对的:
sed -i 's@\(ln -s -f \)$(PREFIX)/bin/@\1@' Makefile
确保 man 页面被安装到正确位置:
sed -i "s@(PREFIX)/man@(PREFIX)/share/man@g" Makefile
执行以下命令,准备编译 Bzip2:
make -f Makefile-libbz2_so make clean
make 命令参数的含义:
-f
Makefile-libbz2_so
该命令使用一个不同的 Makefile
文件构建
Bzip2,对于我们的例子来说就是使用 Makefile-libbz2_so
文件。 它创建一个共享库
libbz2.so
, 并将 Bzip2
工具链接到这个库。
编译并测试该软件包:
make
安装软件包中的程序:
make PREFIX=/usr install
安装链接到共享库的 bzip2
二进制程序到 /bin
目录,
创建必要的符号链接,并进行清理:
cp -v bzip2-shared /bin/bzip2 cp -av libbz2.so* /lib ln -sv ../../lib/libbz2.so.1.0 /usr/lib/libbz2.so rm -v /usr/bin/{bunzip2,bzcat,bzip2} ln -sv bzip2 /bin/bunzip2 ln -sv bzip2 /bin/bzcat
解压缩 bzip 压缩文件 |
|
解压缩到标准输出 |
|
对 bzip 压缩过的文件运行 cmp |
|
对 bzip 压缩过的文件运行 diff |
|
对 bzip 压缩过的文件运行 egrep 命令 |
|
对 bzip 压缩过的文件运行 fgrep 命令 |
|
对 bzip 压缩过的文件运行 grep 命令 |
|
使用 Burrows-Wheeler 块排序文本压缩算法和 Huffman 编码压缩文件;其压缩率优于更常见的使用 “Lempel-Ziv” 算法的压缩工具,如 gzip |
|
试图从损坏的 bzip2 压缩文件中恢复数据 |
|
对 bzip 压缩过的文件运行 less 命令 |
|
对 bzip 压缩过的文件运行 more 命令 |
|
这个库实现基于 Burrows-Wheeler 算法的无损块排序数据压缩 |
pkg-config 软件包提供一个在 configure 和 make 文件执行过程中向构建工具传递头文件和/或库文件路径的工具。
准备安装 Pkg-config:
./configure --prefix=/usr \ --with-internal-glib \ --disable-host-tool \ --docdir=/usr/share/doc/pkg-config-0.29.2
新的配置选项的含义:
--with-internal-glib
该选项允许 pkg-config 使用它内部的 Glib 版本, 因为 LFS 不提供外部的 Glib 。
--disable-host-tool
该选项防止创建一个指向 pkg-config 程序的不需要的硬链接。
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
Ncurses 软件包包含终端无关的字符屏幕处理库。
输入以下命令,使构建系统不安装一个 configure 脚本未处理的静态库:
sed -i '/LIBTOOL_INSTALL/d' c++/Makefile.in
准备安装 Ncurses:
./configure --prefix=/usr \ --mandir=/usr/share/man \ --with-shared \ --without-debug \ --without-normal \ --enable-pc-files \ --enable-widec
新的配置选项的含义:
--enable-widec
该选项使得宽字符库(例如 libncursesw.so.6.1
) 被构建,而不构建常规字符库(例如
libncurses.so.6.1
)。
宽字符库在多字节和传统 8 位 locale 中都能工作, 而常规字符库只能在 8 位 locale 中工作。
宽字符库和普通字符库在源码层面是兼容的,但二进制不兼容。
--enable-pc-files
该参数使得构建系统生成并安装 pkg-config 使用的 .pc 文件。
--without-normal
该选项禁止构建系统编译并安装多数静态库。
编译该软件包:
make
该软件包有测试套件,但只能在安装该软件包后才能运行。 测试用例位于 test/
中, 阅读其中的 README
文件了解更多细节。
安装该软件包:
make install
将共享库移动到期望的 /lib
目录:
mv -v /usr/lib/libncursesw.so.6* /lib
由于共享库被移走了,一个符号链接指向了不存在的文件。 重新创建它:
ln -sfv ../../lib/$(readlink /usr/lib/libncursesw.so) /usr/lib/libncursesw.so
许多程序仍然希望链接器能够找到非宽字符版本的 Ncurses 库。 通过使用符号链接和链接脚本,诱导它们链接到宽字符库:
for lib in ncurses form panel menu ; do rm -vf /usr/lib/lib${lib}.so echo "INPUT(-l${lib}w)" > /usr/lib/lib${lib}.so ln -sfv ${lib}w.pc /usr/lib/pkgconfig/${lib}.pc done
最后,确保那些在构建时寻找 -lcurses
的老式程序仍然能够构建:
rm -vf /usr/lib/libcursesw.so echo "INPUT(-lncursesw)" > /usr/lib/libcursesw.so ln -sfv libncurses.so /usr/lib/libcurses.so
如果需要的话,安装 Ncurses 文档:
mkdir -v /usr/share/doc/ncurses-6.1 cp -v -R doc/* /usr/share/doc/ncurses-6.1
上述指令没有创建非宽字符的 Ncurses 库, 因为从源码编译的软件包不会在运行时链接到它。 然而,已知的需要链接到非宽字符 Ncurses 库的二进制程序都需要版本 5,如果您为了满足一些仅有二进制版本的程序,或者满足 LSB 兼容性, 必须安装这样的库,执行以下命令再次构建该软件包:
make distclean ./configure --prefix=/usr \ --with-shared \ --without-normal \ --without-debug \ --without-cxx-binding \ --with-abi-version=5 make sources libs cp -av lib/lib*.so.5* /usr/lib
将 termcap 描述转换成 terminfo 描述 |
|
如果可能的话,清空屏幕 |
|
比较或输出 terminfo 描述 |
|
将 terminfo 描述转化为 termcap 描述 |
|
提供 ncurses 的配置信息 |
|
以终端默认值重新初始化终端 |
|
清除并设置终端的 tab 宽度 |
|
Terminfo 条目描述编译器,将 terminfo 文件从源代码格式翻译为 ncurses 库子程序需要的二进制格式 [terminfo 文件包含特定终端的功能信息。] |
|
列出所有可用的终端类型,并给出每种类型的主要名称和描述 |
|
使 shell 可以使用终端相关的功能;也可以重置或初始化终端, 或者报告它的长名称 |
|
可以被用于初始化终端 |
|
指向 |
|
包含在终端屏幕上以多种复杂方式显示文本的函数; 使用这些函数的典型例子是运行内核的 make menuconfig 时显示的目录。 |
|
包含实现表单的函数 |
|
包含实现目录的函数 |
|
包含实现面板的函数 |
Attr 软件包包含管理文件系统对象扩展属性的工具。
准备安装 Attr:
./configure --prefix=/usr \ --bindir=/bin \ --disable-static \ --sysconfdir=/etc \ --docdir=/usr/share/doc/attr-2.4.48
编译该软件包:
make
测试套件必须在支持扩展属性的文件系统,如 ext2、ext3 或 ext4 上运行。运行下列命令以测试编译结果:
make check
安装该软件包:
make install
需要将共享库移动到 /lib
目录,因此
/usr/lib
中的 .so
符号链接也需要重新建立:
mv -v /usr/lib/libattr.so.* /lib ln -sfv ../../lib/$(readlink /usr/lib/libattr.so) /usr/lib/libattr.so
Acl 软件包包含管理访问控制列表的工具, 访问控制列表能够更细致地自由定义文件和目录的访问权限。
准备安装 Acl:
./configure --prefix=/usr \ --bindir=/bin \ --disable-static \ --libexecdir=/usr/lib \ --docdir=/usr/share/doc/acl-2.2.53
编译该软件包:
make
Acl 的测试套件必须在构建了链接到 Acl 库的 Coreutils 后才能在支持访问控制的文件系统上运行。 如果想运行它们,在构建好 Coreutils 后再返回这里,并执行 make check。
安装该软件包:
make install
共享库需要被移动到 /lib
目录,因此
/usr/lib
中的 .so
符号链接需要重新建立:
mv -v /usr/lib/libacl.so.* /lib ln -sfv ../../lib/$(readlink /usr/lib/libacl.so) /usr/lib/libacl.so
Libcap 软件包为 Linux 内核提供的 POSIX 1003.1e 权能字实现用户接口。这些权能字是 root 用户的最高特权分割成的一组不同权限。
防止静态库的安装:
sed -i '/install.*STALIBNAME/d' libcap/Makefile
编译该软件包:
make
该软件包不包含测试套件。
安装该软件包:
make RAISE_SETFCAP=no lib=lib prefix=/usr install chmod -v 755 /usr/lib/libcap.so.2.27
make 命令选项的含义:
RAISE_SETFCAP=no
该参数跳过对 setcap 使用它本身的步骤。 这可以避免在不支持扩展权能字的内核或文件系统上发生安装错误。
lib=lib
在 x86_64 上,该参数将库安装在 $prefix/lib
,而不是 $prefix/lib64
。 它在 x86 上没有作用。
需要把共享库移动到 /lib
,因此 /usr/lib
中的 .so
符号链接需要重新建立:
mv -v /usr/lib/libcap.so.* /lib ln -sfv ../../lib/$(readlink /usr/lib/libcap.so) /usr/lib/libcap.so
Sed 软件包包含一个流编辑器。
首先修复 LFS 环境中的一个问题,并删除一个失败的测试:
sed -i 's/usr/tools/' build-aux/help2man sed -i 's/testsuite.panic-tests.sh//' Makefile.in
准备安装 Sed:
./configure --prefix=/usr --bindir=/bin
编译该软件包,并生成 HTML 文档:
make make html
运行以下命令以测试编译结果:
make check
安装该软件包及其文档:
make install install -d -m755 /usr/share/doc/sed-4.7 install -m644 doc/sed.html /usr/share/doc/sed-4.7
Psmisc 软件包包含显示正在运行的进程信息的程序。
准备安装 Psmisc:
./configure --prefix=/usr
编译该软件包:
make
该软件包不包含测试套件。
安装该软件包:
make install
最后,移动 killall 和 fuser 到 FHS 指定的位置:
mv -v /usr/bin/fuser /bin mv -v /usr/bin/killall /bin
Iana-Etc 软件包包含网络服务和协议的数据。
以下命令将 IANA 提供的原始数据转化为供 /etc/protocols
和 /etc/services
使用的正确格式:
make
该软件包不包含测试套件。
安装该软件包:
make install
Bison 软件包包含语法分析器生成器。
首先,解决当前版本构建系统中的一个问题:
sed -i '6855 s/mv/cp/' Makefile.in
准备编译 Bison:
./configure --prefix=/usr --docdir=/usr/share/doc/bison-3.4.1
编译该软件包,但不能使用并行编译, 以躲避当前版本构建系统中的一个竞争条件:
make -j1
在考虑测试的情况下, bison 和 flex 之间存在循环依赖。 如果希望的话,可以在下一节安装 flex 后,重新构建 bison 软件包, 再执行 make check 运行测试。
安装该软件包:
make install
Flex 软件包包含一个工具,用于生成在文本中识别模式的程序。
首先修正一个 glibc-2.26 引入的问题:
sed -i "/math.h/a #include <malloc.h>" src/flexdef.h
构建过程假设 help2man 程序存在,以根据可执行文件的 --help 选项创建一个 man 页面。 然而它并不存在,因此我们使用一个环境变量,跳过这一过程。 现在,准备编译 Flex:
HELP2MAN=/tools/bin/true \ ./configure --prefix=/usr --docdir=/usr/share/doc/flex-2.6.4
编译该软件包:
make
如果要测试编译结果(需要约 0.5 SBU), 执行:
make check
安装该软件包:
make install
个别程序还不知道 flex ,
并试图去运行它的前身 lex。为了支持这些程序, 创建一个名为
lex
的符号链接,它以 lex 仿真模式运行 flex
:
ln -sv flex /usr/bin/lex
Grep 软件包包含在文件中进行搜索的程序。
准备安装 Grep:
./configure --prefix=/usr --bindir=/bin
编译该软件包:
make
运行以下命令以测试编译结果:
make -k check
安装该软件包:
make install
Bash 软件包包含 Bourne-Again SHell。
准备安装 Bash:
./configure --prefix=/usr \ --docdir=/usr/share/doc/bash-5.0 \ --without-bash-malloc \ --with-installed-readline
配置选项的含义:
--with-installed-readline
该选项告诉 Bash 使用系统中已经安装的 readline
库, 而不是它自己的 readline 版本。
编译该软件包:
make
如果不运行测试套件,跳到 “安装该软件包”。
为了准备进行测试,确保 nobody
用户可以写入源代码目录:
chown -Rv nobody .
现在以 nobody
用户的身份运行测试:
su nobody -s /bin/bash -c "PATH=$PATH HOME=/home make tests"
安装该软件包,并把主要的可执行文件移动到 /bin
:
make install mv -vf /usr/bin/bash /bin
执行新编译的 bash 程序 (替换当前正在执行的版本):
exec /bin/bash --login +h
上面使用的参数使得 bash 进程是一个可交互的登录 shell ,并且仍然禁用散列功能, 这样新程序一旦可用就会被找到。
Libtool 软件包包含 GNU 通用库支持脚本。 它在一个一致、可移植的接口下隐藏了使用共享库的复杂性。
准备安装 Libtool:
./configure --prefix=/usr
编译该软件包:
make
为了测试编译结果,执行:
make check
在多核系统上,可以显著减少 libtool 的测试时间。 为了使用多个核心,在上述命令中附加 TESTSUITEFLAGS=-j<N> 参数。 例如,使用 -j4 可以减少超过 60% 的测试时间。
在 LFS 构建环境中,已知有五个测试因为循环依赖而失败。 然而在 automake 安装后,所有测试都能通过。
安装该软件包:
make install
GDBM 软件包包含 GNU 数据库管理器。 它是一个使用可扩展散列的数据库函数库,工作方法和标准 UNIX dbm 类似。 该库提供用于存储键值对、通过键搜索和获取数据, 以及删除键和对应数据的原语。
准备安装 GDBM:
./configure --prefix=/usr \ --disable-static \ --enable-libgdbm-compat
配置选项的含义:
--enable-libgdbm-compat
该选项启用 libgdbm 兼容性库的构建, 因为 LFS 之外的一些软件包需要它提供的老式 DBM 子程序。
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
Gperf 根据一组键值,生成完美散列函数。
准备安装 Gperf:
./configure --prefix=/usr --docdir=/usr/share/doc/gperf-3.1
编译该软件包:
make
已知同时执行多个测试(-j 选项的值大于 1)会导致测试失败。 执行以下命令测试编译结果:
make -j1 check
安装该软件包:
make install
Expat 软件包包含用于解析 XML 文件的面向流的 C 语言库。
首先修正 LFS 环境中退化测试的一个问题:
sed -i 's|usr/bin/env |bin/|' run.sh.in
准备安装 Expat:
./configure --prefix=/usr \ --disable-static \ --docdir=/usr/share/doc/expat-2.2.7
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
如果需要,安装该软件包的文档:
install -v -m644 doc/*.{html,png,css} /usr/share/doc/expat-2.2.7
Inetutils 软件包包含基本网络程序。
准备安装 Inetutils:
./configure --prefix=/usr \ --localstatedir=/var \ --disable-logger \ --disable-whois \ --disable-rcp \ --disable-rexec \ --disable-rlogin \ --disable-rsh \ --disable-servers
配置选项的含义:
--disable-logger
该选项防止 Inetutils 安装 logger 程序, 它被脚本文件用于向系统日志守护程序传递消息。这里不安装它, 因为 Util-linux 会安装更新的版本。
--disable-whois
该选项防止构建过时的 whois 客户端, BLFS 手册中有一个更好的 whois 客户端。
--disable-r*
这些参数禁用过时的程序,由于安全问题,它们不应该被继续使用。 它们提供的功能可以由 BLFS 手册中的 openssh 软件包代替。
--disable-servers
该选项禁用 Inetutils 软件包包含的若干网络服务程序, 它们在基本的 LFS 系统中注定是不合适的。 其中一些服务程序从本质上就不安全, 只有在可信的网络环境中才能被认为是安全的。 要注意的是,对于其中许多服务程序,都能找到更好的替代品。
编译该软件包:
make
运行以下命令以测试编译结果:
make check
其中一项测试 libls.sh 在初始的 chroot 环境中可能失败, 但在 LFS 系统构建完成后再重新运行时即可通过。另外,一项名为 ping-localhost.sh 的测试在宿主系统不支持 ipv6 时会失败。
安装该软件包:
make install
移动一些程序,这样在 /usr
文件系统不可用时也能使用它们:
mv -v /usr/bin/{hostname,ping,ping6,traceroute} /bin mv -v /usr/bin/ifconfig /sbin
Perl 软件包包含实用报表提取语言。
首先创建一个基本的 /etc/hosts
文件, Perl
的一个配置文件和可选的测试套件都会引用它:
echo "127.0.0.1 localhost $(hostname)" > /etc/hosts
该版本的 Perl 会构建 Compress::Raw::ZLib 和 Compress::Raw::BZip2 模块。默认情况下 Perl 会使用内部的 zlib 和 bzip2 源码副本构建它们, 执行以下命令,使得 Perl 使用系统中已经安装好的库:
export BUILD_ZLIB=False export BUILD_BZIP2=0
为了能够完全控制 Perl 的设置,您可以在以下命令中移除 “-des” 选项,并手动选择构建该软件包的方式。 或者,直接使用下面的命令,以使用 Perl 自动检测的默认值:
sh Configure -des -Dprefix=/usr \ -Dvendorprefix=/usr \ -Dman1dir=/usr/share/man/man1 \ -Dman3dir=/usr/share/man/man3 \ -Dpager="/usr/bin/less -isR" \ -Duseshrplib \ -Dusethreads
配置选项的含义
-Dvendorprefix=/usr
这保证 perl 知道如何告知软件包应该在哪里安装它们的 perl 模块。
-Dpager="/usr/bin/less
-isR"
这保证该软件包使用less
对输出进行分页,而不是使用
more
。
-Dman1dir=/usr/share/man/man1
-Dman3dir=/usr/share/man/man3
由于 Groff 还没有安装,Configure 认为我们不需要 Perl 的 man 页面。这些参数覆盖这个判断。
-Duseshrplib
构建 libperl 共享库,一些 perl 模块需要它。
-Dusethreads
构建带有线程支持的 perl。
编译该软件包:
make
为了测试编译结果 (需要约 11 SBU),执行以下命令:
make -k test
由于使用了最新的 gdbm 版本,一项测试会失败。
安装该软件包,并清理环境变量:
make install unset BUILD_ZLIB BUILD_BZIP2
Module::CoreList 的命令行前端 |
|
通过命令行与综合 Perl 归档网络 (CPAN) 交互 |
|
从 Unicode 字符映射或 Tcl 编码文件构建 Encode 模块使用的 Perl 扩展 |
|
猜测一些文件的编码格式 |
|
将 |
|
将 |
|
用于检验安装好的 Perl 模块的 shell 脚本, 可以从安装好的模块创建压缩包 |
|
在特定输入输出格式之间转化数据 |
|
可以被用于配置 |
|
由 C 语言、sed、 awk 和 sh 的最好特性结合成的一门瑞士军刀式语言 |
|
指向 perl 的硬链接 |
|
用于创建关于 Perl 或者它附带的模块的 bug 报告, 并用邮件发送它们 |
|
显示集成在 Perl 安装目录树或某个 Perl 脚本中的一页 pod 格式文档 |
|
Perl 安装检验程序;它可以被用于确认 Perl 和它的库都安装正确 |
|
用于生成发送给 Perl 开发者的感谢信 |
|
字符编码转换器 iconv 的 Perl 版本 |
|
一个用于将 Perl4 |
|
将 pod 格式的文件转换为 HTML 格式 |
|
将 pod 数据转换为格式化的 *roff 输入 |
|
将 pod 数据转化为格式化的 ASCII 文本 |
|
输出文件中嵌入的 pod 文档中的使用方法信息 |
|
检查 pod 格式文档文件的语法 |
|
显示 pod 文档中的指定章节 |
|
用于运行使用 Test::Harness 模块的测试 |
|
一个 Perl 编写的类似 tar 的程序 |
|
一个比较压缩档案和未压缩版本的 Perl 程序 |
|
一个在 tar 档案中的文件内容上进行模式匹配的 Perl 程序 |
|
打印或检查 SHA 校验和 |
|
被用于 Perl 的强制性详细警告诊断 |
|
将 Perl XS 代码转换为 C 代码 |
|
显示 Zip 文件内部结构的详细信息 |
XML::Parser 模块是 James Clark 的 XML 解析器 Expat 的 Perl 接口。
准备安装 XML::Parser:
perl Makefile.PL
编译该软件包:
make
运行以下命令以测试编译结果:
make test
安装该软件包:
make install
Intltool 是一个从源代码文件中提取可翻译字符串的国际化工具。
首先修复由 perl-5.22 及更新版本导致的警告:
sed -i 's:\\\${:\\\$\\{:' intltool-update.in
准备安装 Intltool:
./configure --prefix=/usr
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install install -v -Dm644 doc/I18N-HOWTO /usr/share/doc/intltool-0.51.0/I18N-HOWTO
Autoconf 软件包包含生成能自动配置软件包的 shell 脚本的程序。
首先,修复 Perl 5.28 引入的 bug:
sed '361 s/{/\\{/' -i bin/autoscan.in
准备编译 Autoconf:
./configure --prefix=/usr
编译该软件包:
make
目前由于 bash-5 和 libtool-2.4.3 的变化,测试套件无法正常工作。 如果无论如何要运行测试,执行命令:
make check
安装该软件包:
make install
产生自动配置软件源码包,使其适用于多种类 Unix 系统的 shell 脚本;它产生的脚本可以独立运行 —— 运行它们不需要 autoconf 程序。 |
|
一个创建 C #define 预处理指令的模板, 以供配置脚本使用的程序 |
|
M4 宏处理器的封装器 |
|
在 autoconf 和 automake 模板文件发生变化时, 按照正确顺序自动运行 autoconf、 autoheader、aclocal、 automake、gettextize, 以及 libtoolize,以便节省时间。 |
|
帮助用户为软件包创建 |
|
修改仍然使用 autoconf
宏的旧名称调用它们的 |
|
帮助用户为软件包编写 |
Automake 软件包包含自动生成 Makefile,以便和 Autoconf 一同使用的程序。
准备安装 Automake:
./configure --prefix=/usr --docdir=/usr/share/doc/automake-1.16.1
编译该软件包:
make
由于单个测试点中存在的内部时延,即使仅有一个处理器, 也应该使用 make 命令的 -j4 选项加速测试。输入以下命令测试编译结果:
make -j4 check
已知在 LFS 环境中,一项名为 subobj.sh 的测试可能失败。
安装该软件包:
make install
Xz 软件包包含文件压缩和解压缩工具,它能够处理 lzma 和新的 xz 压缩文件格式。使用 xz 压缩文本文件, 可以得到比传统的 gzip 或 bzip2 更好的压缩比。
准备编译 Xz:
./configure --prefix=/usr \ --disable-static \ --docdir=/usr/share/doc/xz-5.2.4
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包,并保证所有重要文件都位于正确的目录中:
make install mv -v /usr/bin/{lzma,unlzma,lzcat,xz,unxz,xzcat} /bin mv -v /usr/lib/liblzma.so.* /lib ln -svf ../../lib/$(readlink /usr/lib/liblzma.so) /usr/lib/liblzma.so
解压到标准输出 |
|
在 LZMA 压缩文件上执行 cmp |
|
在 LZMA 压缩文件上执行 diff |
|
在 LZMA 压缩文件上执行 egrep |
|
在 LZMA 压缩文件上执行 fgrep |
|
在 LZMA 压缩文件上执行 grep |
|
在 LZMA 压缩文件上执行 less |
|
使用 LZMA 格式压缩或解压缩文件 |
|
一个轻量的 LZMA 压缩文件快速解码器 |
|
显示 LZMA 压缩文件头中存储的信息 |
|
在 LZMA 压缩文件上执行 more |
|
使用 LZMA 格式解压缩文件 |
|
使用 XZ 格式解压缩文件 |
|
使用 XZ 格式压缩或解压缩文件 |
|
解压到标准输出 |
|
在 XZ 压缩文件上执行 cmp |
|
一个轻量的 XZ 压缩文件解码器 |
|
在 XZ 压缩文件上执行 diff |
|
在 XZ 压缩文件上执行 egrep |
|
在 XZ 压缩文件上执行 fgrep |
|
在 XZ 压缩文件上执行 grep |
|
在 XZ 压缩文件上执行 less |
|
在 XZ 压缩文件上执行 more |
|
实现基于 Lempel-Zip-Markov 链的无损块排序数据压缩算法的库 |
Kmod 软件包包含用于加载内核模块的库和工具。
准备安装 Kmod:
./configure --prefix=/usr \ --bindir=/bin \ --sysconfdir=/etc \ --with-rootlibdir=/lib \ --with-xz \ --with-zlib
配置选项的含义:
--with-xz,
--with-zlib
它们允许 Kmod 处理压缩过的内核模块。
--with-rootlibdir=/lib
该选项保证一些和库有关的文件被放置在正确的目录中。
编译该软件包:
make
该软件包不包含能在 LFS chroot 环境下运行的测试套件。 测试套件至少需要 git 程序的支持,且有些测试在 git 仓库外不会运行。
安装该软件包,并创建与 Module-Init-Tools (曾经用于处理 Linux 内核模块的软件包)兼容的符号链接:
make install for target in depmod insmod lsmod modinfo modprobe rmmod; do ln -sfv ../bin/kmod /sbin/$target done ln -sfv kmod /bin/lsmod
Gettext 软件包包含国际化和本地化工具, 它们允许程序在编译时加入 NLS (母语支持)功能, 使它们能够以用户的母语输出消息。
准备编译 Gettext:
./configure --prefix=/usr \ --disable-static \ --docdir=/usr/share/doc/gettext-0.20.1
编译该软件包:
make
输入以下命令以测试编译结果(需要较长时间, 大约 3 SBU):
make check
安装该软件包:
make install chmod -v 0755 /usr/lib/preloadable_libintl.so
将标准 Gettext 微架构文件复制到源代码包 |
|
替换 shell 格式化字符串中的环境变量 |
|
通过查询消息目录中的翻译,将中性语言消息翻译成用户的语言 |
|
主要用于 gettext 的 shell 函数库 |
|
将所有标准 Gettext 文件复制到软件包顶层目录中, 以开始国际化该软件包 |
|
根据属性过滤翻译目录中的消息,或修改这些属性 |
|
连接并合并给定的 |
|
比较两个 |
|
找出给定的多个 |
|
将翻译目录转换成另一种字符编码 |
|
创建英文翻译目录 |
|
对翻译目录中的所有翻译执行命令 |
|
对翻译目录中的所有翻译应用过滤器 |
|
根据翻译目录创建二进制消息目录 |
|
找出翻译目录中所有匹配给定模式, 或属于给定源代码文件的消息 |
|
创建一个新的 |
|
将两个原始翻译文件组合成一个文件 |
|
反编译二进制消息目录,生成原始翻译文本 |
|
去除翻译目录中重复的翻译 |
|
显示某条语法形式依赖于数字的文本消息的母语翻译 |
|
将塞尔维亚语文本从西里尔字符转换为拉丁字符 |
|
从给定源代码文件中提取可翻译消息,以生成最初的翻译模板 |
|
定义 autosprintf 类,使得 C 格式化输出子程序在 C++ 程序中可用,能够与 <string> 字符串和 <iostream> 流一起使用 |
|
一个内部库,包含若干 Gettext 程序的公共子程序; 不建议普遍使用 |
|
用于编写处理 |
|
一个内部库,包含若干 Gettext 程序使用的公共子程序; 没有设计为普遍使用 |
|
一个被设计为由 LD_PRELOAD 预加载的库,帮助 |
Libelf 是一个处理 ELF (可执行和可链接格式)文件的库。
Libelf 是 elfutils-0.177 软件包的一部分, 使用 elfutils-0.177.tar.bz2 作为源代码包。
准备编译 Libelf:
./configure --prefix=/usr
编译该软件包:
make
执行下列命令以测试编译结果:
make check
只安装 Libelf:
make -C libelf install install -vm644 config/libelf.pc /usr/lib/pkgconfig
Libffi 库提供一个可移植的高级编程接口,用于处理不同调用惯例。 这允许程序在运行时调用任何给定了调用接口的函数。
和 GMP 类似,libffi 在构建时会使用特定于当前处理器的优化。 如果是在为另一台计算机构建系统,请导出 CFLAGS 和 CXXFLAGS 环境变量, 为您的架构指定较为通用的构建目标。否则,所有链接到 libffi 的程序都可能触发非法指令异常。
修改 Makefile ,将头文件安装到标准的 /usr/include
目录,而不是 /usr/lib/libffi-3.2.1/include
。
sed -e '/^includesdir/ s/$(libdir).*$/$(includedir)/' \ -i include/Makefile.in sed -e '/^includedir/ s/=.*$/=@includedir@/' \ -e 's/^Cflags: -I${includedir}/Cflags:/' \ -i libffi.pc.in
准备安装 libffi:
./configure --prefix=/usr --disable-static --with-gcc-arch=native
The meaning of the configure option:
--with-gcc-arch=native
保证 gcc 为当前系统进行优化。如果不使用该选项, 构建系统会猜测系统架构,在某些系统上可能生成不正确的代码。 如果要将生成的代码从本地系统复制到指令集功能较弱的系统中, 需要使用目标系统架构作为该选项的参数值,参阅 gcc 手册中提供的的 x86 选项。
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
OpenSSL 软件包包含密码学相关的管理工具和库。 它们被用于向其他软件包提供密码学功能,例如 OpenSSH, 电子邮件程序和 Web 浏览器(访问 HTTPS 站点)。
首先,修复上游发现的一个问题:
sed -i '/\} data/s/ =.*$/;\n memset(\&data, 0, sizeof(data));/' \ crypto/rand/rand_lib.c
准备安装 OpenSSL:
./config --prefix=/usr \ --openssldir=/etc/ssl \ --libdir=lib \ shared \ zlib-dynamic
编译该软件包:
make
运行以下命令以测试编译结果:
make test
安装该软件包:
sed -i '/INSTALL_LIBS/s/libcrypto.a libssl.a//' Makefile make MANSUFFIX=ssl install
如果需要的话,安装文档:
mv -v /usr/share/doc/openssl /usr/share/doc/openssl-1.1.1c cp -vfr doc/* /usr/share/doc/openssl-1.1.1c
Python 3 软件包包含 Python 开发环境。它被用于面向对象编程、 编写脚本、为大型程序建立原型或开发完整的应用。
准备安装 Python:
./configure --prefix=/usr \ --enable-shared \ --with-system-expat \ --with-system-ffi \ --with-ensurepip=yes
配置选项的含义:
--with-system-expat
该选项允许链接到系统已经安装的 Expat。
--with-system-ffi
该选项允许链接到系统已经安装的 libffi。
--with-ensurepip=yes
该选项启用 pip 和 setuptools 包管理程序的构建。
编译该软件包:
make
测试套件需要 Tk 和 X 窗口会话的支持, 必须在 BLFS 中重新安装 Python 3 后才能运行。
安装该软件包:
make install chmod -v 755 /usr/lib/libpython3.7m.so chmod -v 755 /usr/lib/libpython3.so ln -sfv pip3.7 /usr/bin/pip3
安装命令的含义:
修正库文件访问权限,使之和其他库文件一致。
如果需要的话,安装预先格式化的文档:
install -v -dm755 /usr/share/doc/python-3.7.4/html tar --strip-components=1 \ --no-same-owner \ --no-same-permissions \ -C /usr/share/doc/python-3.7.4/html \ -xvf ../python-3.7.4-docs-html.tar.bz2
文档安装命令的含义:
--no-same-owner
和 --no-same-permissions
保证安装的文件拥有正确的所有者和权限码。 在没有这些选项的时候,tar 会以上游开发者使用的用户和权限码安装文件。
Ninja 是一个注重速度的小型构建系统。
在运行时, ninja 一般尽量并行运行更多进程, 默认情况下最大进程数是系统 CPU 核心数加 2 得到的值。 某些情况下,这样会导致 CPU 过热,或者耗尽系统内存。 如果使用命令行执行 ninja,可以传递 -jN 参数以限制并行进程数, 但某些软件包内嵌了 ninja 的执行过程,且并不传递 -j 参数。
应用下面这个 可选的 修改, 用户即可通过一个环境变量 NINJAJOBS 限制并行进程数量。 例如 设置:
export NINJAJOBS=4
会限制 ninja 使用 4 个并行进程。
如果您希望 Ninja 能够使用环境变量 NINJAJOBS,执行以下命令, 添加这一功能:
sed -i '/int Guess/a \ int j = 0;\ char* jobs = getenv( "NINJAJOBS" );\ if ( jobs != NULL ) j = atoi( jobs );\ if ( j > 0 ) return j;\ ' src/ninja.cc
构建 Ninja:
python3 configure.py --bootstrap
构建选项的含义:
--bootstrap
这个参数强制 ninja 为当前系统重新构建自身。
运行以下命令以测试编译结果:
python3 configure.py ./ninja ninja_test ./ninja_test --gtest_filter=-SubprocessTest.SetWithLots
安装该软件包:
install -vm755 ninja /usr/bin/ install -vDm644 misc/bash-completion /usr/share/bash-completion/completions/ninja install -vDm644 misc/zsh-completion /usr/share/zsh/site-functions/_ninja
Meson 是一个开放源代码构建系统,它的设计保证了非常快的执行速度, 同时更注重用户友好性。
执行以下命令编译 Meson:
python3 setup.py build
该软件包不包含测试套件。
安装该软件包:
python3 setup.py install --root=dest cp -rv dest/* /
安装选项的含义:
--root=dest
默认情况下 python3 setup.py install 将若干文件(如 man 页面)安装到 Python Eggs 中。 在指定了根目录位置时,setup.py 将这些文件安装到符合标准的目录树中。 我们即可直接复制该目录树,使得这些文件位于标准指定的位置。
Coreutils 软件包包含用于显示和设定系统基本属性的工具。
POSIX 要求 Coreutils 中的程序即使在多字节 locale 中也能正确识别字符边界。下面应用一个补丁, 以解决 Coreutils 不满足这项 POSIX 要求的问题, 并修复其他一些国际化相关的 bug:
patch -Np1 -i ../coreutils-8.31-i18n-1.patch
在之前,这个补丁中找出了许多 bug。在向 Coreutils 维护者报告新 bug 前,请检查它们在不使用该补丁的情况下是否还会重现。
阻止一个在某些机器上会无限循环的测试:
sed -i '/test.lock/s/^/#/' gnulib-tests/gnulib.mk
现在准备编译 Coreutils:
autoreconf -fiv FORCE_UNSAFE_CONFIGURE=1 ./configure \ --prefix=/usr \ --enable-no-install-program=kill,uptime
配置选项的含义:
该命令重新生成配置文件,使之与 automake 的最新版本一致。
FORCE_UNSAFE_CONFIGURE=1
该环境变量允许以 root 用户身份构建该软件包。
--enable-no-install-program=kill,uptime
这个开关的目的是防止 Coreutils 安装那些被其他软件包安装的二进制程序。
编译该软件包:
make
如果不运行测试套件, 直接跳到 “安装该软件包” 。
现在测试套件已经可以运行了。首先运行那些设计为由 root
用户运行的测试:
make NON_ROOT_USERNAME=nobody check-root
之后我们要以 nobody
用户身份运行其余测试。然而,某些测试要求测试用户属于至少一个组。 为了不跳过这些测试,我们添加一个临时组,并使得
nobody
用户成为它的成员:
echo "dummy:x:1000:nobody" >> /etc/group
修正访问权限,使得非 root 用户可以编译和运行测试:
chown -Rv nobody .
执行以下命令,确保 su
环境的 PATH 变量包含 /tools/bin 并运行测试:
su nobody -s /bin/bash \ -c "PATH=$PATH make RUN_EXPENSIVE_TESTS=yes check"
已知测试程序 test-getlogin 在部分构建的系统环境(如这里的 chroot 环境)会失败,但是在本章的最后再运行即可通过。 另外已知测试程序 tty.sh 可能会失败。
删除临时组:
sed -i '/dummy/d' /etc/group
安装该软件包:
make install
将程序移动到 FHS 要求的位置:
mv -v /usr/bin/{cat,chgrp,chmod,chown,cp,date,dd,df,echo} /bin mv -v /usr/bin/{false,ln,ls,mkdir,mknod,mv,pwd,rm} /bin mv -v /usr/bin/{rmdir,stty,sync,true,uname} /bin mv -v /usr/bin/chroot /usr/sbin mv -v /usr/share/man/man1/chroot.1 /usr/share/man/man8/chroot.8 sed -i s/\"1\"/\"8\"/1 /usr/share/man/man8/chroot.8
LFS-Bootscripts 包中的部分脚本可能依赖于 head, nice, sleep, 以及 touch。由于 /usr
在系统引导的较早阶段可能不可用, 需要将这些程序移动到根分区,以保证 FHS
兼容性:
mv -v /usr/bin/{head,nice,sleep,touch} /bin
根据 base32 标准(RFC 4648)编码和解码数据 |
|
根据 base64 标准(RFC 4648)编码和解码数据 |
|
打印或检查 BLAKE2 (512 位) 校验和 |
|
从文件名移除所有路径和一个给定后缀 |
|
使用一些算法编码或解码数据 |
|
将文件合并到标准输出 |
|
修改文件和目录的 SELinux 安全上下文 |
|
修改文件和目录所属的组 |
|
修改给定文件的访问权限为指定模式; 模式可以是所需修改的符号表示,或新权限的八进制码 |
|
修改拥有文件的用户或组 |
|
将给定目录作为 |
|
输出每个给定文件的循环冗余检查(CRC) 校验和及字节数 |
|
比较两个排好序的文件, 将两个文件特有的部分和它们共有的部分显示为三列 |
|
复制文件 |
|
将给定文件分割为若干新文件,根据给定模式或行号进行分割, 并输出每个新文件的字节数 |
|
根据给定的域或位置,打印输入的分节和选定部分 |
|
以给定格式显示当前时间,或设定系统时间 |
|
以给定块大小和个数复制文件,同时可以进行转换 |
|
报告每个已挂载文件系统(或包含给定文件的文件系统) 的总大小和可用空间 |
|
列出给定目录的内容 (和 ls 命令相同) |
|
输出用于设定 |
|
从文件名中删掉非目录的后缀 |
|
报告当前目录使用的磁盘空间, 给出当前目录下所有子目录和文件占用的空间 |
|
显示给定字符串 |
|
在修改的环境中运行命令 |
|
将制表符转换成空格 |
|
计算表达式 |
|
打印所有给定整数的质因数 |
|
什么也不做;总是以失败状态码退出; |
|
重新格式化给定文件的段落 |
|
折叠给定文件中的行 |
|
报告用户所属的组 |
|
打印文件的前 10 (或给定行数)行 |
|
以十六进制格式打印主机数字标识符 |
|
报告当前用户或给定用户的有效用户 ID、组 ID 和所属的组 |
|
复制文件并设定它们的访问权限,以及(如果可能) 它们的所有者和属组 |
|
将两个文件中拥有相同域的行合并 |
|
以给定文件名创建硬链接 |
|
在文件之间创建硬链接或软(符号)链接 |
|
报告当前用户登录名 |
|
列出给定目录内容 |
|
报告或检查消息摘要 5 (MD5)校验和 |
|
以给定名称创建目录 |
|
以给定名称创建先进先出表(FIFO), 在 UNIX 惯用语中又称为 “命名管道” |
|
以给定名称创建设备节点;设备节点可能是字符特殊文件、 块特殊文件或 FIFO |
|
安全地创建临时文件,常用在脚本中 |
|
移动或重命名文件或目录 |
|
以修改的调度优先级运行程序 |
|
标出给定文件的行 |
|
以免疫挂机信号的方式执行命令,同时将输出重定向到日志文件 |
|
打印进程可用的处理单元数目 |
|
在数字和人类可读字符串之间互相转换 |
|
以八进制或其他格式转储文件 |
|
合并给定文件,将它们的对应行连接起来,以制表符分割 |
|
检查文件名的有效性和可移植性 |
|
是轻量级 finger 客户端,报告给定用户的一些信息 |
|
对文件进行分页和分栏以便打印 |
|
打印环境变量 |
|
以给定格式打印给定参数,很像 C printf 函数 |
|
用文中的每个关键字,根据给定文件内容生成重排索引 |
|
报告当前工作目录名 |
|
报告给定符号链接的值 |
|
打印解析过的目录 |
|
删除文件或目录 |
|
如果目录是空的,删除它们 |
|
以给定 SELinux 安全上下文运行命令 |
|
以给定的范围和增量打印等差数列 |
|
打印或检查 160 位安全散列算法 1 (SHA1)校验和 |
|
打印或检查 224 位安全散列算法校验和 |
|
打印或检查 256 位安全散列算法校验和 |
|
打印或检查 384 位安全散列算法校验和 |
|
打印或检查 512 位安全散列算法校验和 |
|
将给定文件多次用复杂模式覆盖,增加恢复数据的难度 |
|
打乱文件中的行 |
|
等待给定时间 |
|
对给定文件的行进行排序 |
|
根据大小或行数,将指定文件分割成若干部分 |
|
显示文件或文件系统状态 |
|
以修改的标准流缓冲操作运行命令 |
|
设置或报告终端行设定 |
|
打印每个指定文件的校验和及块个数 |
|
刷新文件系统缓冲;它将修改过的块强制写入磁盘, 并更新超级块 |
|
逆序连接给定文件 |
|
输出给定文件的最后 10 (或指定行数)行 |
|
读取标准输入,并将内容同时写入标准输出和给定文件 |
|
比较两个值,或检查文件类型 |
|
在限定时间内运行命令 |
|
修改文件时间戳,将每个给定文件的访问和修改时间设为当前时间; 以零长度创建当前不存在的文件 |
|
从标准输入变换、压缩或删除给定字符 |
|
什么也不做;总是以成功状态码退出 |
|
将文件截断或扩展到指定大小 |
|
进行拓扑排序; 根据给定文件的部分顺序信息输出完整的排序列表 |
|
报告标准输入的终端文件名 |
|
报告系统信息 |
|
将空格转换成制表符 |
|
在连续的相同行中只保留一行,删除其他所有行 |
|
删除给定文件 |
|
报告当前登录系统的用户名 |
|
和 ls -l 相同 |
|
报告给定文件的行数、单词数和字节数 |
|
报告当前登录的用户 |
|
报告与当前有效用户 ID 相关的用户名 |
|
不停输出 “y” 或给定字符串,直到被杀死 |
|
stdbuf 使用的库 |
Check 是一个 C 语言单元测试框架。
准备安装 Check:
./configure --prefix=/usr
构建该软件包:
make
现在已经编译完成,执行以下命令执行 Check 测试套件:
make check
注意 Check 测试套件可能需要相对较长(高达 4 SBU)的时间。
安装该软件包,并修复一个脚本:
make docdir=/usr/share/doc/check-0.12.0 install sed -i '1 s/tools/usr/' /usr/bin/checkmk
Diffutils 软件包包含显示文件或目录之间差异的程序。
准备安装 Diffutils:
./configure --prefix=/usr
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
Gawk 软件包包含操作文本文件的程序。
首先,确保不安装某些不需要的文件:
sed -i 's/extras//' Makefile.in
准备安装 Gawk:
./configure --prefix=/usr
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
如果需要的话,安装文档:
mkdir -v /usr/share/doc/gawk-5.0.1 cp -v doc/{awkforai.txt,*.{eps,pdf,jpg}} /usr/share/doc/gawk-5.0.1
Findutils 软件包包含用于查找文件的程序。 这些程序能够递归地搜索目录树,以及创建、维护和搜索数据库 (一般比递归搜索快,但在数据库最近没有更新时不可靠)。
首先,禁用一个在某些机器上会无限循环的测试:
sed -i 's/test-lock..EXEEXT.//' tests/Makefile.in
然后,进行 glibc-2.28 和更高版本要求的一些修补:
sed -i 's/IO_ftrylockfile/IO_EOF_SEEN/' gl/lib/*.c sed -i '/unistd/a #include <sys/sysmacros.h>' gl/lib/mountlist.c echo "#define _IO_IN_BACKUP 0x100" >> gl/lib/stdio-impl.h
准备安装 Findutils:
./configure --prefix=/usr --localstatedir=/var/lib/locate
配置选项的含义:
--localstatedir
该选项将 locate 数据库的位置改为
/var/lib/locate
, 以与 FHS
兼容。
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
LFS-Bootscripts 软件包中的某些脚本依赖于 find。由于 /usr
在引导早期阶段可能不可用,需要将这个程序移动到根分区。
updatedb
脚本也需要进行修改, 以修正一个显式指定的路径:
mv -v /usr/bin/find /bin sed -i 's|find:=${BINDIR}|find:=/bin|' /usr/bin/updatedb
Groff 软件包包含处理和格式化文本的程序。
Groff 期望环境变量 PAGE
包含默认纸张大小。
对于美国用户来说,PAGE=letter
是正确的。 对于其他地方的用户,PAGE=A4
可能更好。
尽管在编译时配置了默认纸张大小,可以通过向 /etc/papersize
文件写入 “A4” 或 “letter” 覆盖默认值。
准备安装 Groff:
PAGE=<paper_size>
./configure --prefix=/usr
该软件包不支持并行构建。编译该软件包:
make -j1
该软件包不包含测试套件。
安装该软件包:
make install
读取 troff 字体文件并为其添加 groff 系统使用的一些额外字体规格信息 |
|
创建供 groff 和 grops 使用的字体文件 |
|
产生化学结构式的 groff 预处理器 |
|
将 troff 输入文件中嵌入的公式描述编译成 troff 理解的命令 |
|
将 troff EQN (公式)转换成裁减好的图像 |
|
标出 groff/nroff/troff 文件的区别 |
|
将 lilypond 语言写成的乐谱转换为 groff 语言 |
|
groff 预处理器,允许在 groff 文件中增加 perl 代码 |
|
groff 的预处理器,允许在 groff 文件中增加汉语拼音 |
|
将 grap 图形转换成裁减好的位图图像 |
|
用于 gremlin 文件的groff 预处理器 |
|
groff 的驱动程序, 生成 TeX dvi 格式 |
|
groff 文档格式化系统的前端;一般来说,它运行 troff 程序和一个适用于选定设备的后处理器 |
|
在 X 和 tty 终端显示 groff 文件和 man 页面 |
|
读取文件,并猜测 groff 选项
|
|
是一个用于 Canon CAPSL 打印机 (LBP-4 和 LBP-8 系列激光打印机)的 groff 驱动程序 |
|
是一个生成用于 HP LaserJet 4 打印机的 PCL5 格式的 groff 驱动程序 |
|
将 troff 输出转换成 PDF |
|
将 troff 输出转换成 PostScript |
|
将 troff 输出转换成用于打字机类设备的形式 |
|
根据 HP 标签的字体规格文件, 创建用于 groff -Tlj4的字体文件 |
|
创建用于给定文件文献数据库的反向索引, 以供 refer、lookbib 以及 lkbib 使用 |
|
在文献数据库中搜索包含指定关键字的引用, 并报告找到的所有引用 |
|
在标准错误输出上显示命令提示符(除非标准输入不是终端), 读取包含一组关键字的行, 在给定文件的文献数据库中搜索包含这些关键字的引用, 将它们打印到标准输出,重复这一过程直到输入结束 |
|
groff 的简单预处理器 |
|
将公式格式化为美国标准信息交换代码(ASCII)输出 |
|
一个使用 groff 仿真 nroff 命令的脚本 |
|
一个 groff 包装器,提供从 mom 宏包编码的文件转换为 PDF 文档的功能 |
|
用 groff 创建 PDF 文档 |
|
将 |
|
将 troff 或 TeX 输入文件中嵌入的图片描述编译成 TeX 或 troff 理解的命令 |
|
将 PIC 图示转换成裁切好的图像 |
|
将 GNU troff 的输出翻译成 HTML |
|
将输入文件的编码转换成 GNU troff 理解的格式 |
|
将 GNU troff 输出翻译成 HTML |
|
将文件内容复制到标准输出,除了在 .[ 和 .] 之间的行被解释为文献引用, .R1 和 .R2 之间的行被解释为处理文献引用的方式 |
|
将 roff 文件转换成 DVI 格式 |
|
将 roff 文件转换成 HTML 格式 |
|
将 roff 文件转换成 PDF |
|
将 roff 文件转换成 ps 文件 |
|
将 roff 文件转换成文本文件 |
|
将 roff 文件转换成其他格式 |
|
读取文件,将 .so file 形式的行替换为提到的 file 文件 file |
|
将 troff 输入中嵌入的表格描述编译成 troff 理解的命令 |
|
创建用于 groff -Tdvi 的字体文件 |
|
和 UNIX troff 高度兼容; 它应该由 groff 命令调用, 后者也会以正确的顺序和选项运行预处理器和后处理器 |
GRUB 软件包包含 “大统一” (GRand Unified) 启动引导器。
准备安装 GRUB:
./configure --prefix=/usr \ --sbindir=/sbin \ --sysconfdir=/etc \ --disable-efiemu \ --disable-werror
新的配置选项的含义:
--disable-werror
该选项允许在有较新的 Flex 版本导致的警告时完成构建。
--disable-efiemu
该选项通过禁用 LFS 不需要的特性和测试程序, 最小化需要构建的内容。
编译该软件包:
make
该软件包不包含测试套件。
安装该软件包:
make install mv -v /etc/bash_completion.d/grub /usr/share/bash-completion/completions
使用 GRUB 引导您的 LFS 系统的方法将在 第 8.4 节 “使用 GRUB 设定引导过程” 中讨论。
grub-install 使用的辅助程序 |
|
用于编辑环境块的工具 |
|
检验文件是否是给定类型 |
|
调试文件系统驱动程序的工具 |
|
处理 ia32 和 amd64 EFI 镜像,根据 Apple 格式粘合它们 |
|
在您的驱动器上安装 GRUB |
|
将 xkb 布局转化为 GRUB 能够识别的格式的脚本 |
|
Mac 风格的 bless 命令,用于 HFS 或 HFS+ 文件系统 |
|
将经典的 GRUB |
|
生成 GRUB 配置文件 |
|
创建 GRUB 可引导镜像 |
|
生成 GRUB 键盘布局文件 |
|
准备 GRUB 网络启动目录 |
|
生成用于引导菜单的加密 PBKDF2 密码 |
|
生成相对于根目录的系统路径名称 |
|
为软盘或 CDROM/DVD 创建 GRUB 可引导镜像 |
|
生成独立(包含所有模块)的镜像 |
|
打印 GRUB 设备路径的帮助程序 |
|
对给定路径或设备探测信息 |
|
仅为下次启动设置 GRUB 默认引导项 |
|
为 Apple Mac 设置 Apple .disk_label |
|
在 GRUB 配置脚本中检查语法错误 |
|
设置 GRUB 默认引导项 |
|
grub-setup 使用的帮助程序 |
|
将 syslinux 配置文件转换为 grub.cfg 格式 |
Less 软件包包含一个文本文件查看器。
准备安装 Less:
./configure --prefix=/usr --sysconfdir=/etc
配置选项的含义:
--sysconfdir=/etc
该选项告诉该软件包创建的程序在 /etc
查找配置文件
编译该软件包:
make
该软件包不包含测试套件。
安装该软件包:
make install
Gzip 软件包包含压缩和解压缩文件的程序。
准备安装 Gzip:
./configure --prefix=/usr
编译该软件包:
make
运行以下命令以测试编译结果:
make check
已知两个测试可能在 LFS 环境中失败:help-version 和 zmore。
安装该软件包:
make install
移动一个必须放置在根文件系统的程序:
mv -v /usr/bin/gzip /bin
解压缩 gzip 压缩的文件 |
|
创建自解压可执行文件 |
|
使用 Lempel-Ziv (LZ77) 编码压缩文件 |
|
解压压缩文件 |
|
将给定 gzip 压缩文件解压到标准输出 |
|
在 gzip 压缩文件上运行 cmp |
|
在 gzip 压缩文件上运行 diff |
|
在 gzip 压缩文件上运行 egrep |
|
在 gzip 压缩文件上运行 fgrep |
|
为给定所有文件中的 gzip 压缩文件确保 |
|
在 gzip 压缩文件上运行 grep |
|
在 gzip 压缩文件上运行 less |
|
在 gzip 压缩文件上运行 more |
|
将 compress
格式压缩文件重新压缩为 gzip 格式 —— 转换
|
IPRoute2 软件包包含基于 IPv4 的基本和高级网络程序。
该软件包中的 arpd 程序依赖于 LFS 不安装的 Berkeley DB,因此不会被构建。然而,用于 arpd 的一个目录和它的 man 页面仍会被安装,运行以下命令以防止它们的安装。 如果需要使用 arpd 二进制程序, 参考 BLFS 手册中的 Berkeley DB 编译说明,它位于 http://www.linuxfromscratch.org/blfs/view/9.0/server/databases.html#db。
sed -i /ARPD/d Makefile rm -fv man/man8/arpd.8
还需要禁用两个需要 http://www.linuxfromscratch.org/blfs/view/9.0/postlfs/iptables.html 的模块:
sed -i 's/.m_ipt.o//' tc/Makefile
编译该软件包:
make
该软件包没有能够工作的测试套件。
安装该软件包:
make DOCDIR=/usr/share/doc/iproute2-5.2.0 install
配置网桥 |
|
连接状态工具 |
|
通用网络连接工具前端 |
|
一个封装 ip 命令的脚本 [注意它需要 arping 和 rdisk 程序,它们来自于 iputils 软件包,可以在 http://www.skbuff.net/iputils/找到。] |
|
显示网络接口统计,包括接口上发送和接收的数据包数量 |
|
该软件包的主程序,包含以下不同的功能:
ip link ip addr 允许用户查看网络地址及其属性, 添加新地址或删除旧地址 ip neigh 允许用户查看 ARP 近邻绑定及其属性,增加新近邻项,或删除旧项 ip rule 允许用户查看或修改路由策略 ip route 允许用户查看路由表或修改路由表规则 ip tunnel 允许用户查看 IP 隧道及其属性, 或修改它们 ip maddr 允许用户查看多播地址及其属性, 或修改它们 ip mroute 允许用户设定、 修改或删除多播路由 ip monitor 允许用户连续地监视设备、 地址和路由的状态 |
|
提供 Linux 网络统计;它是旧的rtstat 的更通用、功能更完备的替代品 |
|
显示网络统计 |
|
ip route 的一个组件。用于刷新路由表 |
|
ip route 的一个组件。用于显示路由表 |
|
显示 |
|
路由监视工具 |
|
将 ip -o 的输出转换为可读形式 |
|
路由状态工具 |
|
与 netstat 命令相似;显示活动连接 |
|
流量管制可执行程序;用于实现服务质量(QOS)和服务类型(COS) 协议 tc qdisc 允许用户设定排队规则 tc class 允许用户设定基于排队规则调度的调度类 tc estimator 允许用户预计进入网络的流量 tc filter 允许用户设定 QOS/COS 数据包过滤 tc policy 允许用户设定 QOS/COS 策略 |
Kbd 软件包包含按键表文件、控制台字体和键盘工具。
退格和删除键的行为在 Kbd 软件包的不同按键映射中不一致。 以下补丁修复 i386 按键映射中的这个问题:
patch -Np1 -i ../kbd-2.2.0-backspace-1.patch
在修补后,退格键生成编码为 127 的字符,删除键生成广为人知的 escape 序列。
删除多余的 resizecons 程序(它需要已经不存在的 svgalib 提供视频模式文件 —— 一般使用 setfont 即可调整控制台大小)及其 man 页面。
sed -i 's/\(RESIZECONS_PROGS=\)yes/\1no/g' configure sed -i 's/resizecons.8 //' docs/man/man8/Makefile.in
准备安装 Kbd:
PKG_CONFIG_PATH=/tools/lib/pkgconfig ./configure --prefix=/usr --disable-vlock
配置选项的含义:
--disable-vlock
该选项防止构建 vlock 工具,因为它需要 chroot 环境中不可用的 PAM 库。
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
对于白罗斯文,Kbd 软件包没有提供有用的、 假设白罗斯文使用 ISO-8859-5 编码的键盘映射,而是一般使用 CP1251 键盘映射。使用白罗斯文等文字的用户需要单独下载可工作的键盘映射。
如果需要的话,安装文档:
mkdir -v /usr/share/doc/kbd-2.2.0 cp -R -v docs/doc/* /usr/share/doc/kbd-2.2.0
修改当前虚拟终端 |
|
取消未使用的虚拟终端分配 |
|
转储键盘转换表 |
|
打印活动虚拟终端的个数 |
|
打印内核扫描码到键码的映射表 |
|
获取终端状态信息 |
|
报告或设置键盘模式 |
|
设置键盘重复和延迟率 |
|
加载键盘翻译表 |
|
加载内核 unicode 到字体的映射表 |
|
一个过时程序, 曾用于将用户定义输出字符映射表加载到终端驱动程序; 现在该任务由 setfont 完成 |
|
在新的虚拟终端(VT)启动程序 |
|
向控制台字体增加 Unicode 字符表 |
|
提取控制台字体中嵌入的 Unicode 字符表 |
|
删除控制台字体中嵌入的 Unicode 字符表 |
|
处理控制台字体的 Unicode 字符表 |
|
修改控制台上的增强图形适配器(EGA)和视频图像阵列(VGA) 字体 |
|
加载内核扫描码到键码的映射表项; 在键盘上由特殊按键时很有用 |
|
设置键盘标志位和发光二极管(LED) |
|
定义键盘元键处理 |
|
设定所有虚拟终端的控制台颜色映射 |
|
显示当前 EGA/VGA 控制台屏幕字体 |
|
报告键盘按键的扫描码、键码和 ASCII 编码 |
|
将键盘和控制台设定为 UNICODE 模式 [不要使用该程序, 除非您的键盘映射文件是 ISO-8859-1 编码的。 对于其他编码,该工具产生错误结果。] |
|
使键盘和控制台退出 UNICODE 模式 |
Libpipeline 软件包包含用于灵活、方便地处理子进程流水线的库。
准备安装 Libpipeline:
./configure --prefix=/usr
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
Make 软件包包含用于编译软件包的程序。
再一次地,绕过 glibc-2.27 和更新版本导致的一个问题:
sed -i '211,217 d; 219,229 d; 232 d' glob/glob.c
准备安装 Make:
./configure --prefix=/usr
编译该软件包:
make
测试套件需要知道 perl 支持文件的位置。 我们使用环境变量提供该位置,运行以下命令测试编译结果:
make PERL5LIB=$PWD/tests/ check
安装该软件包:
make install
Patch 软件包包含根据通常由 diff 程序创建的 “补丁” 文件,修改或创建文件的程序。
准备安装 Patch:
./configure --prefix=/usr
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
Man-DB 软件包包含查找和阅读 man 页面的程序。
准备安装 Man-DB:
./configure --prefix=/usr \ --docdir=/usr/share/doc/man-db-2.8.6.1 \ --sysconfdir=/etc \ --disable-setuid \ --enable-cache-owner=bin \ --with-browser=/usr/bin/lynx \ --with-vgrind=/usr/bin/vgrind \ --with-grap=/usr/bin/grap \ --with-systemdtmpfilesdir= \ --with-systemdsystemunitdir=
配置选项的含义:
--disable-setuid
该选项防止将 man 程序 setuid 到用户
man
。
--enable-cache-owner=bin
该选项使得系统范围的缓存文件所有者为用户 bin。
--with-...
这三个选项设定一些默认程序。 lynx 是基于文本的 web 浏览器 (安装过程可在 BLFS 中查阅), vgrind 将程序源代码转换成 Groff 输入,grap 用于在 Groff 文档中画图。 vgrind 和 grap 在阅读 man 手册页面时一般用不到,它们不是 LFS 或 BLFS 的一部分, 但如果需要的话,您应该可以在完成 LFS 的构建后自行安装它们。
--with-systemd...
该选项防止安装不必要的,为 systemd 提供的目录和文件。
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
下表展示了 Man-DB 假定的安装在 /usr/share/man/<ll>
中的 man
手册页面的编码字符集。 另外,Man-DB 还能正确地判断出这些页面是否为 UTF-8 编码。
表 6.1. 传统 8 位 man 手册页面的预期字符编码
语言 (代号) | 编码 | 语言 (代号) | 编码 |
---|---|---|---|
丹麦语 (da) | ISO-8859-1 | 克罗地亚语 (hr) | ISO-8859-2 |
德语 (de) | ISO-8859-1 | 匈牙利语 (hu) | ISO-8859-2 |
英语 (en) | ISO-8859-1 | 日语 (ja) | EUC-JP |
西班牙语 (es) | ISO-8859-1 | 朝鲜语 (ko) | EUC-KR |
爱沙尼亚语 (et) | ISO-8859-1 | 立陶宛语 (lt) | ISO-8859-13 |
芬兰语 (fi) | ISO-8859-1 | 拉脱维亚语 (lv) | ISO-8859-13 |
法语 (fr) | ISO-8859-1 | 马其顿语 (mk) | ISO-8859-5 |
爱尔兰语 (ga) | ISO-8859-1 | 波兰语 (pl) | ISO-8859-2 |
加利西亚语 (gl) | ISO-8859-1 | 罗马尼亚语 (ro) | ISO-8859-2 |
印尼语 (id) | ISO-8859-1 | 俄语 (ru) | KOI8-R |
冰岛语 (is) | ISO-8859-1 | 斯洛伐克语 (sk) | ISO-8859-2 |
意大利语 (it) | ISO-8859-1 | 斯洛文尼亚语 (sl) | ISO-8859-2 |
挪威语(波克默尔语) (nb) | ISO-8859-1 | 塞尔维亚语(拉丁文) (sr@latin) | ISO-8859-2 |
荷兰语 (nl) | ISO-8859-1 | 塞尔维亚语 (sr) | ISO-8859-5 |
新挪威语 (nn) | ISO-8859-1 | 土耳其语 (tr) | ISO-8859-9 |
挪威语 (no) | ISO-8859-1 | 乌克兰语 (uk) | KOI8-U |
葡萄牙语 (pt) | ISO-8859-1 | 越南语 (vi) | TCVN5712-1 |
瑞典语 (sv) | ISO-8859-1 | 简体中文 (zh_CN) | GBK |
白罗斯语 (be) | CP1251 | 简体中文,新加坡 (zh_SG) | GBK |
保加利亚语 (bg) | CP1251 | 繁体中文,香港特别行政区 (zh_HK) | BIG5HKSCS |
捷克语 (cs) | ISO-8859-2 | 繁体中文,台湾省 (zh_TW) | BIG5 |
希腊文 (el) | ISO-8859-7 |
用该表之外的语言编写的 man 手册页面不被支持。
Tar 软件包包含一个归档程序。
准备编译 Tar:
FORCE_UNSAFE_CONFIGURE=1 \ ./configure --prefix=/usr \ --bindir=/bin
配置选项的含义:
FORCE_UNSAFE_CONFIGURE=1
该选项强制以 root 用户身份运行 mknod
测试。一般认为以 root 用户身份运行该测试是危险的, 不过由于是在仅仅部分构建好的系统上运行测试,
可以覆盖掉这个安全措施。
编译该软件包:
make
执行以下命令测试编译结果(需要约 3 SBU):
make check
安装该软件包:
make install make -C doc install-html docdir=/usr/share/doc/tar-1.32
Texinfo 软件包包含阅读、编写和转换 info 页面的程序。
准备安装 Texinfo:
./configure --prefix=/usr --disable-static
配置选项的含义:
--disable-static
在本例中,顶层配置脚本会抱怨说这是一个无法识别的选项, 但 XSParagraph
配置脚本能够识别它,并禁止将静态库 XSParagraph.a
安装到 /usr/lib/texinfo
。
编译该软件包:
make
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
可选地,安装属于 TeX 环境的组件:
make TEXMF=/usr/share/texmf install-tex
make 命令参数的含义:
TEXMF=/usr/share/texmf
TEXMF
Makefile 变量包含之后可能安装的
TeX 软件包的 TeX 目录树根位置。
Info 文档系统使用纯文本文件保存目录项的列表, 该文件位于 /usr/share/info/dir
。 不幸的是,由于一些软件包 Makefile
中的偶然问题, 它偶尔会与系统实际安装的 info 页面不同步。 如果需要重新创建 /usr/share/info/dir
文件, 可以运行以下命令完成这一工作:
pushd /usr/share/info rm -v dir for f in * do install-info $f dir 2>/dev/null done popd
用于阅读和 man 页面类似的 info 页面, man 页面一般只解释可用的命令行选项,而 info 页面更为深入 [例如,可以对比 man bison 和 info bison。] |
|
用于安装 info 页面;该命令更新 info 索引文件 |
|
将给定 Texinfo 源代码文档转换成 info 页面、 纯文本或 HTML |
|
将给定 Texinfo 文档格式化为可移植文档格式(PDF)文件 |
|
将 Pod 转换成 Texinfo 格式 |
|
将 Texinfo 文档转换成其他几种格式 |
|
将给定 Texinfo 文档格式化为可打印的设备无关文件 |
|
将给定 Texinfo 文档格式化为可移植文档格式(PDF)文件 |
|
用于排序 Texinfo 索引文件 |
Vim 软件包包含强大的文本编辑器。
如果您喜爱其他编辑器 —— 例如 Emacs、Joe、或者 Nano —— 参考 http://www.linuxfromscratch.org/blfs/view/9.0/postlfs/editors.html 中建议的安装说明。
首先,修改 vimrc
配置文件的默认位置为
/etc
:
echo '#define SYS_VIMRC_FILE "/etc/vimrc"' >> src/feature.h
准备编译 Vim:
./configure --prefix=/usr
编译该软件包:
make
为了准备运行测试套件,需要使得 nobody
拥有写入源代码目录树的权限:
chown -Rv nobody .
现在,以 nobody
用户运行测试:
su nobody -s /bin/bash -c "LANG=en_US.UTF-8 make -j1 test" &> vim-test.log
测试套件会将大量二进制数据输出到屏幕,这可能扰乱当前终端设置。 为了避免这个问题,像上面的命令一样,将输出重定向到日志文件。 测试成功完成后,日志文件末尾会包含 “ALL DONE”。
安装该软件包:
make install
许多用户习惯于使用命令 vi,而不是 vim。为了在用户习惯性地输入 vi 时能够执行 vim, 为二进制程序和各种语言的 man 页面创建符号链接:
ln -sv vim /usr/bin/vi for L in /usr/share/man/{,*/}man1/vim.1; do ln -sv vim.1 $(dirname $L)/vi.1 done
默认情况下,Vim 的文档安装在 /usr/share/vim
。下面创建的符号链接允许通过 /usr/share/doc/vim-8.1.1846
访问符号链接,这个路径与其他软件包的文档位置格式一致:
ln -sv ../vim/vim81/doc /usr/share/doc/vim-8.1.1846
如果在安装 LFS 系统后安装了 X 窗口系统,可能需要在安装 X 后重新编译 Vim 。 Vim 提供的 GUI 版本编辑器需要 X 和一些额外的软件包才能安装。 关于这一安装过程的更多信息,参考 Vim 文档和 BLFS 手册中位于 http://www.linuxfromscratch.org/blfs/view/9.0/postlfs/vim.html 的 Vim 安装页面。
默认情况下,vim 在不兼容 vi 的模式下运行, 这对于过去使用其他编辑器的用户来说可能显得陌生。 以下配置包含的 “nocompatible” 设定是为了强调编辑器使用了新的行为这一事实。 它也提醒那些想要使用 “compatible” 模式的用户, 必须在配置文件的一开始改变模式,因为它会修改其他设置, 因此对这些设置的覆盖必须在设定模式后进行。 执行以下命令创建默认 vim 配置文件:
cat > /etc/vimrc << "EOF"
" Begin /etc/vimrc
" Ensure defaults are set before customizing settings, not after
source $VIMRUNTIME/defaults.vim
let skip_defaults_vim=1
set nocompatible
set backspace=2
set mouse=
syntax on
if (&term == "xterm") || (&term == "putty")
set background=dark
endif
" End /etc/vimrc
EOF
set nocompatible
设定使得
vim
以一种更有用的方式(也是默认方式)行动, 而不是兼容于 vi 的旧模式。如果需要保留旧的 vi 行为,删除其中的 “no”。 set backspace=2
设定允许退格越过换行、
自动缩进和插入模式的起始位置。参数 syntax
on
启用 vim 符号高亮功能。参数 set mouse=
允许在 chroot
中或通过远程连接工作时使用鼠标正确地粘贴文本。 最后,包含设定 set background=dark
的
if 语句纠正 vim 对于某些终端模拟器背景色的猜测,
这能够提供更适合这些程序黑色背景的配色方案。
关于其他可用选项的文档可以通过执行以下命令获得:
vim -c ':options'
默认情况下 Vim 只安装英语拼写检查文件。 如果希望安装您使用的语言的拼写检查文件,从 ftp://ftp.vim.org/pub/vim/runtime/spell/
为您的语言和字符编码下载 *.spl
和可选的
*.sug
文件,并将它们保存到 /usr/share/vim/vim81/spell/
。
为了使用这些拼写检查文件,需要在 /etc/vimrc
中进行配置,例如:
set spelllang=en,ru
set spell
关于更多信息,参考以上 URL 位置中合适的 README 文件。
Procps-ng 软件包包含监视进程的程序。
准备安装 procps-ng:
./configure --prefix=/usr \ --exec-prefix= \ --libdir=/usr/lib \ --docdir=/usr/share/doc/procps-ng-3.3.15 \ --disable-static \ --disable-kill
配置选项的含义:
--disable-kill
该选项禁用 kill 命令的构建, Util-linux 软件包将安装它。
编译该软件包:
make
在 LFS 系统上,测试套件需要一些自定义修改。 删除一个在没有使用 tty 设备输入时会失败的测试,并修正另外两个。 执行以下命令运行测试套件:
sed -i -r 's|(pmap_initname)\\\$|\1|' testsuite/pmap.test/pmap.exp sed -i '/set tty/d' testsuite/pkill.test/pkill.exp rm testsuite/pgrep.test/pgrep.exp make check
安装该软件包:
make install
最后,将必要的库移动到 /usr
尚未挂载时也能访问的位置:
mv -v /usr/lib/libprocps.so.* /lib ln -sfv ../../lib/$(readlink /usr/lib/libprocps.so) /usr/lib/libprocps.so
报告系统中可用和已用内存(包括物理内存和交换空间) 的容量 |
|
根据名称和其他属性查找进程 |
|
报告给定程序的 PID |
|
根据名称和其他属性向进程发送信号 |
|
报告给定进程的内存映射 |
|
列出正在运行的进程 |
|
报告一个进程的当前工作目录 |
|
实时显示内核 slab 缓存详细信息 |
|
在运行时修改内核参数 |
|
打印当前系统平均负载示意图 |
|
列出 CPU 占用最大的进程列表; 它实时地提供处理器活动的连续概况 |
|
报告系统运行时间、登录用户数目和系统平均负载 |
|
报告虚拟内存统计,给出进程、内存、分页、块输入输出(IO)、 陷阱和 CPU 活动信息 |
|
显示当前登录用户和它们的登录地点、时间 |
|
重复执行给定命令,显示其输出的第一页; 这使得用户可以观察输出随时间的变化 |
|
包含该软件包大多数程序使用的函数 |
Util-linux 软件包包含若干工具程序,包括处理文件系统、 终端、分区和消息的工具。
FHS 建议使用 /var/lib/hwclock
目录作为
adjtime
文件的位置,而不是 一般的
/etc
目录。 首先创建该目录,启用
hwclock
程序的存储功能:
mkdir -pv /var/lib/hwclock
准备安装 Util-linux:
./configure ADJTIME_PATH=/var/lib/hwclock/adjtime \ --docdir=/usr/share/doc/util-linux-2.34 \ --disable-chfn-chsh \ --disable-login \ --disable-nologin \ --disable-su \ --disable-setpriv \ --disable-runuser \ --disable-pylibmount \ --disable-static \ --without-python \ --without-systemd \ --without-systemdsystemunitdir
--disable 和 --without 选项防止一些警告, 它们与一些 LFS 中不存在, 或与其他软件包安装的程序不兼容的构建组件相关。
编译该软件包:
make
如果希望的话,以非 root 用户身份运行测试套件:
以 root 用户身份运行测试套件可能对系统造成损害。 为了运行它,内核配置选项 CONFIG_SCSI_DEBUG 必须在当前运行的系统中可用,且必须被构建为内核模块, 直接将其构建为内核的一部分会导致系统无法引导。 为了测试的完整覆盖,必须安装其他 BLFS 软件包。 如果希望的话,可以在重新启动,进入完整的 LFS 系统后, 执行以下命令运行测试:
bash tests/run.sh --srcdir=$PWD --builddir=$PWD
chown -Rv nobody . su nobody -s /bin/bash -c "PATH=$PATH make -k check"
安装该软件包:
make install
告知 Linux 内核有新的分区 |
|
打开 tty 端口,提示输入登录名,再启动 login 程序 |
|
丢弃设备上的扇区 |
|
一个命令行工具,用于定位和打印块设备属性 |
|
在给定块设备上运行 zone 命令 |
|
允许用户从命令行调用块设备 ioctl |
|
显示简单的日历 |
|
操作给定设备的分区表 |
|
修改 CPU 状态 |
|
配置内存 |
|
显示和调整 OOM-killer 分数 |
|
操纵进程实时属性 |
|
过滤掉反向换行符 |
|
为缺失加粗、半行等功能的终端过滤 nroff 输出 |
|
过滤掉给定列 |
|
将给定文件格式化为多栏 |
|
将 Ctrl+Alt+Del 键组合的功能设定为硬复位或软复位 |
|
要求 Linux 内核删除分区 |
|
转储内核引导消息 |
|
弹出可移动媒体 |
|
为文件预先分配空间 |
|
低级格式化软盘 |
|
操作给定设备的分区表 |
|
统计给定文件在内存中占用的页面数 |
|
根据通用唯一识别码(UUID)查找文件系统 |
|
是 libmount 库的命令行接口,可以处理 mountinfo、fstab 和 mtab 文件 |
|
获取文件锁,并在持有锁的情况下运行命令 |
|
用于检查或修复文件系统 |
|
用于对给定设备上的 Cramfs 文件系统进行一致性检查 |
|
用于对给定设备上的 Minix 文件系统进行一致性检查 |
|
是内核驱动 ioctl 选项 FIFREEZE/FITHAW 的简单包装 |
|
丢弃已挂载文件系统上未使用的块 |
|
解析给定命令行的选项 |
|
以十六进制或其他给定格式转储文件 |
|
读取或设置系统硬件时钟,它又被称为实时时钟(RTC) 或基本输入输出系统(BIOS)时钟 |
|
到 setarch 的符号链接 |
|
设定程序的 IO 调度类和优先级 |
|
创建多种 IPC 资源 |
|
删除给定 IPC 资源 |
|
提供 IPC 状态信息 |
|
报告 ISO 9660 文件系统的大小 |
|
向进程发送信号 |
|
显示哪些用户最后登录(或登出), 在 |
|
显示 |
|
为串口线附加行规则 |
|
到 setarch 的符号链接 |
|
到 setarch 的符号链接 |
|
将给定消息记入系统日志 |
|
显示以给定字符串开始的行 |
|
设定和控制回环设备 |
|
以树状格式列出所有或给定块设备的信息 |
|
打印 CPU 体系结构信息 |
|
打印系统当前部署的 IPC 设施的信息 |
|
列出本地系统锁 |
|
列出用户、组和系统账户的信息 |
|
列出可用内存的范围和它们的在线状态 |
|
列出命名空间 |
|
为 xauth 创建魔术 cookie (128位随机十六进制数) |
|
控制其他用户能否向当前用户终端发送消息 |
|
在设备(一般是硬盘分区)上创建文件系统 |
|
创建 Santa Cruz Operations (SCO) bfs 文件系统 |
|
创建 cramfs 文件系统 |
|
创建 Minix 文件系统 |
|
将给定文件或设备初始化为交换空间 |
|
用于对文本在屏幕上进行分页的过滤器 |
|
将给定设备上的文件系统挂载到文件系统树结构中的给定目录 |
|
检查目录是否为挂载点 |
|
显示给定目录名中的符号链接 |
|
在其他程序的命名空间中运行程序 |
|
告知内核磁盘分区的存在性和编号 |
|
将当前进程的根文件系统设为给定文件系统 |
|
获取和设定进程资源限制 |
|
将 Linux raw 字符设备绑定到块设备 |
|
读取内核性能分析信息 |
|
重命名给定文件,将给定字符串替换为另一个字符串 |
|
修改当前进程的优先级 |
|
要求 Linux 内核改变分区大小 |
|
反转给定文件的行 |
|
用于启用或禁用无线设备的工具 |
|
进入睡眠状态,直到给定的唤醒时间 |
|
记录终端会话打字机文档 |
|
根据计时信息重放终端会话打字机文档 |
|
在新程序环境中修改系统报告的体系结构, 并设置进程执行域信息 |
|
在新会话中运行给定程序 |
|
设定终端属性 |
|
一个分区表修改器 |
|
允许 |
|
允许修改交换空间 UUID 和标签 |
|
禁止在文件或设备上进行分页交换 |
|
启用文件或设备上的分页交换, 或列出当前用于交换的设备和文件 |
|
将另一个文件系统切换为挂载树的根 |
|
跟踪日志文件的增长;显示日志文件的最后 10 行, 并在日志文件有新记录时继续显示它们 |
|
获取或设置进程 CPU 亲和性 |
|
将下划线 escape 序列转换为当前终端格式的过滤器 |
|
解除文件系统与系统文件目录树的连接 |
|
到 setarch 的符号链接 |
|
在某些命名空间与父进程脱离的情况下运行程序 |
|
以更加用户友好的格式显示给定登录文件 |
|
UUID 库使用的守护进程,用于安全、确保唯一性地生成 UUID |
|
创建新的 UUID。 每个新的 UUID 可以被合理地认为在本地系统和其他系统上, 在过去和未来,都是唯一的 |
|
用于解析统一标识符的工具 |
|
显示文件或标准输入(默认值)的内容到所有登录用户的终端 |
|
显示硬件看门狗电路状态 |
|
报告给定命令二进制文件、源代码文件和 man 页面的位置 |
|
从设备上擦除文件系统签名 |
|
到 setarch 的符号链接 |
|
设定和控制 zram (压缩内存盘) 的程序 |
|
包含设备识别和标识提取子程序 |
|
包含操作分区表的子程序 |
|
包含挂载和解挂块设备的子程序 |
|
包含以表格形式在屏幕上输出的辅助子程序 |
|
包含为对象生成唯一标识符, 使它在本地系统以外也可以访问的子程序 |
E2fsprogs 软件包包含处理 ext2
文件系统的工具。 此外它也支持 ext3
和
ext4
日志文件系统。
E2fsprogs 文档推荐在源代码目录树中的一个子目录中构建该软件包:
mkdir -v build cd build
准备安装 E2fsprogs:
../configure --prefix=/usr \ --bindir=/bin \ --with-root-prefix="" \ --enable-elf-shlibs \ --disable-libblkid \ --disable-libuuid \ --disable-uuidd \ --disable-fsck
环境变量和配置选项的含义:
--with-root-prefix=""
和
--bindir=/bin
某些程序(例如 e2fsck 程序)被认为是关键的。在
/usr
尚未挂载等情况下,
这些程序仍然必须可用。它们应该放置在 /lib
和
/sbin
等目录中。如果没有向
e2fsprogs 配置脚本传递该参数,这些程序会被安装到 /usr
目录。
--enable-elf-shlibs
该选项表示创建该软件包中一些程序使用的共享库。
--disable-*
该选项防止 e2fsprogs 构建和安装 libuuid
和 libblkid
库, uuidd
守护程序,以及 fsck 包装器, 因为
Util-linux 会安装更新的版本。
编译该软件包:
make
执行以下命令,以运行测试:
make check
某个 e2fsprogs 测试会试图分配 256MB 内存。 如果您的内存刚刚超过或者甚至不足这个大小, 一定要为测试启用足够的交换空间。 阅读 第 2.5 节 “在分区上建立文件系统” 和 第 2.7 节 “挂载新的分区” 了解创建和启用交换空间的详细过程。
安装二进制程序、文档和共享库:
make install
安装静态库和头文件:
make install-libs
将安装好的静态库变为可写的,以便之后移除调试符号:
chmod -v u+w /usr/lib/{libcom_err,libe2p,libext2fs,libss}.a
该软件包安装了一个 gzip 压缩的 .info
文件,却没有更新系统的 dir
文件。执行以下命令解压该文件,并更新系统 dir
文件:
gunzip -v /usr/share/info/libext2fs.info.gz install-info --dir-file=/usr/share/info/dir /usr/share/info/libext2fs.info
如果需要,执行以下命令创建并安装一些额外的文档:
makeinfo -o doc/com_err.info ../lib/et/com_err.texinfo install -v -m644 doc/com_err.info /usr/share/info install-info --dir-file=/usr/share/info/dir /usr/share/info/com_err.info
在一个设备(一般是磁盘分区)上搜索坏块 |
|
在 |
|
一个错误表编译器; 它将包含错误编号名称和消息的表转化成 C 源代码,以和 |
|
一个文件系统调试器;可以检验并修改 |
|
打印给定设备上文件系统的超级块和块组信息 |
|
报告可用空间碎片信息 |
|
用于检查或修复 |
|
用于将 |
|
显示或修改给定设备上的 |
|
检查 ext4 文件系统的 MMP 状态 |
|
检查某个已挂载的 ext2,ext3 或 ext4 文件系统 |
|
检查所有已挂载的 ext2,ext3 或 ext4 文件系统 |
|
重放设备上找到的 ext2/ext3/ext4 文件系统撤销日志 undo_log [可以用于撤销 e2fsprogs 程序的失败操作。] |
|
Ext4 文件系统加密工具 |
|
ext4 文件系统在线碎片整理器 |
|
报告特定文件碎片化程度 |
|
默认情况下检查 |
|
默认情况下检查 |
|
默认情况下检查 |
|
将命令输出保存到日志文件 |
|
列出 ext2 文件系统上的文件属性 |
|
将包含命令名称和帮助信息的表格转换成 C 源代码文件,以便和 |
|
在给定设备上创建 |
|
默认情况下创建 |
|
默认情况下创建 |
|
默认情况下创建 |
|
用于在 |
|
可以用于扩大或压缩 |
|
调整 |
|
公用错误显示子程序 |
|
被 dumpe2fs、chattr, 和 lsattr 使用 |
|
包含允许用户级程序操纵 |
|
被 debugfs 使用 |
Sysklogd 软件包包含记录系统消息的程序, 例如在意外情况发生时内核给出的消息。
首先,修复 klogd 中在特定情况下会发生段错误的问题, 并修复一个过时的程序结构:
sed -i '/Error loading kernel symbols/{n;n;d}' ksym_mod.c sed -i 's/union wait/int/' syslogd.c
编译该软件包:
make
该软件包不包含测试套件。
安装该软件包:
make BINDIR=/sbin install
执行以下命令,创建一个新的 /etc/syslog.conf
文件:
cat > /etc/syslog.conf << "EOF"
# Begin /etc/syslog.conf
auth,authpriv.* -/var/log/auth.log
*.*;auth,authpriv.none -/var/log/sys.log
daemon.* -/var/log/daemon.log
kern.* -/var/log/kern.log
mail.* -/var/log/mail.log
user.* -/var/log/user.log
*.emerg *
# End /etc/syslog.conf
EOF
Sysvinit 软件包包含控制系统启动、运行和关闭的程序。
首先,应用一个补丁,它会删除 sysvinit 中其他软件包已经安装的程序, 使一条消息更加清晰,并修复一个引发编译器警告的问题:
patch -Np1 -i ../sysvinit-2.95-consolidated-1.patch
编译该软件包:
make
该软件包不包含测试套件。
安装该软件包:
make install
将引导消息写入日志文件 |
|
以 fstab 编码的参数运行命令 |
|
通常以 |
|
内核初始化硬件后启动的第一个进程,它接管引导过程, 启动其配置文件中指定的所有进程 |
|
向除了自身会话中以外的所有进程发送信号, 杀死多数进程,但不会杀死作为它本身父进程的 shell |
|
告诉内核停止系统运行并关闭电源 (参阅 halt) |
|
告诉内核重启系统 (参阅 halt) |
|
报告上一次的和当前的系统运行级别, 上一次系统运行级别记录在 |
|
安全地将系统下线,向所有进程发送信号,并通知所有登录用户 |
|
告诉 init 切换到哪个运行级别 |
Eudev 软件包包含动态创建设备节点的程序。
准备安装 Eudev:
./configure --prefix=/usr \ --bindir=/sbin \ --sbindir=/sbin \ --libdir=/usr/lib \ --sysconfdir=/etc \ --libexecdir=/lib \ --with-rootprefix= \ --with-rootlibdir=/lib \ --enable-manpages \ --disable-static
编译该软件包:
make
创建一些目录,它们目前被用于测试,但之后也会成为安装好的 eudev 的一部分。
mkdir -pv /lib/udev/rules.d mkdir -pv /etc/udev/rules.d
运行以下命令以测试编译结果:
make check
安装该软件包:
make install
安装一些在 LFS 环境中很有用的自定义规则和支持文件:
tar -xvf ../udev-lfs-20171102.tar.xz make -f udev-lfs-20171102/Makefile.lfs install
硬件设备的相关信息被维护在 /etc/udev/hwdb.d
和 /lib/udev/hwdb.d
目录中。
Eudev 需要将这些信息编译到二进制数据库
/etc/udev/hwdb.bin
中。初始化该数据库:
udevadm hwdb --update
每次硬件信息有更新时,都要运行该命令。
许多程序和库在默认情况下被编译为带有调试符号的二进制文件 (通过使用 gcc 的 -g
选项)。 这意味着在调试这些带有调试信息的程序和库时,
调试器不仅能给出内存地址,还能给出子程序和变量的名称。
然而,插入这些调试符号会显著增大程序或库的体积。 下面是一些表现调试符号占用空间的例子:
一个有调试符号的 bash 二进制程序: 1200 KB
一个没有调试符号的 bash 二进制程序: 480 KB
带有调试符号的 Glibc 和 GCC 文件 (/lib
和 /usr/lib
目录中): 87 MB
没有调试符号的 Glibc 和 GCC 文件:16 MB
以上文件大小的值可能随编译器和 C 运行库的版本而变化, 但在比较带调试符号和不带调试符号的程序时,它们文件大小的差距通常达到 2 至 5 倍。
由于大多数用户永远不会用调试器调试系统软件, 可以通过移除它们的调试符号,回收大量磁盘空间。 下一节展示如何从系统程序和库中移除所有调试符号。
本节是可选的。如果系统不是为程序员设计的, 也没有调试系统软件的计划,可以通过从二进制程序和库移除调试符号, 将系统的体积减小约 90 MB 。除了无法再调试全部软件外, 这不会造成任何不便。
大多数使用以下命令的用户不会遇到什么困难,但如果打错了命令, 很容易导致新系统无法使用。因此,在运行 strip 命令前,最好备份 LFS 系统的当前状态。
首先将一些库的调试符号保存在单独的文件中,在 BLFS 中, 如果使用 valgrind 或 gdb 运行退化测试, 则需要这些调试信息的存在。
save_lib="ld-2.30.so libc-2.30.so libpthread-2.30.so libthread_db-1.0.so" cd /lib for LIB in $save_lib; do objcopy --only-keep-debug $LIB $LIB.dbg strip --strip-unneeded $LIB objcopy --add-gnu-debuglink=$LIB.dbg $LIB done save_usrlib="libquadmath.so.0.0.0 libstdc++.so.6.0.27 libitm.so.1.0.0 libatomic.so.1.2.0" cd /usr/lib for LIB in $save_usrlib; do objcopy --only-keep-debug $LIB $LIB.dbg strip --strip-unneeded $LIB objcopy --add-gnu-debuglink=$LIB.dbg $LIB done unset LIB save_lib save_usrlib
在移除调试符号前,必须特别小心, 保证没有需要移除调试符号的二进制程序还在运行:
exec /tools/bin/bash
注意这里使用 /tools/bin/bash 代替当前运行的 /bin/bash , 因为 /bin/bash 二进制文件将会被移除调试符号。 之后使用的 /tools/bin/find 和 /tools/bin/strip 同理。
现在即可安全地移除程序和库的调试符号:
/tools/bin/find /usr/lib -type f -name \*.a \ -exec /tools/bin/strip --strip-debug {} ';' /tools/bin/find /lib /usr/lib -type f \( -name \*.so* -a ! -name \*dbg \) \ -exec /tools/bin/strip --strip-unneeded {} ';' /tools/bin/find /{bin,sbin} /usr/{bin,sbin,libexec} -type f \ -exec /tools/bin/strip --strip-all {} ';'
这里会有很多文件被报告为格式无法识别,这些警告可以安全地忽略。 它们表明那些文件是脚本文件,而不是二进制文件。
最后,清理在执行测试的过程中遗留的一些文件:
rm -rf /tmp/*
现在需要登出,并使用新的 chroot 命令行重新进入 chroot 环境。 从现在起,在退出并重新进入 chroot 环境时, 要使用下面的修改过的 chroot 命令:
logout chroot "$LFS" /usr/bin/env -i \ HOME=/root TERM="$TERM" \ PS1='(lfs chroot) \u:\w\$ ' \ PATH=/bin:/usr/bin:/sbin:/usr/sbin \ /bin/bash --login
这是由于 /tools
中的程序不再必要。因此,如果您希望的话,可以删除 /tools
目录。
删除 /tools
也会移除 Tcl、Expect 和
DejaGNU 的临时拷贝,它们之前被用于进行工具链测试。 如果您之后仍然需要它们,必须重新编译和安装这些软件包。
BLFS 手册包含了安装它们的说明 (见 http://www.linuxfromscratch.org/blfs/)。
如果解挂了虚拟内核文件系统, 必须通过手动或重启系统的方式重新挂载它们,保证在进入 chroot 时它们已经挂载好,正如 第 6.2.2 节 “挂载和填充 /dev” 和 第 6.2.3 节 “挂载虚拟内核文件系统” 解释的那样。
在本章的前几节中,有几个静态库的安装没有被禁止, 目的是满足一些软件包的退化测试需要。这些库来自于 binutils、bzip2、 e2fsprogs、flex、libtool 和 zlib。如果您希望的话,可以现在删除它们:
rm -f /usr/lib/lib{bfd,opcodes}.a rm -f /usr/lib/libbz2.a rm -f /usr/lib/lib{com_err,e2p,ext2fs,ss}.a rm -f /usr/lib/libltdl.a rm -f /usr/lib/libfl.a rm -f /usr/lib/libz.a
/usr/lib 和 /usr/libexec 目录中还有一些扩展名为 .la 的文件。 它们是 “libtool 档案” 文件, 在 Linux 系统上一般是不必要的。截至目前, 已经安装的 .la 文件中没有一个是必要的。执行以下命令删除它们:
find /usr/lib /usr/libexec -name \*.la -delete
如果希望了解更多关于 libtool 档案文件的信息,参阅 BLFS 章节 "About Libtool Archive (.la) files"。
引导 Linux 系统需要完成若干任务。 引导过程必须挂载虚拟和真实文件系统、初始化设备、启用交换、 检查文件系统完整性、挂载所有交换分区或文件、设定系统时钟、 启用网络、启动系统需要的守护进程,并完成用户自定义的其他工作。 引导过程必须被组织好,以保证这些任务以正确顺序进行, 并以尽量快的速度完成。
System V 是自 1983 年以来就在 Unix 和 Linux 等类 Unix 系统中被广泛应用的经典引导过程。它包含一个小程序 init,该程序设定 login (通过 getty)并运行一个脚本。该脚本一般被命名为 rc,控制一组附加脚本的运行, 这些附加脚本完成初始化系统需要的各项工作。
init 程序受到
/etc/inittab
文件的控制,被组织为用户可以选择的系统运行级别:
0 — 停止运行
1 — 单用户模式
2 — 多用户模式,没有网络
3 — 完整的多用户模式
4 — 用户自定义模式
5 — 拥有显示管理器的完整多用户模式
6 — 重启系统
通常的默认运行级别是 3 或 5。
完备的,已经被详细理解的系统。
容易定制。
引导速度较慢。一个中等速度的基本 LFS 系统从第一个内核消息开始,到出现登录提示符为止, 需要 8-12 秒的引导时间,之后还需要约 2 秒启动网络连接。
串行执行引导任务,这与前一项缺点相关。 引导过程中的延迟(如文件系统检查)会延迟整个引导过程。
不支持控制组(cgroups)、每用户公平共享调度等高级特性。
添加脚本时,需要手动决定它在引导过程中的次序。
LFS-Bootscripts 软件包包含一组在引导和关机过程中, 启动和停止 LFS 系统的脚本。 它们的配置文件和自定义引导过程的方法将在后续章节中描述。
安装该软件包:
make install
在挂载文件系统前检查文件系统完整性 (日志文件系统和网络文件系统除外) |
|
删除在重启过程中不应保留的文件,例如 |
|
加载用户希望使用的键盘布局对应的键映射表, 并加载屏幕字体 |
|
包含若干启动脚本使用的错误处理和状态检查等脚本 |
|
停止系统运行 |
|
停用网络设备 |
|
启用网络设备 |
|
设置系统主机名和本地回环设备 |
|
加载 |
|
挂载所有文件系统,标为 noauto 的文件系统和网络文件系统除外 |
|
挂载 |
|
设定网卡等网络接口,并(在可能的情况下)设定默认网关 |
|
主要运行级别控制脚本; 它负责逐个运行所有其他脚本, 运行的顺序根据脚本符号链接的名称确定 |
|
重启系统 |
|
确认在系统重启或停止运行前,所有进程已经终止 |
|
在硬件时钟没有设定为 UTC 时,将内核时钟重设为本地时间 |
|
提供为网络接口分配静态网际互联协议(IP)地址的功能 |
|
启用或禁用交换空间 |
|
如果 |
|
启动或停止系统与内核日志守护进程 |
|
为其他守护进程创建自定义启动脚本的模板 |
|
准备 |
|
重试失败的 udev 事件,如果必要的话,将生成的规则从 |
在 第 6 章 中,我们在构建 eudev 时安装了 Udev 软件包。在我们详细讨论它的工作原理之前, 首先按时间顺序简要介绍历史上曾经使用过的设备管理方式。
传统的 Linux 系统通常使用静态设备创建方法,即在 /dev
下创建大量设备节点 (有时有数千个节点),无论对应的硬件设备是否真的存在。 一般通过 MAKEDEV 脚本完成这一工作,
它包含以相关的主设备号和次设备号, 为世界上可能存在的每个设备建立节点的大量 mknod 程序调用。
使用 Udev 方法, 只有那些被内核检测到的设备才会获得为它们创建的设备节点。
由于这些设备节点在每次引导系统时都会重新创建, 它们被储存在 devtmpfs
文件系统中(一个虚拟文件系统,完全驻留在系统内存)。
设备节点不需要太多空间,它们使用的系统内存可以忽略不计。
在 2000 年 2 月,一个称为 devfs
的新文件系统被合并到 2.3.46 版内核中, 并在 2.4 系列稳定内核中可用。尽管它本身曾经存在于内核源代码中,
但这种设备节点动态创建方法从未得到内核核心开发者的大力支持。
devfs
采用的这种方法的主要问题是它处理设备的检测、创建和命名的方式, 其中最致命的是设备节点命名方式。一个被广泛接受的理念是,
如果设备名称是允许配置的,那么设备命名策略应该由系统管理员, 而不是某个(些)特定开发者决定。 devfs
还受到其设计中固有的竞争条件的严重影响,
在不对内核进行大量修改的前提下无法修复这一问题。 在一段时间后,由于缺乏维护,它被标记为过时特性,最终在 2006 年 6
月被从内核中移除。
在不稳定的 2.5 系列内核开发过程中,加入了一个新的虚拟文件系统, 称为 sysfs
, 并在 2.6 系列稳定内核中发布。
它的工作是将系统硬件配置信息导出给用户空间进程, 有了这个用户空间可见的配置描述,就可能开发一种 devfs
的用户空间替代品。
前面已经简要提到了 sysfs
文件系统。
有些读者可能好奇, sysfs
是如何知道系统中存在哪些设备,以及应该为它们使用什么设备号的。
答案是,那些编译到内核中的驱动程序在它们的对象被内核检测到时, 直接将它们注册到 sysfs
(内部的 devtmpfs)。对于那些被编译为模块的驱动程序,
注册过程在模块加载时进行。只要 sysfs
文件系统被挂载好(位于 /sys),用户空间程序即可使用驱动程序注册在 sysfs
中的数据,Udev 就能够使用这些数据对设备进行处理
(包括修改设备节点)。
内核通过 devtmpfs
直接创建设备文件,任何希望注册设备节点的驱动程序都要通过 devtmpfs
(经过驱动程序核心)实现。当一个 devtmpfs
实例被挂载到 /dev
时,设备节点将被以固定的名称、访问权限和所有者首次创建。
很快,内核会向 udevd
发送一个 uevent 事件。 根据 /etc/udev/rules.d
、 /lib/udev/rules.d
以及 /run/udev/rules.d
中的规则,udevd
将为设备节点创建额外的符号链接,或修改其访问权限、所有者、属组, 或修改该对象的 udevd 数据库条目(名称)。
以上三个目录中的规则都被编号,且这三个目录的内容将合并处理。 如果 udevd 找不到它正在创建的设备对应的规则,
它将会沿用 devtmpfs
最早使用的访问权限和所有权。
编译为内核模块的设备驱动程序可能有内建的别名, 别名可以通过 modinfo 程序查询,
它通常和该模块支持的设备的总线相关标识符有关。 例如,snd-fm801 驱动程序支持厂商 ID 为
0x1319,设备 ID 为 0x0801 的 PCI 设备,其别名为 “pci:v00001319d00000801sv*sd*bc04sc01l*”。
对于多数设备,总线驱动程序会通过 sysfs
导出应该处理该设备的驱动程序别名,例如 /sys/bus/pci/devices/0000:00:0d.0/modalias
文件应该包含字符串 “pci:v00001319d00000801sv00001319sd00001319bc04sc01i00
”。Udev 附带的默认规则会导致 udevd 以 MODALIAS
uevent 环境变量的内容 (应该和 sysfs 的
modalias
文件内容一致)调用
/sbin/modprobe,并加载在通配符扩展后,
别名能够匹配该字符串的所有模块。
在本例中,这意味着除了 snd-fm801 外,过时(且不希望)的 forte 如果可用, 也会被加载。下面将介绍防止加载不希望的驱动程序的方法。
内核本身也能够在需要时为网络协议、文件系统和 NLS 支持加载模块。
在自动创建设备节点时,可能发现一些问题。
Udev 只加载拥有总线特定别名,且总线驱动程序正确地向 sysfs
导出了必要别名的模块。如果情况不是这样,
您应该考虑用其他方法加载模块。在 Linux-5.2.8 中, 已知 Udev 可以加载编写正确的
INPUT、IDE、PCI、USB、SCSI、SERIO 和 FireWire 驱动程序。
为了确定您需要的设备驱动程序是否包含 Udev 支持, 以模块名为参数运行 modinfo 命令。 然后试着在
/sys/bus
中找到设备对应的目录,并检查其中是否有
modalias
文件。
如果 sysfs
中有 modalias
文件,说明驱动程序支持该设备,
并能够直接和设备交互,但却没有正确地别名。这是驱动程序的 bug , 您需要不通过 Udev
直接加载驱动,并等待这个问题日后被解决。
如果在 /sys/bus
下的相关目录中没有
modalias
文件,
说明内核开发者尚未对该总线类型增加 modalias 支持。 在 Linux-5.2.8 中,ISA 总线不受支持。
只能等待这个问题在日后被解决。
Udev 根本不会尝试加载 snd-pcm-oss 等 “包装器” 驱动程序,或 loop 等非硬件驱动程序。
如果 “包装器” 仅仅用于增强其他模块的功能 (例如,snd-pcm-oss 增强 snd-pcm 的功能, 使 OSS
应用程序能够使用声卡),需要配置 modprobe 使其在 Udev
加载被包装的模块时, 自动加载包装器。为此,在对应的 /etc/modprobe.d/
文件中加入一个 “softdep” 行,例如:
<filename>
.conf
softdep snd-pcm post: snd-pcm-oss
注意 “softdep” 命令也允许 pre:
依赖项,或混合使用 pre:
和 post:
依赖项。参阅 modprobe.d(5)
man
手册页面, 了解更多关于 “softdep” 语法和功能的信息。
如果出现问题的模块不是包装器,而是本身就有特定功能的模块, 需要配置 modules 启动脚本以在系统引导时加载它。
为此,将模块名添加到 /etc/sysconfig/modules
中,作为单独的一行。包装器模块也可以被添加到这里, 但对于包装器来说这不是最好的方案。
不要构建该模块,或者在 /etc/modprobe.d/blacklist.conf
文件中禁用它。以
forte 为例,
下面一行禁用了该模块:
blacklist forte
被禁用的模块仍然可以通过直接执行 modprobe 手动加载。
这一般是由于一条规则意外地匹配了某个设备。 例如,一个写得不好的规则可能同时匹配到 SCSI 磁盘(正确的) 和对应厂商的 SCSI 通用设备(不正确的)。 找到引起问题的规则,并通过 udevadm info 的帮助,将它进一步细化。
这可能是前一个问题的另一个表现形式。如果不是, 而且您的规则使用了 sysfs
属性,
这个问题可能由内核计时问题引发,这类问题需要在新的内核版本中修复。 目前,您可以创建一条规则以等待被使用的
sysfs
属性, 并将它附加到
/etc/udev/rules.d/10-wait-for-sysfs.rules
文件中(如果不存在就创建一个文件),绕过这个问题。 如果您通过这种方法解决了问题,请通知 LFS 开发邮件列表。
以下内容假设驱动程序已经被编译到内核中,或作为模块被加载, 而且您已经检查过并确认 Udev 没有创建命名错误的设备。
如果驱动程序没有将它的信息导出到 sysfs
,
Udev 就无法获得创建设备节点必需的信息。 这种问题往往出现在内核源代码树以外的第三方驱动程序中。 这时,需要在
/lib/udev/devices
中使用正确的主设备号和次设备号,创建一个静态设备节点 (参考内核文档中的 devices.txt
或第三方驱动厂商提供的文档),udev 会将该静态设备节点复制到
/dev
。
这是由于 Udev 从设计上就是并行加载模块的,因此无法预测加载顺序。 这个问题永远也不会被 “修复”。 您不应该指望内核提供稳定的设备命名,而是应该创建您自己的规则, 以根据设备的一些稳定属性,例如设备序列号或 Udev 安装的一些 *_id 工具的输出,来创建具有稳定名称的符号链接。 可以参考 第 7.4 节 “管理设备” 和 第 7.5 节 “一般网络配置” 中的例子。
以下链接包含了一些额外的帮助文档:
A Userspace Implementation of devfs
http://www.kroah.com/linux/talks/ols_2003_udev_paper/Reprint-Kroah-Hartman-OLS2003.pdf
The sysfs
Filesystem
http://www.kernel.org/pub/linux/kernel/people/mochel/doc/papers/ols-2005/mochel.pdf
Udev 在默认情况下,根据固件与 BIOS 的数据,或总线、插槽与 MAC 地址 等物理特性命名网络设备。 这种命名法的主要目的是保证网络设备在每次引导时获得一致的命名, 而不是基于网卡被系统发现的时间进行命名。 例如,在老式命名方法中,如果一台计算机拥有两块网卡, 其中一块由 Intel 生产,另一块由 Realtek 生产, 则 Intel 网卡可能被命名为 eth0 ,而 Realtek 网卡被命名为 eth1, 然而在重新启动系统后,它们的顺序可能会颠倒。
在新的命名架构中,典型的网络设备名称就像 enp5s0 或 wlp3s0 这样。 如果您不喜欢这种命名惯例,可以采用传统或自定义命名架构。
传统的,如同 eth0,eth1 这样的命名架构可以通过在内核命令行中加入 net.ifnames=0
而恢复。
这在那些没有两块同类以太网设备的机器上最为合适。 笔记本一般拥有两个以太网连接,在这种命名方式下分别是 eth0 和
wlan0,也可以采用这种方法。 内核命令行需要写入 GRUB 配置文件中,参阅 第 8.4.4 节 “创建 GRUB
配置文件”。
可以创建自定义 Udev 规则,定制命名架构。 系统中包含一个生成初始规则的脚本,执行以下命令生成初始规则:
bash /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 生产商 ID 和设备 ID,如果它是 PCI 设备的话), 并在括号中给出它的驱动程序,如果能找到驱动程序的话。 它们并不被用于确定该设备的命名,仅供您在编写规则时进行参考。 第二行是匹配该 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
的值是我们关注的重点。
您在继续阅读之前,需要确保自己知道赋予每个网络接口的命名, 在之后的配置文件中需要使用这些名称。
您之后可能希望安装的一些程序(如某些媒体播放器)预期 /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 -i -e 's/"write_cd_rules"/"write_cd_rules mode
"/' \
/etc/udev/rules.d/83-cdrom-symlinks.rules
注意现在并不需要创建规则文件和符号链接,因为已经绑定挂载了宿主的 /dev
目录, 我们假定宿主系统存在正确的符号链接。 只要在第一次引导 LFS
系统后创建规则和符号链接即可。
然而,如果您有多个 CD-ROM 设备, 则生成的符号链接可能指向不同于您的宿主系统的设备,
因为设备发现的顺序不可预测。在您第一次引导 LFS 系统后, 创建的设备分配将会是稳定的,因此这仅在您希望宿主系统和
LFS 中的符号链接指向同一设备时才会成为问题。 如果您需要这样,在引导后检查(如果需要的话修改)生成的
/etc/udev/rules.d/70-persistent-cd.rules
文件,保证分配的符号链接和您的需要一致。
正如 第 7.3 节 “设备和模块管理概述” 中所述,
那些功能相同的设备在 /dev
中的顺序是随机的。例如,如果您有一个 USB 摄像头和一个电视棒, 有时 /dev/video0
会指向摄像头, /dev/video1
指向电视棒, 而有时在重启后这个顺序正好颠倒过来。
对于所有除了声卡和网卡以外的设备, 该问题都可以通过为自定义永久符号链接创建 Udev 规则来解决。 对于网卡的解决方案在
第 7.5 节 “一般网络配置”
中单独描述,而声卡配置可以在
BLFS 中找到。
对于您的每个可能有这类问题的设备(即使在您当前使用的 Linux 发行版上并没有问题),找到 /sys/class
或 /sys/block
中的对应目录。对于视频设备,目录可能是 /sys/class/video4linux/video
。找出能够唯一确认该设备的属性
(通常是厂商和产品 ID,或者序列号):
X
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
总会指向正确设备。
负责网络的启动脚本根据 /etc/sysconfig
中配置文件的内容, 决定应该启用或禁用哪些网络接口。对于每个需要配置的网络接口, 该目录中都应该包含一个文件,文件名类似
ifconfig.xyz
, 这里“xyz”应该能够描述该网卡,
使用接口名(如 eth0)一般比较合适。 文件内部是该网络接口的属性,如 IP 地址、子网掩码等。 文件名必须以
ifconfig 开头。
如果没有使用前一节描述的自定义命名策略, udev 会根据系统物理特征命名网卡接口,例如 enp2s1。 如果您不能确定接口名,可以在引导您的 LFS 系统后使用 ip link 或 ls /sys/class/net 命令确认。
作为示例,以下命令为 eth0 设备创建一个静态 IP 地址配置:
cd /etc/sysconfig/ cat > ifconfig.eth0
<< "EOF"ONBOOT=
EOFyes
IFACE=eth0
SERVICE=ipv4-static
IP=192.168.1.2
GATEWAY=192.168.1.1
PREFIX=24
BROADCAST=192.168.1.255
您必须修改每个文件中用斜体显示的设定值,使其与您的网络环境相匹配。
如果 ONBOOT
变量被设置为“yes”, 则 System V
网络脚本会在引导系统时启用该网络接口卡(NIC)。 否则,网络脚本会忽略该 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 页面获得更多信息。
系统需要某种方式,获取域名服务(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。
在引导过程中,/etc/hostname
被用于设定系统主机名。
执行以下命令,创建 /etc/hostname
文件,并输入一个主机名:
echo "<lfs>
" > /etc/hostname
<lfs>
需要被替换为赋予该计算机的名称。 不要在这里输入全限定域名 (FQDN) ,它应该被写入 /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, 某些程序需要它才能正常工作。
如果使用 DHCP、DHCPv6 或 IPv6 自动配置,或者不准备配置网卡, 执行以下命令创建 /etc/hosts
文件:
cat > /etc/hosts << "EOF"
# Begin /etc/hosts
127.0.0.1 localhost
127.0.1.1 <FQDN>
<主机名>
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
# End /etc/hosts
EOF
::1 是 127.0.0.1 在 IPv6 中的对应,即 IPv6 回环接口。
如果使用静态地址,执行以下命令创建 /etc/hosts
文件:
cat > /etc/hosts << "EOF"
# Begin /etc/hosts
127.0.0.1 localhost
127.0.1.1 <FQDN>
<主机名>
<192.168.0.2>
<FQDN>
<主机名>
[别名 1] [别名 2] ...
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
# End /etc/hosts
EOF
其中 <192.168.0.2>
、
<FQDN>
和
<HOSTNAME>
值需要为特定使用环境和需求进行修改 (如果系统或网络管理员分配了 IP 地址,且本机将被连接到现有的网络中)。
可以跳过别名(alias),它们不是必要的。
Linux 使用一种称为 SysVinit 的特殊引导架构,它基于 运行级别 (run-level)的概念而构建。 不同系统的 SysVinit 可能会区别很大, 因此不能假设那些在某个 Linux 发行版上正常工作的方法也能在 LFS 正常工作。LFS 有自己的处事原则,但它也遵守被广泛接受的标准。
SysVinit(之后简称为 “init”)使用运行级别架构工作。 有七个(编号为 0 到
6)运行级别(实际上还有更多, 但它们用于一些特殊情况,一般并不使用。参阅 init(8)
了解更多细节), 每个都对应于计算机在启动时应该进行的一组操作。
默认运行级别是 3,下面是不同运行级别的描述:
0: 停止系统运行
1: 单用户模式
2: 没有网络的多用户模式
3: 有网络的多用户模式
4: 保留用于自定义,如果没有自定义,和 3 相同
5: 和 4 相同,一般用于 GUI 登录 (如 X 的 xdm 或 KDE 的 kdm)
6: 重启计算机
在内核初始化过程中,第一个运行的程序要么是内核命令行中指定的程序, 要么默认为 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:S016:once:/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
文件末尾。
通过运行 init <runlevel>
可以切换运行级别, 这里 <runlevel>
是要切换到的运行级别。 例如,如果要重新启动计算机,用户可以使用 init 6 命令, 它和
reboot
作用相同。同样, init
0 和 halt 作用相同。
在 /etc/rc.d
中有一些名字类似
rc?.d
的目录(这里 ?
是运行级别编号),以及一个目录 rcsysinit.d
,
这些目录都包含一些符号链接。有的符号链接的名字以 K 开头,其他的则以 S 开头。它们的名字中, 第一个字符后都有两位数字。K
表示停止(杀死,kill)一个服务, 而 S 表示启动(start)一个服务。两位数字决定了运行这些脚本的顺序, 从
00 到 99 —— 数字较小的脚本更早执行。当 init
切换到另一个运行级别时,它会执行这些脚本,从而适当地启动或停止服务, 满足选择的运行级别要求。
符号链接实际指向的脚本位于 /etc/rc.d/init.d
,它们完成实际的工作。 K 链接和 S 链接指向
/etc/rc.d/init.d
中的相同脚本,这是因为脚本接受不同的参数,如 start
、stop
、 restart
、reload
以及 status
。当发现 K 链接时,脚本被传递
stop
参数。当发现 S 链接时,
对应的脚本被传递 start
参数。
以上解释有一个例外情况,当 rc0.d
和
rc6.d
目录中出现以 S 开头的链接时,它们不会启动任何服务。相反,它们被以参数
stop
调用,并停止服务。
这一行为背后的逻辑是,在重新启动系统或停止系统运行时,不需要启动任何服务, 只需要停止整个系统。
下面是脚本接受的不同参数及其解释:
start
启动服务。
stop
停止服务。
restart
停止服务,再重新启动它。
reload
更新服务配置。 当服务的配置文件被修改后,如果不需要重新启动服务, 就使用该参数。
status
报告服务是否在运行中。如果正在运行,报告其 PID。
您可以自由地修改引导过程的工作方式(毕竟这是您自己的 LFS 系统)。 我们给出的文件只是示例,用于展示它们可以完成的任务。
/etc/rc.d/init.d/udev
初始化脚本启动
udevd,
触发内核已经创建的“冷插拔”设备, 等待其 udev 规则执行完毕。该脚本也会取消默认的
uevent 处理程序 /sbin/hotplug
,
因为现在内核不再需要调用外部二进制程序,相反, udevd 会监听一个 netlink 套接字,
以获取内核发出的 uevent 事件。
某些子系统的 udev 规则依赖于一些直到 mount_fs
脚本被执行才会挂载的文件系统(如独立的 /usr
和
/var
文件系统就会导致这种现象) 初始化脚本
/etc/rc.d/init.d/udev_retry
负责重新触发这些子系统的事件。该脚本在 mountfs
后运行,因此这些规则(如果被重新触发)这一次应该能够成功执行。 配置文件 /etc/sysconfig/udev_retry
中包含的除注释外的所有单词都会被认为是一个需要触发的子系统。 为了找到某个设备的子系统,执行 udevadm info --attribute-walk
<device>, 这里 <device> 是一个 /dev 或
/sys 中的绝对路径, 例如 /dev/sr0 或 /sys/class/rtc。
参阅 第 7.3.2.3 节 “模块加载”, 了解更多关于模块加载和 udev 的信息。
setclock
脚本从硬件时钟读取时间, 硬件时钟又被称为 BIOS 时钟或互补金属氧化物半导体(CMOS)时钟。 如果硬件时钟被设为
UTC 时间,该脚本会根据 /etc/localtime
文件(它告知 hwclock
程序用户处于哪个时区), 将硬件时钟的时间转换成本地时间。不存在确定硬件时钟是否设为 UTC 的方法,
因此这必须手动设置。
在引导后,内核检测硬件功能时,setclock 脚本被 udev 执行。可以用 stop 参数手动调用它, 以将系统时间写入 CMOS 时钟。
如果您不确定您的硬件时钟是否设置为 UTC ,运行 hwclock --localtime --show
命令, 它会显示硬件时钟给出的当前时间。如果这个时间和您的手表显示的一致, 则说明硬件时钟被设定为本地时间。相反,如果
hwclock
输出的时间不是本地时间,则硬件时钟很可能被设定为 UTC 时间。 根据您的时区,在 hwclock
显示的时间上加减对应的小时数,进行进一步的验证。 例如,如果您现在处于莫斯科时区,即 GMT -0700 ,在本地时间上加
7 小时,再进行比较。
如果硬件时钟没有设为 UTC 时间,
在下面的配置文件中,应该将 UTC
变量的值设为
0
。
Windows 会将硬件时钟设定为本地时间,因此如果您要同时安装 Windows 和 Linux,就要将 Linux (包括 LFS 和其他发行版) 配置为使用本地时间,除非能够忍受 Windows 和 Linux 显示的不同时间 (必有一个是错误的)。
执行以下命令,创建一个新的 /etc/sysconfig/clock
文件:
cat > /etc/sysconfig/clock << "EOF"
# Begin /etc/sysconfig/clock
UTC=1
# 将该变量设置为您希望传递给 hwclock 命令的选项,
# 例如 Alpha 机器上的硬件时钟类型。
CLOCKPARAMS=
# End /etc/sysconfig/clock
EOF
访问
http://www.linuxfromscratch.org/hints/downloads/files/time.txt
, 可以找到一个关于如何在 LFS 系统中处理时间问题的好的提示。 它解释了时区、UTC 和 TZ
环境变量等问题。
CLOCKPARAMS 和 UTC 参数也可在 /etc/sysconfig/rc.site
文件中设定。
本节讨论如何配置 console 启动脚本,
使之正确设定键盘映射、控制台字体和控制台内核日志级别。 如果不使用非 ASCII 字符(如版权符号、英镑或欧元符号),
且键盘是美式的,则可以跳过本节。如果不创建本节的配置文件 (且 rc.site
中也没有对应的设置), 则 console 脚本什么也不做。
console 脚本读取
/etc/sysconfig/console
文件中的配置信息, 据此确定使用哪种键映射和控制台字体。一些与特定语言相关的 HOWTO 文档可以帮助您进行配置,参阅
http://www.tldp.org/HOWTO/HOWTO-INDEX/other-lang.html。
如果仍然存在疑问,在 /usr/share/keymaps
和
/usr/share/consolefonts
目录中寻找可用的键映射和屏幕字体,并阅读 loadkeys(1)
和 setfont(8)
man 手册页面, 以确认应该传递给这两个程序的正确参数。
/etc/sysconfig/console
文件应当包含若干行,它们的格式为:变量名="值"。下列变量名会被识别:
该变量指定被发送到终端的内核消息日志等级, 正如使用 dmesg -n 设置的那样。 有效的等级是 "1"(不输出消息) 到 "8" 之间的某个数, 默认值是 "7"。
该变量指定传递给 loadkeys 程序的参数, 通常为希望加载的键映射表名,例如 “it”。 如果没有设置这个变量,启动脚本不会运行 loadkeys, 并使用默认键映射。注意某些键映射表有名字相同的不同版本 (如 cz 及其变式在 qwerty/ 和 qwertz/ 中同时存在, es 在 olpc/ 和 qwerty/ 中同时存在,trf 在 fgGIod/ 和 qwerty/ 中同时存在),此时就要把包含键映射表文件的目录名也写在该变量中 (如 qwerty/es),才能保证加载正确的键映射。
这个(很少使用的)变量指定第二次调用 loadkeys 程序时的参数。 如果您希望对现成的,但不完全符合要求的键映射表进行微调, 这个变量很有用。例如,为了在不包含欧元符号的键映射中附加它, 可以设定该变量为 “euro2”。
该变量指定传递给 setfont 程序的参数。 它一般包含字体名、“-m” 以及需要加载的应用字符映射名, 例如,如果要加载 “lat1-16” 字体,以及 “8859-1” 应用字符映射(它适用于美国), 就将该变量设置为 “lat1-16 -m 8859-1”。 在 UTF-8 模式下,内核根据应用字符映射,将键映射中的 8 位键码组合转换成 UTF-8,因此 “-m” 参数的值应该被设为键映射中键码组合的编码。
将该变量设置为 “1”、“yes” 或 “true”,可以将终端设置为 UTF-8 模式。 这对于基于 UTF-8 的 locale 来说很有用,但对于其他 locale 有害。
对于许多键盘布局,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-15 到 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-5.2.8 只有在被组合的不是多字节字符的情况下, 才能正常解析死键和组合规则。这个缺陷不影响欧洲语言的键映射, 因为在欧洲语言中要么是一个音调被附加到不带音调的 ASCII 字符上, 要么是两个 ASCII 字符被组合在一起。然而,在 UTF-8 模式中, 以希腊语为例,当某人要在 “alpha” 字符上附加一个音调时, 就会出现问题。解决方法是要么不使用 UTF-8,要么安装 X 窗口系统, 它处理输入时没有这个限制。
对于中文、日文、韩文以及其他一些语言文字, 不可能配置 Linux 终端,使其正常显示需要的字符。 这些语言的用户需要安装 X 窗口系统、能够覆盖需要的字符的字体, 以及合适的输入法(如 SCIM 支持许多语言的输入)。
/etc/sysconfig/console
文件只控制
Linux 字符终端的本地化,它和 X 窗口系统、ssh 连接、
串口等其他终端中的键盘布局设置和终端字体毫无关系。在这些情况下, 不存在上面描述的两项限制。
有时,我们希望在引导时创建一些文件,例如 /tmp/ICE-unix
目录。 为此,可以在 /etc/sysconfig/createfiles
配置脚本中创建一项,该文件的格式包含在默认配置文件的注释中。
sysklogd
脚本启动 syslogd 程序,它是 System V
初始化的一部分。 -m 0
选项关闭
sysklogd 每 20
分钟写入日志文件的周期性时间戳,如果您希望启用该时间戳, 编辑 /etc/sysconfig/rc.site
, 将 SYSKLOGD_PARMS
定义为您希望的值。例如,如果要删除所有参数, 将该变量设定为空:
SYSKLOGD_PARMS=
参阅 man
syslogd
了解更多可用选项。
可选的 /etc/sysconfig/rc.site
文件包含为每个 System V 启动脚本自动设定的设置。 /etc/sysconfig/
目录中 hostname
、console
以及 clock
文件的变量值也可以在 rc.site
中设置。 如果某个变量存在于这三个文件当中,它们会覆盖
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@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 seet 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=
LFS 启动脚本能够较为高效地引导和关闭系统, 但您仍然可以进行一些调整,进一步提高速度,
并根据您的喜好修改屏幕显示的消息。为此,需要修改上面给出的 /etc/sysconfig/rc.site
文件。
在启动脚本 udev
的运行过程中,它执行
udev
settle 命令,
该命令需要较长时间才能完成。然而这个执行时间或许不是必要的, 如果您只有简单分区,且仅有一块网卡,
那么引导过程可能不需要等待该命令完成。设定变量 OMIT_UDEV_SETTLE=y,可以跳过该命令。
启动脚本 udev_retry
默认情况下也会执行 udev
settle。 一般来说,该命令只有 /var
目录是一个单独的挂载点时才必要,这是因为系统时钟需要文件
/var/lib/hwclock/adjtime
。
其他的定制也可能导致该脚本需要等待 udev 完成, 但在绝大多数 LFS 系统中没有这个必要。 设定变量
OMIT_UDEV_RETRY_SETTLE=y 跳过这一命令。
默认情况下,文件系统检查是安静(没有输出)的。 因此,在引导过程中,检查过程可能看上去是一个很长的时延。 设置变量 VERBOSE_FSCK=y 可以打开 fsck 的输出。
在重启系统时,您可能希望完全跳过文件系统检查命令 fsck。为此,可以创建文件
/fastboot
,或使用命令
/sbin/shutdown -f -r
now 重启系统。 另一方面,您可以创建 /forcefsck
文件, 或使用 -F
参数(而不是 -f
)运行 shutdown,
在重启系统时强制检查所有文件系统。
设定变量 FASTBOOT=y 会完全禁止引导过程中 fsck 的执行,直到该变量被删除。不建议永久使用该变量。
正常情况下, /tmp
中的所有文件在引导时都被删除。如果其中的文件和目录较多, 可能在引导过程中造成可观的延迟。设定变量
SKIPTMPCLEAN=y 可以跳过删除这些文件的过程。
在系统关闭的过程中,init 向它启动的所有程序(如 getty)发送一个 TERM 信号,等待一段时间 (默认 3 秒),再向每个进程发送 KILL 信号,之后再次等待。 在 sendsignals 脚本中, 以上过程会对那些没有被自己的引导脚本关闭的进程再次执行。 init 的时延可以通过传递参数设定, 为了完全取消时延,可以在关闭或重启系统时使用 -t0 参数 (如 shutdown -t0 -r now)。 sendsignals 脚本的时延可以通过设定参数 KILLDELAY=0 跳过。
Shell 程序 /bin/bash (之后简称 shell)
使用一组启动文件,以帮助创建运行环境。每个文件都有专门的用途, 可能以不同方式影响登录和交互环境。 /etc
目录中的文件提供全局设定,
如果在用户主目录中有对应的文件存在,它可能覆盖全局设定。
在使用 /bin/login
成功登录后, 它读取 /etc/passwd
中的 shell
命令行, 启动一个交互式登录 shell。通过命令行(如 [prompt]$
/bin/bash) 可以启动一个交互式非登录
shell。非交互 shell 通常在 shell 脚本运行时存在,
它处理脚本,在执行命令时不需要等待用户输入,因此是非交互的。
阅读 info bash 中 Bash Startup Files and Interactive Shells 一节, 了解更多信息。
文件 /etc/profile
和 ~/.bash_profile
在 shell 作为交互登录 shell 时被读取。
下面给出的基本 /etc/profile
设定本地语言支持需要的环境变量,正确设定它们有以下好处:
程序输出被翻译成本地语言
字符被正确分类为字母、数字和其他类别, 这对于使 bash 正确接受命令行中的非 ASCII 本地字符来说是必要的
根据所在地区惯例排序字母
适用于所在地区的默认纸张尺寸
正确格式化货币、时间和日期值
译者强烈反对在 /etc/profile
中指定中文
locale。由于字符终端的局限性,它根本无法显示中文字符。 使用中文 locale 将导致许多程序输出中文消息,
结果在字符终端中都显示为问号。建议在 /etc/locale.conf
中指定英文 locale , 在 BLFS
中安装了图形环境后再将 LANG=zh_CN.UTF-8
写入
.xprofile
等图形界面配置文件,
这样即可在字符终端中使用英文 locale ,而在图形界面中使用中文。
将下面的 <ll>
替换为所需语言的双字符代号(例如 “en”), <CC>
替换为国家或地区的双字符代号 (例如
“GB”),
<charmap>
替换为您选定的 locale 的标准字符映射。另外,还可以加入 “@euro” 之类的可选修饰符。
Glibc 支持的所有 locale 可以用以下命令列出:
locale -a
字符映射可能有多个别名,例如 “ISO-8859-1” 也可以称为 “iso8859-1” 和
“iso88591”。 某些程序不能正确处理一些别名(例如,只识别
“UTF-8”,
不能识别 “utf8”),因此在多数情况下,为了保险起见, 最好使用 locale
的规范名称。为了确定规范名称,执行以下命令, 将 <locale name>
替换成
locale -a 对于您希望的
locale 的输出 (以 “en_GB.iso88591” 为例)。
LC_ALL=<locale name>
locale charmap
对于 “en_GB.iso88591” locale,以上命令输出:
ISO-8859-1
这样就最终确定 locale 应设置为 “en_GB.ISO-8859-1”。 在将以上启发方法获得的 locale 添加到 Bash 启动文件之前, 一定要进行下列测试:
LC_ALL=<locale name> locale language LC_ALL=<locale name> locale charmap LC_ALL=<locale name> locale int_curr_symbol LC_ALL=<locale name> locale int_prefix
以上命令应该输出语言名称、选定 locale 使用的字符编码、 本地货币符号以及所在国家或地区的国际电话区号。 如果以上某个命令失败并输出类似下面这样的消息, 意味着您的 locale 在第 6 章中没有安装,或者不被 Glibc 的默认安装支持。
locale: Cannot set LC_* to default locale: No such file or directory
如果出现了这种消息,您应该用 localedef 命令安装所需的 locale ,或重新选择一个不同的 locale 。 后文假设 Glibc 没有输出类似错误消息。
某些 LFS 以外的软件包可能缺乏对您选择的 locale 的支持, 例如 X 库(X 窗口系统的一部分),它在您的 locale 与它内部文件中的字符映射表名不完全匹配时,会输出以下错误消息:
Warning: locale not supported by Xlib, locale set to C
某些情况下 Xlib 期望字符映射以带有规范连字符的大写形式给出, 例如应该使用 “ISO-8859-1” 而不是 “iso88591”。 有时也可以通过去除 locale 规范中的字符映射部分找到合适的规范, 可以通过运行 locale charmap 确认。 例如,您需要将 “de_DE.ISO-8859-15@euro” 替换成 “de_DE@euro”,以获得 Xlib 能够识别的 locale。
其他软件包在 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 位字符集,并且将最高位为 1 的字节视为无效字符。因此,ls 等命令会将它们替换为问号。另外,如果试图用 Mutt 或 Pine 发送包含这些字符的邮件,会发出不符合 RFC 标准的消息 (发出邮件的字符集会被标为 “未知 8 位”)。 因此,您只能在确信自己永远不会使用 8 位字符时才能使用 “C” locale。
许多程序不能很好地支持基于 UTF-8 的 locale, 我们正在努力记录并(如果可能的话)修复它们,参阅 http://www.linuxfromscratch.org/blfs/view/9.0/introduction/locale-issues.html。
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
# 由 Chris Lynn <roryo@roryo.dynup.net> 编辑
# 允许命令行折叠到下一行
set horizontal-scroll-mode Off
# 允许 8 位输入
set meta-flag On
set input-meta On
# 不移除第 8 位
set convert-meta Off
# 显示时保留第 8 位
set output-meta On
# 可以设为 none,visible 或 audible
set bell-style none
# 以下所有命令将第一参数中包含的 escape 序列
# 映射为对应的 readline 功能
"\eOd": backward-word
"\eOc": forward-word
# 用于 linux 终端
"\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
# 用于 xterm
"\eOH": beginning-of-line
"\eOF": end-of-line
# 用于 Konsole
"\e[H": beginning-of-line
"\e[F": end-of-line
# End /etc/inputrc
EOF
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
现在应该配置 LFS 系统,使其可以引导了。 本章讨论创建 fstab
文件、 为新的 LFS 系统构建内核,以及安装 GRUB 引导加载器,
使得系统引导时可以选择进入 LFS 系统。
一些程序使用 /etc/fstab
文件,
以确定哪些文件系统是默认挂载的,和它们应该按什么顺序挂载, 以及哪些文件系统在挂载前必须被检查(确定是否有完整性错误)。
参考以下命令,创建一个新的文件系统表:
cat > /etc/fstab << "EOF"
# Begin /etc/fstab
# 文件系统 挂载点 类型 选项 转储 检查
# 顺序
/dev/<xxx>
/ <fff>
defaults 1 1
/dev/<yyy>
swap swap pri=1 0 0
proc /proc proc nosuid,noexec,nodev 0 0
sysfs /sys sysfs nosuid,noexec,nodev 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
tmpfs /run tmpfs defaults 0 0
devtmpfs /dev devtmpfs mode=0755,nosuid 0 0
# End /etc/fstab
EOF
将 <xxx>
、
<yyy>
和
<fff>
替换为适用于您的系统的值,例如 sda2
、sda5
和 ext4
。
参阅 man 5 fstab
了解该文件中 6 个域的详细信息。
在挂载来源于 MS-DOS 或 Windows 的文件系统 (如
vfat、ntfs、smbfs、cifs、iso9660、udf)时, 需要一个特殊的挂载选项 —— utf8,
才能正常解析文件名中的非 ASCII 字符。对于非 UTF-8 locale,选项 iocharset
的值应该和您的 locale 字符集设定一致,但改写成内核可以识别的写法。
该选项能够正常工作的前提是,将相关的字符集定义(在内核配置选项的 File Systems -> Native
Language Support 子菜单中) 编译到内核中,或构建为内核模块。然而,如果使用了 UTF-8 locale,
对应的 iocharset=utf8
会导致文件系统变得大小写敏感。
为了避免这个问题,在使用 UTF-8 locale 时, 需要用特殊选项 utf8
代替 iocharset=utf8
。 另外,vfat 和 smbfs 文件系统还需要
“codepage” 选项, 它应该被设定为您的语言在 MS-DOS
下的代码页编号。 例如,为了挂载一个 USB 闪存盘,一个 ru_RU.KOI8-R 用户应该在 /etc/fstab
中对应于闪存盘的行添加下列挂载选项:
noauto,user,quiet,showexec,codepage=866,iocharset=koi8r
相应的,ru_RU.UTF-8 用户应该使用下列选项:
noauto,user,quiet,showexec,codepage=866,utf8
注意此时使用的 iocharset
默认为 iso8859-1
(这保证文件系统是大小写不敏感的), 而 utf8
选项告诉内核使用 UTF-8 编码转换文件名, 这样它们就能在 UTF-8
locale 中被正确解析。
也可以在内核配置中, 为一些文件系统指定默认 codepage 和 iocharset 选项值。 相关的配置参数名为
“Default NLS
Option” (CONFIG_NLS_DEFAULT
)、 “Default Remote NLS
Option” (CONFIG_SMB_NLS_DEFAULT
)、 “Default codepage for
FAT” (CONFIG_FAT_DEFAULT_CODEPAGE
) 和 “Default iocharset for
FAT” (CONFIG_FAT_DEFAULT_IOCHARSET
)。 无法在编译内核时为 ntfs
文件系统指定这些默认值。
在某些硬盘上,在 /etc/fstab
中添加
barrier=1
挂载选项, 可以使得 ext3
文件系统在发生电源故障时更可靠。 为了检查磁盘驱动器是否支持该选项,在可用的磁盘驱动器上运行
hdparm。 例如:
hdparm -I /dev/sda | grep NCQ
如果输出内容不为空,说明该选项可用。
注意:基于逻辑卷管理(LVM)的分区不能使用 barrier
选项。
Linux 软件包包含 Linux 内核。
构建内核需要三步 —— 配置、编译、安装。 阅读内核源代码树中的 README
文件, 了解不同于本手册的内核配置方法。
运行以下命令,准备编译内核:
make mrproper
该命令确保内核源代码树绝对干净, 内核开发组建议在每次编译内核前运行该命令。 尽管内核源代码树在解压后应该是干净的,但这并不完全可靠。
下面通过菜单界面配置内核,阅读 http://www.linuxfromscratch.org/hints/downloads/files/kernel-configuration.txt 了解关于内核配置的一般信息。 BLFS 的某些软件包需要特定内核配置,阅读 http://www.linuxfromscratch.org/blfs/view/9.0/longindex.html#kernel-config-index 了解它们。另外在 http://www.kroah.com/lkn 也有一些关于配置和构建内核的信息。
一个较好的初始内核配置可以通过运行 make defconfig 获得。 它会考虑您的当前系统体系结构,将基本内核配置设定到较好的状态。
一定要按照以下列表启用/禁用/设定其中列出的内核特性, 否则系统可能不能正常工作,甚至根本无法引导:
Device Drivers ---> Generic Driver Options ---> [ ] Support for uevent helper [CONFIG_UEVENT_HELPER] [*] Maintain a devtmpfs filesystem to mount at /dev [CONFIG_DEVTMPFS] Kernel hacking ---> Choose kernel unwinder (Frame pointer unwinder) ---> [CONFIG_UNWINDER_FRAME_POINTER]
There are several other options that may be desired depending on the requirements for the system. For a list of options needed for BLFS packages, see the BLFS Index of Kernel Settings (http://www.linuxfromscratch.org/blfs/view/9.0/longindex.html#kernel-config-index).
如果您的硬件平台使用 UEFI,则 make defconfig 命令也会自动加入一些 EFI 相关的内核选项。
为了允许从宿主系统的 UEFI 引导环境引导 LFS 内核, 您必须选择一个内核选项:
Processor type and features ---> [*] EFI stub support [CONFIG_EFI_STUB]
在 LFS 中管理 UEFI 环境的较完整说明包含在 lfs-uefi.txt 中, 它位于 http://www.linuxfromscratch.org/hints/downloads/files/lfs-uefi.txt。
上述配置选项的含义:
Support
for uevent helper
如果启用了该选项,它可能干扰 Udev/Eudev 的设备管理。
Maintain a
devtmpfs
该选项会使内核自动创建设备节点,即使 Udev 没有运行。 Udev 之后才在这些设备节点的基础上运行, 管理它们的访问权限并为它们建立符号链接。 所有 Udev/Eudev 用户都需要启用该选项。
make menuconfig
以上命令中可选的 make 环境变量及含义:
LANG=<host_LANG_value>
LC_ALL=
它们根据宿主使用的 locale 建立 locale 设定。 在 UTF-8 Linux 文本终端下,有时必须这样做才能正确绘制 基于 ncurses 的配置菜单接口。
在这种情况下,一定要将 <host_LANG_value>
替换成宿主环境中的 $LANG
变量值。您也可以使用宿主环境中 $LC_ALL
或
$LC_CTYPE
的值代替。
某些情况下,make
oldconfig 更为合适。 阅读 README
文件了解更多信息。
如果希望的话,也可以将宿主系统的内核配置文件 .config
拷贝到解压出的 linux-5.2.8
目录(前提是可以找到该文件)。然而我们不推荐这样做, 一般来说,浏览整个配置目录,并从头创建内核配置是更好的选择。
编译内核映像和模块:
make
如果要使用内核模块,可能需要在 /etc/modprobe.d
中写入模块配置。讨论模块和内核配置的信息位于 第 7.3 节 “设备和模块管理概述” 和
linux-5.2.8/Documentation
目录下的内核文档中。另外 modprobe.d(5)
也可以作为参考。
如果内核配置使用了模块,安装它们:
make modules_install
在内核编译完成后,需要进行额外步骤完成安装, 一些文件需要拷贝到 /boot
目录中。
如果宿主系统有单独的 /boot 分区,需要将这些文件拷贝到该分区中。 最简单的方法是将宿主系统的 /boot (在 chroot 之外)绑定到 /mnt/lfs/boot 再拷贝文件,在宿主系统中, 以 root 身份执行:
mount --bind /boot /mnt/lfs/boot
指向内核映像的路径可能随机器平台的不同而变化。 下面使用的文件名可以依照您的需要改变, 但文件名的开头应该保持为 vmlinuz, 以保证和下一节描述的引导过程自动设定相兼容。 下面的命令假定是机器是 x86 体系结构:
cp -iv arch/x86/boot/bzImage /boot/vmlinuz-5.2.8-lfs-9.0
System.map
是内核符号文件, 它将内核 API
的每个函数入口点和运行时数据结构映射到它们的地址。 它在调查分析内核出现的问题时被使用。执行以下命令安装该文件:
cp -iv System.map /boot/System.map-5.2.8
make menuconfig
生成的内核配置文件 .config
包含编译好的内核的所有配置选项。 最好能将它保留下来以供日后参考:
cp -iv .config /boot/config-5.2.8
安装 Linux 内核文档:
install -d /usr/share/doc/linux-5.2.8 cp -r Documentation/* /usr/share/doc/linux-5.2.8
需要注意的是,在内核源代码目录中可能有不属于 root 的文件。在以 root 身份解压源代码包时(就像我们在 chroot 环境中所做的那样), 这些文件会获得它们之前在软件包创建者的计算机上的用户和组 ID。 这一般不会造成问题,因为在安装后通常会删除源代码目录树。 然而,Linux 源代码目录树一般会被保留较长时间, 这样创建者当时使用的用户 ID 就可能被分配给本机的某个用户, 导致该用户拥有内核源代码的写权限。
之后在 BLFS 中安装软件包时往往需要修改内核配置。 因此,和其他软件包不同,我们在安装好内核后可以不移除源代码树。
如果要保留内核源代码树,对目录 linux-5.2.8
执行 chown -R
0:0,以保证其中所有文件都属于 root。
有的内核文档建议创建符号链接 /usr/src/linux
指向内核源代码目录,这仅仅适用于 2.6 系列之前的内核。 在 LFS 系统上绝对不要创建它, 因为在构建完基本 LFS
系统后,它可能在您构建其他软件包时引起问题。
在系统 include
目录(即 /usr/include
中的内核头文件应该总是与构建 Glibc 时使用的内核头文件一致,即保持为
第 6.7 节
“Linux-5.2.8 API 头文件” 中安装的净化头文件。 换句话说,永远不要用原始内核头文件, 或其他版本内核的净化头文件替换它们。
多数情况下 Linux 内核模块可以自动加载,但有时需要指定加载顺序。 负责加载内核模块的程序 modprobe 和 insmod 从 /etc/modprobe.d
下的配置文件中读取加载顺序,例如,如果 USB
驱动程序 (ehci_hcd、ohci_hcd 和 uhci_hcd)被构建为模块, 则必须按照先加载 echi_hcd
,再加载 ohci_hcd 和 uhci_hcd 的正确顺序, 才能避免引导时出现警告信息。
为此,执行以下命令创建文件 /etc/modprobe.d/usb.conf
:
install -v -m755 -d /etc/modprobe.d
cat > /etc/modprobe.d/usb.conf << "EOF"
# Begin /etc/modprobe.d/usb.conf
install ohci_hcd /sbin/modprobe ehci_hcd ; /sbin/modprobe -i ohci_hcd ; true
install uhci_hcd /sbin/modprobe ehci_hcd ; /sbin/modprobe -i uhci_hcd ; true
# End /etc/modprobe.d/usb.conf
EOF
如果您不小心错误地配置了 GRUB,而且没有 CD-ROM 之类的备用引导设备,可能会导致您的系统完全无法使用。 本节不是引导您的 LFS 系统的唯一方案, 您可能只要修改现有的启动加载器(如 Grub-Legacy、GRUB2 或 LILO) 配置即可引导 LFS。
您务必保证自己拥有一个紧急引导磁盘,它在计算机不可用(无法引导) 时能够 “救援” 计算机。如果您现在还没有引导设备, 您可以执行以下命令创建一个。在运行下列命令前,您需要跳到 BLFS, 安装包含 xorriso 的 libisoburn 软件包:
cd /tmp grub-mkrescue --output=grub-img.iso xorriso -as cdrecord -v dev=/dev/cdrw blank=as_needed grub-img.iso
为了在使用 UEFI 的宿主系统上引导 LFS ,在构建内核时, 需要启用 CONFIG_EFI_STUB 功能,正如上一节所述。 不过,可以使用 GRUB2 在没有该功能的情况下引导 LFS, 前提是在系统 BIOS 设定中关闭 UEFI 模式和安全引导 (Secure Boot)功能。关于在 UEFI 环境下引导 LFS 的的详细信息, 可以参阅 http://www.linuxfromscratch.org/hints/downloads/files/lfs-uefi.txt。
GRUB 使用一种独特的命名结构,为驱动器和分区命名。 分区名的形式为 (hdn,m),这里 n 是硬盘驱动器编号,m 是分区编号。硬盘驱动器编号从 0 开始,但分区号对于主分区来说从
1 开始,而对于扩展分区来说从 5 开始。例如,分区 sda1
在 GRUB 中的名字是 (hd0,
1),而 sda3
的名字是
(hd1, 3)。和 Linux 不同,
GRUB 不认为 CD-ROM 驱动器属于硬盘驱动器。例如,如果在 hdb
上有一个 CD-ROM 驱动器, 而 hdc
上有一个次要硬盘驱动器,则第二个驱动器仍然名为 hd1。
GRUB 的工作方式是,将数据写入硬盘的第一个物理磁道。 这里不属于任何文件系统,在启动时, 第一个物理磁道中的程序从引导分区加载 GRUB 模块, 默认在 /boot/grub 中查找模块。
引导分区的位置由负责进行配置的用户自己决定, 作者推荐创建一个小的(建议大小为 100 MB)分区,
专门存放引导信息。这样,不同的 Linux 系统(无论是 LFS 还是商业发行版)在启动时和启动后都能访问相同的引导文件。
如果您选择这样做,您需要挂载这个单独的分区,将 /boot 中已有的文件(例如上一节中构建的内核)移动到新的分区中。
之后,解除该分区的挂载,并将它挂载为 /boot
,
而且还要注意更新 /etc/fstab
。
直接使用 LFS 分区也是可以的,但这样在配置多系统启动时比较麻烦。
根据以上信息,确定 LFS 根分区(或 boot 分区)的名称。 下面假设 LFS 根分区(或 boot 分区)是
sda2
。
将 GRUB 文件安装到/boot/grub
并设定引导磁道:
以下命令会覆盖当前启动引导器,如果这不是您希望的, 不要运行该命令。例如,如果您使用第三方启动引导器管理主引导记录 (MBR)。
grub-install /dev/sda
如果系统是使用 UEFI 引导的, grub-install 会试图为
x86_64-efi 目标安装文件,
但它们并未在第 6 章中安装。如果出现了这类问题,请在以上命令中添加 --target i386-pc
选项。
生成 /boot/grub/grub.cfg
:
cat > /boot/grub/grub.cfg << "EOF"
# Begin /boot/grub/grub.cfg
set default=0
set timeout=5
insmod ext2
set root=(hd0,2)
menuentry "GNU/Linux, Linux 5.2.8-lfs-9.0" {
linux /boot/vmlinuz-5.2.8-lfs-9.0 root=/dev/sda2 ro
}
EOF
从 GRUB的视角来看, 内核文件的位置相对于它使用的分区。如果您使用了单独的 /boot 分区,需要从上面的 linux 行删除 /boot, 然后修改 set root 行, 指向 /boot 分区。
GRUB 是一个很强大的程序,它提供了非常多的选项, 可以支持多种设备、操作系统和分区类型,还有很多用于定制启动屏幕、 声音、鼠标输入等的选项。这些选项的细节超过了本书的范围, 不予讨论。
有一个命令 grub-mkconfig 被用于自动创建配置文件。它使用 /etc/grub.d 中的脚本创建新配置文件,这会覆盖您手动编写的 grub.cfg。 这些脚本主要是为非源代码发行版设计的, 在 LFS 中不推荐使用。但是,如果您安装了商业发行版, 它很可能在发行版中被运行,记得备份 grub.cfg 以防它被覆盖。
很好!现在新的 LFS 系统已经安装好了!我们祝愿您在全新的, 自定义的 Linux 系统中取得成功!
It may be a good idea to create an /etc/lfs-release
file. By having this file,
it is very easy for you (and for us if you need to ask for help
at some point) to find out which LFS version is installed on
the system. Create this file by running:
echo 9.0 > /etc/lfs-release
另外,最好创建一个文件,根据 Linux Standards Base (LSB) 的规则显示系统状态。运行以下命令创建该文件:
cat > /etc/lsb-release << "EOF" DISTRIB_ID="Linux From Scratch" DISTRIB_RELEASE="9.0" DISTRIB_CODENAME="<your name here>" DISTRIB_DESCRIPTION="Linux From Scratch" EOF
您可以修改 'DISTRIB_CODENAME' 域,体现您的系统的独特性。
现在您已经完成了本书中的构建过程,那么问题来了,您希望被计为一名 LFS 用户吗?前往 http://www.linuxfromscratch.org/cgi-bin/lfscounter.php 并输入您的姓名和第一次使用的 LFS 版本,即可注册成为 LFS 用户。
下面重启计算机进入 LFS。
现在已经安装好了本书中的所有软件,可以重新启动进入 LFS 了。 然而,您应该注意,按照本书的指令构建的系统是很小的, 可能会缺失一些功能,导致您无法继续使用系统。 通过在当前的 chroot 环境中安装一些额外的 BLFS 软件包, 您可以在重启进入新安装的 LFS 后更容易继续工作。 以下是一些建议安装的软件包:
字符模式浏览器,例如 Lynx, 这样您可以在一个虚拟终端中阅读 BLFS 手册, 同时在另一个虚拟终端中构建 BLFS 软件包。
GPM 软件包允许您在虚拟终端中进行复制粘贴操作。
如果静态 IP 配置不符合您的网络环境要求, 安装 dhcpcd 或 dhcp 的客户端部分之类的软件包,用于 DHCP 客户端。
安装 sudo, 这样就能以非 root 身份构建软件包,再很容易地切换到 root 身份进行安装。
如果您想在拥有舒适的 GUI 环境的远程计算机访问新系统, 安装 openssh。
为了更方便地从网络中下载文件,安装 wget。
最后,再次检查以下配置文件:
/etc/bashrc
/etc/dircolors
/etc/fstab
/etc/hosts
/etc/inputrc
/etc/profile
/etc/resolv.conf
/etc/vimrc
/root/.bash_profile
/root/.bashrc
/etc/sysconfig/network
/etc/sysconfig/ifconfig.eth0
现在,终于可以引导全新的 LFS 系统了!首先退出 chroot 环境:
logout
解挂虚拟文件系统:
umount -v $LFS/dev/pts umount -v $LFS/dev umount -v $LFS/run umount -v $LFS/proc umount -v $LFS/sys
解挂 LFS 文件系统:
umount -v $LFS
如果建立了多个挂载点,在解挂主挂载点 LFS 前, 先解挂其他挂载点:
umount -v $LFS/usr umount -v $LFS/home umount -v $LFS
重新启动系统:
shutdown -r now
如果 GRUB 引导加载器如前文所述安装并配置正确, 它应该会自动引导 LFS 9.0 启动项。
重启后,LFS 系统就可以使用了,您可以安装更多的软件包, 以满足自己的需求。
感谢您阅读本书。我们希望您觉得本书对您有用, 而且您能够从构建系统的过程中学到很多知识。
现在 LFS 系统已经安装好了,您可能想问 “然后呢?” 为了解答这个问题,我们为您汇集了一份有用资源的列表。
维护
所有软件都会定期发布 Bug 报告和安全注意事项。 由于 LFS 系统是从源代码构建的,您必须自己留意它们。 有一些跟踪它们的在线网站,下面列出一些:
CERT (计算机应急响应小组)
CERT 有一个邮件列表,发布许多操作系统和应用程序的安全警告。 访问 http://www.us-cert.gov/cas/signup.html 阅读邮件列表订阅信息。
Bugtraq
Bugtraq 是一个计算机安全公示邮件列表, 它发布新发现的安全问题,偶尔还会提供可能的修复方式。 访问 http://www.securityfocus.com/archive 阅读订阅信息。
Beyond Linux From Scratch
BLFS 涵盖了许多不属于 LFS 范畴的软件的安装过程, 项目主页是 http://www.linuxfromscratch.org/blfs/。
LFS Hints
LFS Hints 是一组由 LFS 社区志愿者提交的帮助文档, 它位于 http://www.linuxfromscratch.org/hints/list.html。
邮件列表
如果您需要帮助,希望跟踪 LFS 开发进度, 或者希望参与该项目,访问 第 1.4.2 节 “邮件列表” 了解一下 LFS 邮件列表。
Linux 文档计划
Linux 文档计划(The Linux Documentation Project) 的目标是通过协作解决 Linux 文档的所有问题, 它包含大量 HOWTO 文档、指南和 man 页面。它的网址是 http://www.tldp.org/。
ABI |
Application Binary Interface 应用程序二进制接口 |
ALFS |
Automated Linux From Scratch |
API |
Application Programming Interface 应用程序编程接口 |
ASCII |
American Standard Code for Information Interchange 美国标准信息交换代码 |
BIOS |
Basic Input/Output System 基本输入输出系统 |
BLFS |
Beyond Linux From Scratch |
BSD |
Berkeley Software Distribution |
chroot |
change root 切换根目录 |
CMOS |
Complementary Metal Oxide Semiconductor 互补金属氧化物半导体 |
COS |
Class Of Service 服务类型 |
CPU |
Central Processing Unit 中央处理器 |
CRC |
Cyclic Redundancy Check 循环冗余检查 |
CVS |
Concurrent Versions System 并行版本系统 |
DHCP |
Dynamic Host Configuration Protocol 动态主机配置协议 |
DNS |
Domain Name Service 域名服务 |
EGA |
Enhanced Graphics Adapter 增强图形适配器 |
ELF |
Executable and Linkable Format 可执行与可链接格式 |
EOF |
End of File 文件结束符 |
EQN |
公式 |
ext2 |
第二代增强文件系统 |
ext3 |
第三代增强文件系统 |
ext4 |
第四代增强文件系统 |
FAQ |
Frequently Asked Questions 常见问题 |
FHS |
Filesystem Hierarchy Standard |
FIFO |
First-In, First Out 先进先出 |
FQDN |
Fully Qualified Domain Name 全限定域名 |
FTP |
File Transfer Protocol 文件传输协议 |
GB |
Gigabytes 吉字节 |
GCC |
GNU Compiler Collection GNU 编译器集合 |
GID |
Group Identifier 组标识符 |
GMT |
Greenwich Mean Time 格林尼治时间 |
HTML |
Hypertext Markup Language 超文本标记语言 |
IDE |
Integrated Drive Electronics |
IEEE |
Institute of Electrical and Electronic Engineers 国际电子电气工程师学会 |
IO |
Input/Output 输入输出 |
IP |
Internet Protocol 网络之间互连的协议 |
IPC |
Inter-Process Communication 进程间通信 |
IRC |
Internet Relay Chat 互联网中继聊天 |
ISO |
International Organization for Standardization 国际标准化组织 |
ISP |
Internet Service Provider 互联网服务提供者 |
KB |
Kilobytes 千字节 |
LED |
Light Emitting Diode 发光二极管 |
LFS |
Linux From Scratch |
LSB |
Linux Standard Base |
MB |
Megabytes 兆字节 |
MBR |
Master Boot Record 主引导记录 |
MD5 |
Message Digest 5 消息摘要算法第五版 |
NIC |
Network Interface Card 网络接口卡 |
NLS |
Native Language Support 自然语言支持 |
NNTP |
Network News Transport Protocol 网络新闻传输协议 |
NPTL |
Native POSIX Threading Library 原生 POSIX 线程库 |
OSS |
Open Sound System 开放音频系统 |
PCH |
Pre-Compiled Headers 预编译头文件 |
PCRE |
Perl Compatible Regular Expression Perl 兼容的正则表达式 |
PID |
Process Identifier 进程标识符 |
PTY |
pseudo terminal 伪终端 |
QOS |
Quality Of Service 服务质量 |
RAM |
Random Access Memory 随机访问内存 |
RPC |
Remote Procedure Call 远程过程调用 |
RTC |
Real Time Clock 实时时钟 |
SBU |
Standard Build Unit 标准构建单位 |
SCO |
The Santa Cruz Operation |
SHA1 |
Secure-Hash Algorithm 1 安全散列算法第一版 |
TLDP |
The Linux Documentation Project Linux 文档计划 |
TFTP |
Trivial File Transfer Protocol 简单文件传输协议 |
TLS |
Thread-Local Storage 线程本地存储 |
UID |
User Identifier 用户标识符 |
umask |
user file-creation mask 用户文件创建掩码 |
USB |
Universal Serial Bus 通用串行总线 |
UTC |
Coordinated Universal Time 协调世界时 |
UUID |
Universally Unique Identifier 通用唯一识别码 |
VC |
Virtual Console 虚拟终端 |
VGA |
Video Graphics Array 视频图形阵列 |
VT |
Virtual Terminal 虚拟终端 |
译者感谢 西电开源社区 提供的 LFS 软件包镜像 和 GIT 托管服务 ,以及 Linux 中国翻译组 翻译的 LFS-7.7-systemd 手册,这是译者初次阅读的 LFS 版本, 在翻译过程中译者也参考了该版本。
我们希望感谢以下人员和组织对 Linux From Scratch 项目作出的贡献:
Gerard Beekmans <gerard AT linuxfromscratch D0T org> – LFS 创始人
Bruce Dubbs <bdubbs AT linuxfromscratch D0T org> – LFS 执行编辑
Jim Gifford <jim AT linuxfromscratch D0T org> – CLFS 共同负责人
Pierre Labastie <pierre AT linuxfromscratch D0T org> – BLFS 编辑及 ALFS 负责人
DJ Lucas <dj AT linuxfromscratch D0T org> – LFS 和 BLFS 编辑
Ken Moffat <ken AT linuxfromscratch D0T org> – BLFS 编辑
在 LFS 和 BLFS 的相关邮件列表中还有无数朋友, 他们为本书进行提供了宝贵的建议,对本书中的安装说明进行了测试, 提供了 bug 报告和安装说明, 以及分享了在安装各种软件包时获得的宝贵经验。 他们的工作使得本书得以发布。
Manuel Canales Esparcia <macana AT macana-es D0T com> – 西班牙语 LFS 翻译项目
Johan Lenglet <johan AT linuxfromscratch D0T org> – 2008 年以前的 LFS 法语翻译项目
Jean-Philippe Mengual <jmengual AT linuxfromscratch D0T org> – 2008-2016 年的 LFS 法语翻译项目
Julien Lepiller <jlepiller AT linuxfromscratch D0T org> – 2017 年后的 LFS 法语翻译项目
Anderson Lizardo <lizardo AT linuxfromscratch D0T org> – 葡萄牙语 LFS 翻译项目
Thomas Reitelbach <tr AT erdfunkstelle D0T de> – 德语 LFS 翻译项目
Anton Maisak <info AT linuxfromscratch D0T org D0T ru> – 俄语 LFS 翻译项目
Elena Shevcova <helen AT linuxfromscratch D0T org D0T ru> – 俄语 LFS 翻译项目
Scott Kveton <scott AT osuosl D0T org> – lfs.oregonstate.edu 镜像站
William Astle <lost AT l-w D0T net> – ca.linuxfromscratch.org 镜像站
Eujon Sellers <jpolen@rackspace.com> – lfs.introspeed.com 镜像站
Justin Knierim <tim@idge.net> – lfs-matrix.net 镜像站
Manuel Canales Esparcia <manuel AT linuxfromscratch D0T org> – lfsmirror.lfs-es.info 镜像站
Luis Falcon <Luis Falcon> – torredehanoi.org 镜像站
Guido Passet <guido AT primerelay D0T net> – nl.linuxfromscratch.org 镜像站
Bastiaan Jacques <baafie AT planet D0T nl> – lfs.pagefault.net 镜像站
Sven Cranshoff <sven D0T cranshoff AT lineo D0T be> – lfs.lineo.be 镜像站
Scarlet Belgium – lfs.scarlet.be 镜像站
Sebastian Faulborn <info AT aliensoft D0T org> – lfs.aliensoft.org 镜像站
Stuart Fox <stuart AT dontuse D0T ms> – lfs.dontuse.ms 镜像站
Ralf Uhlemann <admin AT realhost D0T de> – lfs.oss-mirror.org 镜像站
Antonin Sprinzl <Antonin D0T Sprinzl AT tuwien D0T ac D0T at> – at.linuxfromscratch.org 镜像站
Fredrik Danerklint <fredan-lfs AT fredan D0T org> – se.linuxfromscratch.org 镜像站
Franck <franck AT linuxpourtous D0T com> – lfs.linuxpourtous.com 镜像站
Philippe Baque <baque AT cict D0T fr> – lfs.cict.fr 镜像站
Vitaly Chekasin <gyouja AT pilgrims D0T ru> – lfs.pilgrims.ru 镜像站
Benjamin Heil <kontakt AT wankoo D0T org> – lfs.wankoo.org 镜像站
Anton Maisak <info AT linuxfromscratch D0T org D0T ru> – linuxfromscratch.org.ru 镜像站
Satit Phermsawang <satit AT wbac D0T ac D0T th> – lfs.phayoune.org 镜像站
Shizunet Co.,Ltd. <info AT shizu-net D0T jp> – lfs.mirror.shizu-net.jp 镜像站
Init World <http://www.initworld.com/> – lfs.initworld.com 镜像站
Jason Andrade <jason AT dstc D0T edu D0T au> – au.linuxfromscratch.org 镜像站
Christine Barczak <theladyskye AT linuxfromscratch D0T org> – LFS 手册编辑
Archaic <archaic@linuxfromscratch.org> – LFS 技术作家/编辑, HLFS 项目领导者,BLFS 编辑,Hints 和补丁项目维护者
Matthew Burgess <matthew AT linuxfromscratch D0T org> – LFS 项目领导者,LFS 技术作家/编辑
Nathan Coulson <nathan AT linuxfromscratch D0T org> – LFS-Bootscripts 维护者
Timothy Bauscher
Robert Briggs
Ian Chilton
Jeroen Coumans <jeroen AT linuxfromscratch D0T org> – 网站开发者,FAQ 维护者
Manuel Canales Esparcia <manuel AT linuxfromscratch D0T org> – LFS/BLFS/HLFS XML 和 XSL 维护者
Alex Groenewoud – LFS 技术作家
Marc Heerdink
Jeremy Huntwork <jhuntwork AT linuxfromscratch D0T org> – LFS 技术作家,LFS LiveCD 维护者
Bryan Kadzban <bryan AT linuxfromscratch D0T org> – LFS 技术作家
Mark Hymers
Seth W. Klein – FAQ 维护者
Nicholas Leippe <nicholas AT linuxfromscratch D0T org> – Wiki 维护者
Anderson Lizardo <lizardo AT linuxfromscratch D0T org> – 网站后台脚本维护者
Randy McMurchy <randy AT linuxfromscratch D0T org> – BLFS 项目领导者,LFS 编辑
Dan Nicholson <dnicholson AT linuxfromscratch D0T org> – LFS 和 BLFS 编辑
Alexander E. Patrakov <alexander AT linuxfromscratch D0T org> – LFS 技术作家,LFS 国际化编辑,LFS LiveCD 维护者
Simon Perreault
Scot Mc Pherson <scot AT linuxfromscratch D0T org> – LFS NNTP 网关维护者
Douglas R. Reno <renodr AT linuxfromscratch D0T org> – Systemd 编辑
Ryan Oliver <ryan AT linuxfromscratch D0T org> – CLFS 项目共同负责人
Greg Schafer <gschafer AT zip D0T com D0T au> – LFS 技术作家, 下一代启用 64 位构建方法总设计师
Jesse Tie-Ten-Quee – LFS 技术作家
James Robertson <jwrober AT linuxfromscratch D0T org> – Bugzilla 维护者
Tushar Teredesai <tushar AT linuxfromscratch D0T org> – BLFS 手册编辑,Hints 和补丁计划领导者
Jeremy Utley <jeremy AT linuxfromscratch D0T org> – LFS 技术作家,Bugzilla 维护者,LFS-Bootscripts 维护者
Zack Winkles <zwinkles AT gmail D0T com> – LFS 技术作家
LFS 中构建的每个软件包都依赖于一个或多个其他软件包, 才能正确地构建和安装。某些软件包甚至存在循环依赖, 即第一个软件包依赖于第二个软件包,而第二个软件包反过来又依赖第一个。 由于这些依赖关系的存在,在 LFS 中构建软件包的顺序非常关键。 本页面的目的就是记录 LFS 中每个软件包构建时的依赖关系。
对于我们构建的每个软件包,我们都列出了三种甚至四种依赖关系。 第一种列出了编译和安装该软件包需要的其他软件包。 第二种列出了不属于第一种情况, 但在运行该软件包测试套件时需要的其他软件包。 第三种列出了在构建和安装前, 需要该软件包已经构建并安装到最终位置的其他软件包。 多数情况下,这是因为它们会在脚本中硬编码指向二进制程序的路径。 如果不按照特定顺序构建,则最终的系统中某个脚本可能包含路径 /tools/bin/[二进制程序],这显然是我们不希望的。
第四种列出的依赖关系是 LFS 中没有提到的可选软件包, 但它们对用户可能很有用。这些软件包本身可能还有必要或可选的依赖关系。 对于这些依赖关系,推荐的方法是在完成 LFS 手册后, 安装可选依赖项,再重新构建相关的 LFS 软件包。 BLFS 提到了几个软件包的重新安装方法。
本附录中的脚本按照它们通常位于的目录排序列出。 顺序是 /etc/rc.d/init.d
,/etc/sysconfig
,/etc/sysconfig/network-devices
,以及 /etc/sysconfig/network-devices/services
。
在每个目录中,脚本按正常情况下它们被调用的顺序排列。
rc
脚本是 init 调用的第一个脚本,它开始引导过程。
#!/bin/bash ######################################################################## # Begin rc # # Description : Main Run Level Control Script # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # : DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## . /lib/lsb/init-functions print_error_msg() { log_failure_msg # $i is set when called MSG="FAILURE:\n\nYou should not be reading this error message.\n\n" MSG="${MSG}It means that an unforeseen error took place in\n" MSG="${MSG}${i},\n" MSG="${MSG}which exited with a return value of ${error_value}.\n" MSG="${MSG}If you're able to track this error down to a bug in one of\n" MSG="${MSG}the files provided by the ${DISTRO_MINI} book,\n" MSG="${MSG}please be so kind to inform us at ${DISTRO_CONTACT}.\n" log_failure_msg "${MSG}" log_info_msg "Press Enter to continue..." wait_for_user } check_script_status() { # $i is set when called if [ ! -f ${i} ]; then log_warning_msg "${i} is not a valid symlink." SCRIPT_STAT="1" fi if [ ! -x ${i} ]; then log_warning_msg "${i} is not executable, skipping." SCRIPT_STAT="1" fi } run() { if [ -z $interactive ]; then ${1} ${2} return $? fi while true; do read -p "Run ${1} ${2} (Yes/no/continue)? " -n 1 runit echo case ${runit} in c | C) interactive="" ${i} ${2} ret=${?} break; ;; n | N) return 0 ;; y | Y) ${i} ${2} ret=${?} break ;; esac done return $ret } # Read any local settings/overrides [ -r /etc/sysconfig/rc.site ] && source /etc/sysconfig/rc.site DISTRO=${DISTRO:-"Linux From Scratch"} DISTRO_CONTACT=${DISTRO_CONTACT:-"lfs-dev@linuxfromscratch.org (Registration required)"} DISTRO_MINI=${DISTRO_MINI:-"LFS"} IPROMPT=${IPROMPT:-"no"} # These 3 signals will not cause our script to exit trap "" INT QUIT TSTP [ "${1}" != "" ] && runlevel=${1} if [ "${runlevel}" == "" ]; then echo "Usage: ${0} <runlevel>" >&2 exit 1 fi previous=${PREVLEVEL} [ "${previous}" == "" ] && previous=N if [ ! -d /etc/rc.d/rc${runlevel}.d ]; then log_info_msg "/etc/rc.d/rc${runlevel}.d does not exist.\n" exit 1 fi if [ "$runlevel" == "6" -o "$runlevel" == "0" ]; then IPROMPT="no"; fi # Note: In ${LOGLEVEL:-7}, it is ':' 'dash' '7', not minus 7 if [ "$runlevel" == "S" ]; then [ -r /etc/sysconfig/console ] && source /etc/sysconfig/console dmesg -n "${LOGLEVEL:-7}" fi if [ "${IPROMPT}" == "yes" -a "${runlevel}" == "S" ]; then # The total length of the distro welcome string, without escape codes wlen=${wlen:-$(echo "Welcome to ${DISTRO}" | wc -c )} welcome_message=${welcome_message:-"Welcome to ${INFO}${DISTRO}${NORMAL}"} # The total length of the interactive string, without escape codes ilen=${ilen:-$(echo "Press 'I' to enter interactive startup" | wc -c )} i_message=${i_message:-"Press '${FAILURE}I${NORMAL}' to enter interactive startup"} # dcol and icol are spaces before the message to center the message # on screen. itime is the amount of wait time for the user to press a key wcol=$(( ( ${COLUMNS} - ${wlen} ) / 2 )) icol=$(( ( ${COLUMNS} - ${ilen} ) / 2 )) itime=${itime:-"3"} echo -e "\n\n" echo -e "\\033[${wcol}G${welcome_message}" echo -e "\\033[${icol}G${i_message}${NORMAL}" echo "" read -t "${itime}" -n 1 interactive 2>&1 > /dev/null fi # Make lower case [ "${interactive}" == "I" ] && interactive="i" [ "${interactive}" != "i" ] && interactive="" # Read the state file if it exists from runlevel S [ -r /var/run/interactive ] && source /var/run/interactive # Attempt to stop all services started by the previous runlevel, # and killed in this runlevel if [ "${previous}" != "N" ]; then for i in $(ls -v /etc/rc.d/rc${runlevel}.d/K* 2> /dev/null) do check_script_status if [ "${SCRIPT_STAT}" == "1" ]; then SCRIPT_STAT="0" continue fi suffix=${i#/etc/rc.d/rc$runlevel.d/K[0-9][0-9]} prev_start=/etc/rc.d/rc$previous.d/S[0-9][0-9]$suffix sysinit_start=/etc/rc.d/rcS.d/S[0-9][0-9]$suffix if [ "${runlevel}" != "0" -a "${runlevel}" != "6" ]; then if [ ! -f ${prev_start} -a ! -f ${sysinit_start} ]; then MSG="WARNING:\n\n${i} can't be " MSG="${MSG}executed because it was not " MSG="${MSG}not started in the previous " MSG="${MSG}runlevel (${previous})." log_warning_msg "$MSG" continue fi fi run ${i} stop error_value=${?} if [ "${error_value}" != "0" ]; then print_error_msg; fi done fi if [ "${previous}" == "N" ]; then export IN_BOOT=1; fi if [ "$runlevel" == "6" -a -n "${FASTBOOT}" ]; then touch /fastboot fi # Start all functions in this runlevel for i in $( ls -v /etc/rc.d/rc${runlevel}.d/S* 2> /dev/null) do if [ "${previous}" != "N" ]; then suffix=${i#/etc/rc.d/rc$runlevel.d/S[0-9][0-9]} stop=/etc/rc.d/rc$runlevel.d/K[0-9][0-9]$suffix prev_start=/etc/rc.d/rc$previous.d/S[0-9][0-9]$suffix [ -f ${prev_start} -a ! -f ${stop} ] && continue fi check_script_status if [ "${SCRIPT_STAT}" == "1" ]; then SCRIPT_STAT="0" continue fi case ${runlevel} in 0|6) run ${i} stop ;; *) run ${i} start ;; esac error_value=${?} if [ "${error_value}" != "0" ]; then print_error_msg; fi done # Store interactive variable on switch from runlevel S and remove if not if [ "${runlevel}" == "S" -a "${interactive}" == "i" ]; then echo "interactive=\"i\"" > /var/run/interactive else rm -f /var/run/interactive 2> /dev/null fi # Copy the boot log on initial boot only if [ "${previous}" == "N" -a "${runlevel}" != "S" ]; then cat $BOOTLOG >> /var/log/boot.log # Mark the end of boot echo "--------" >> /var/log/boot.log # Remove the temporary file rm -f $BOOTLOG 2> /dev/null fi # End rc
#!/bin/sh ######################################################################## # # Begin /lib/lsb/init-funtions # # Description : Run Level Control Functions # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # : DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # # Notes : With code based on Matthias Benkmann's simpleinit-msb # http://winterdrache.de/linux/newboot/index.html # # The file should be located in /lib/lsb # ######################################################################## ## Environmental setup # Setup default values for environment umask 022 export PATH="/bin:/usr/bin:/sbin:/usr/sbin" ## Set color commands, used via echo # 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 NORMAL="\\033[0;39m" # Standard console grey SUCCESS="\\033[1;32m" # Success is green WARNING="\\033[1;33m" # Warnings are yellow FAILURE="\\033[1;31m" # Failures are red INFO="\\033[1;36m" # Information is light cyan BRACKET="\\033[1;34m" # Brackets are blue # Use a colored prefix BMPREFIX=" " SUCCESS_PREFIX="${SUCCESS} * ${NORMAL} " FAILURE_PREFIX="${FAILURE}*****${NORMAL} " WARNING_PREFIX="${WARNING} *** ${NORMAL} " SKIP_PREFIX="${INFO} S ${NORMAL}" SUCCESS_SUFFIX="${BRACKET}[${SUCCESS} OK ${BRACKET}]${NORMAL}" FAILURE_SUFFIX="${BRACKET}[${FAILURE} FAIL ${BRACKET}]${NORMAL}" WARNING_SUFFIX="${BRACKET}[${WARNING} WARN ${BRACKET}]${NORMAL}" SKIP_SUFFIX="${BRACKET}[${INFO} SKIP ${BRACKET}]${NORMAL}" BOOTLOG=/run/bootlog KILLDELAY=3 SCRIPT_STAT="0" # Set any user specified environment variables e.g. HEADLESS [ -r /etc/sysconfig/rc.site ] && . /etc/sysconfig/rc.site ## Screen Dimensions # Find current screen size if [ -z "${COLUMNS}" ]; then COLUMNS=$(stty size) COLUMNS=${COLUMNS##* } fi # When using remote connections, such as a serial port, stty size returns 0 if [ "${COLUMNS}" = "0" ]; then COLUMNS=80 fi ## Measurements for positioning result messages COL=$((${COLUMNS} - 8)) WCOL=$((${COL} - 2)) ## Set Cursor Position Commands, used via echo SET_COL="\\033[${COL}G" # at the $COL char SET_WCOL="\\033[${WCOL}G" # at the $WCOL char CURS_UP="\\033[1A\\033[0G" # Up one line, at the 0'th char CURS_ZERO="\\033[0G" ################################################################################ # start_daemon() # # Usage: start_daemon [-f] [-n nicelevel] [-p pidfile] pathname [args...] # # # # Purpose: This runs the specified program as a daemon # # # # Inputs: -f: (force) run the program even if it is already running. # # -n nicelevel: specify a nice level. See 'man nice(1)'. # # -p pidfile: use the specified file to determine PIDs. # # pathname: the complete path to the specified program # # args: additional arguments passed to the program (pathname) # # # # Return values (as defined by LSB exit codes): # # 0 - program is running or service is OK # # 1 - generic or unspecified error # # 2 - invalid or excessive argument(s) # # 5 - program is not installed # ################################################################################ start_daemon() { local force="" local nice="0" local pidfile="" local pidlist="" local retval="" # Process arguments while true do case "${1}" in -f) force="1" shift 1 ;; -n) nice="${2}" shift 2 ;; -p) pidfile="${2}" shift 2 ;; -*) return 2 ;; *) program="${1}" break ;; esac done # Check for a valid program if [ ! -e "${program}" ]; then return 5; fi # Execute if [ -z "${force}" ]; then if [ -z "${pidfile}" ]; then # Determine the pid by discovery pidlist=`pidofproc "${1}"` retval="${?}" else # The PID file contains the needed PIDs # Note that by LSB requirement, the path must be given to pidofproc, # however, it is not used by the current implementation or standard. pidlist=`pidofproc -p "${pidfile}" "${1}"` retval="${?}" fi # Return a value ONLY # It is the init script's (or distribution's functions) responsibilty # to log messages! case "${retval}" in 0) # Program is already running correctly, this is a # successful start. return 0 ;; 1) # Program is not running, but an invalid pid file exists # remove the pid file and continue rm -f "${pidfile}" ;; 3) # Program is not running and no pidfile exists # do nothing here, let start_deamon continue. ;; *) # Others as returned by status values shall not be interpreted # and returned as an unspecified error. return 1 ;; esac fi # Do the start! nice -n "${nice}" "${@}" } ################################################################################ # killproc() # # Usage: killproc [-p pidfile] pathname [signal] # # # # Purpose: Send control signals to running processes # # # # Inputs: -p pidfile, uses the specified pidfile # # pathname, pathname to the specified program # # signal, send this signal to pathname # # # # Return values (as defined by LSB exit codes): # # 0 - program (pathname) has stopped/is already stopped or a # # running program has been sent specified signal and stopped # # successfully # # 1 - generic or unspecified error # # 2 - invalid or excessive argument(s) # # 5 - program is not installed # # 7 - program is not running and a signal was supplied # ################################################################################ killproc() { local pidfile local program local prefix local progname local signal="-TERM" local fallback="-KILL" local nosig local pidlist local retval local pid local delay="30" local piddead local dtime # Process arguments while true; do case "${1}" in -p) pidfile="${2}" shift 2 ;; *) program="${1}" if [ -n "${2}" ]; then signal="${2}" fallback="" else nosig=1 fi # Error on additional arguments if [ -n "${3}" ]; then return 2 else break fi ;; esac done # Check for a valid program if [ ! -e "${program}" ]; then return 5; fi # Check for a valid signal check_signal "${signal}" if [ "${?}" -ne "0" ]; then return 2; fi # Get a list of pids if [ -z "${pidfile}" ]; then # determine the pid by discovery pidlist=`pidofproc "${1}"` retval="${?}" else # The PID file contains the needed PIDs # Note that by LSB requirement, the path must be given to pidofproc, # however, it is not used by the current implementation or standard. pidlist=`pidofproc -p "${pidfile}" "${1}"` retval="${?}" fi # Return a value ONLY # It is the init script's (or distribution's functions) responsibilty # to log messages! case "${retval}" in 0) # Program is running correctly # Do nothing here, let killproc continue. ;; 1) # Program is not running, but an invalid pid file exists # Remove the pid file. rm -f "${pidfile}" # This is only a success if no signal was passed. if [ -n "${nosig}" ]; then return 0 else return 7 fi ;; 3) # Program is not running and no pidfile exists # This is only a success if no signal was passed. if [ -n "${nosig}" ]; then return 0 else return 7 fi ;; *) # Others as returned by status values shall not be interpreted # and returned as an unspecified error. return 1 ;; esac # Perform different actions for exit signals and control signals check_sig_type "${signal}" if [ "${?}" -eq "0" ]; then # Signal is used to terminate the program # Account for empty pidlist (pid file still exists and no # signal was given) if [ "${pidlist}" != "" ]; then # Kill the list of pids for pid in ${pidlist}; do kill -0 "${pid}" 2> /dev/null if [ "${?}" -ne "0" ]; then # Process is dead, continue to next and assume all is well continue else kill "${signal}" "${pid}" 2> /dev/null # Wait up to ${delay}/10 seconds to for "${pid}" to # terminate in 10ths of a second while [ "${delay}" -ne "0" ]; do kill -0 "${pid}" 2> /dev/null || piddead="1" if [ "${piddead}" = "1" ]; then break; fi sleep 0.1 delay="$(( ${delay} - 1 ))" done # If a fallback is set, and program is still running, then # use the fallback if [ -n "${fallback}" -a "${piddead}" != "1" ]; then kill "${fallback}" "${pid}" 2> /dev/null sleep 1 # Check again, and fail if still running kill -0 "${pid}" 2> /dev/null && return 1 fi fi done fi # Check for and remove stale PID files. if [ -z "${pidfile}" ]; then # Find the basename of $program prefix=`echo "${program}" | sed 's/[^/]*$//'` progname=`echo "${program}" | sed "s@${prefix}@@"` if [ -e "/var/run/${progname}.pid" ]; then rm -f "/var/run/${progname}.pid" 2> /dev/null fi else if [ -e "${pidfile}" ]; then rm -f "${pidfile}" 2> /dev/null; fi fi # For signals that do not expect a program to exit, simply # let kill do its job, and evaluate kill's return for value else # check_sig_type - signal is not used to terminate program for pid in ${pidlist}; do kill "${signal}" "${pid}" if [ "${?}" -ne "0" ]; then return 1; fi done fi } ################################################################################ # pidofproc() # # Usage: pidofproc [-p pidfile] pathname # # # # Purpose: This function returns one or more pid(s) for a particular daemon # # # # Inputs: -p pidfile, use the specified pidfile instead of pidof # # pathname, path to the specified program # # # # Return values (as defined by LSB status codes): # # 0 - Success (PIDs to stdout) # # 1 - Program is dead, PID file still exists (remaining PIDs output) # # 3 - Program is not running (no output) # ################################################################################ pidofproc() { local pidfile local program local prefix local progname local pidlist local lpids local exitstatus="0" # Process arguments while true; do case "${1}" in -p) pidfile="${2}" shift 2 ;; *) program="${1}" if [ -n "${2}" ]; then # Too many arguments # Since this is status, return unknown return 4 else break fi ;; esac done # If a PID file is not specified, try and find one. if [ -z "${pidfile}" ]; then # Get the program's basename prefix=`echo "${program}" | sed 's/[^/]*$//'` if [ -z "${prefix}" ]; then progname="${program}" else progname=`echo "${program}" | sed "s@${prefix}@@"` fi # If a PID file exists with that name, assume that is it. if [ -e "/var/run/${progname}.pid" ]; then pidfile="/var/run/${progname}.pid" fi fi # If a PID file is set and exists, use it. if [ -n "${pidfile}" -a -e "${pidfile}" ]; then # Use the value in the first line of the pidfile pidlist=`/bin/head -n1 "${pidfile}"` # This can optionally be written as 'sed 1q' to repalce 'head -n1' # should LFS move /bin/head to /usr/bin/head else # Use pidof pidlist=`pidof "${program}"` fi # Figure out if all listed PIDs are running. for pid in ${pidlist}; do kill -0 ${pid} 2> /dev/null if [ "${?}" -eq "0" ]; then lpids="${lpids}${pid} " else exitstatus="1" fi done if [ -z "${lpids}" -a ! -f "${pidfile}" ]; then return 3 else echo "${lpids}" return "${exitstatus}" fi } ################################################################################ # statusproc() # # Usage: statusproc [-p pidfile] pathname # # # # Purpose: This function prints the status of a particular daemon to stdout # # # # Inputs: -p pidfile, use the specified pidfile instead of pidof # # pathname, path to the specified program # # # # Return values: # # 0 - Status printed # # 1 - Input error. The daemon to check was not specified. # ################################################################################ statusproc() { local pidfile local pidlist if [ "${#}" = "0" ]; then echo "Usage: statusproc [-p pidfle] {program}" exit 1 fi # Process arguments while true; do case "${1}" in -p) pidfile="${2}" shift 2 ;; *) if [ -n "${2}" ]; then echo "Too many arguments" return 1 else break fi ;; esac done if [ -n "${pidfile}" ]; then pidlist=`pidofproc -p "${pidfile}" $@` else pidlist=`pidofproc $@` fi # Trim trailing blanks pidlist=`echo "${pidlist}" | sed -r 's/ +$//'` base="${1##*/}" if [ -n "${pidlist}" ]; then /bin/echo -e "${INFO}${base} is running with Process" \ "ID(s) ${pidlist}.${NORMAL}" else if [ -n "${base}" -a -e "/var/run/${base}.pid" ]; then /bin/echo -e "${WARNING}${1} is not running but" \ "/var/run/${base}.pid exists.${NORMAL}" else if [ -n "${pidfile}" -a -e "${pidfile}" ]; then /bin/echo -e "${WARNING}${1} is not running" \ "but ${pidfile} exists.${NORMAL}" else /bin/echo -e "${INFO}${1} is not running.${NORMAL}" fi fi fi } ################################################################################ # timespec() # # # # Purpose: An internal utility function to format a timestamp # # a boot log file. Sets the STAMP variable. # # # # Return value: Not used # ################################################################################ timespec() { STAMP="$(echo `date +"%b %d %T %:z"` `hostname`) " return 0 } ################################################################################ # log_success_msg() # # Usage: log_success_msg ["message"] # # # # Purpose: Print a successful status message to the screen and # # a boot log file. # # # # Inputs: $@ - Message # # # # Return values: Not used # ################################################################################ log_success_msg() { /bin/echo -n -e "${BMPREFIX}${@}" /bin/echo -e "${CURS_ZERO}${SUCCESS_PREFIX}${SET_COL}${SUCCESS_SUFFIX}" # Strip non-printable characters from log file logmessage=`echo "${@}" | sed 's/\\\033[^a-zA-Z]*.//g'` timespec /bin/echo -e "${STAMP} ${logmessage} OK" >> ${BOOTLOG} return 0 } log_success_msg2() { /bin/echo -n -e "${BMPREFIX}${@}" /bin/echo -e "${CURS_ZERO}${SUCCESS_PREFIX}${SET_COL}${SUCCESS_SUFFIX}" echo " OK" >> ${BOOTLOG} return 0 } ################################################################################ # log_failure_msg() # # Usage: log_failure_msg ["message"] # # # # Purpose: Print a failure status message to the screen and # # a boot log file. # # # # Inputs: $@ - Message # # # # Return values: Not used # ################################################################################ log_failure_msg() { /bin/echo -n -e "${BMPREFIX}${@}" /bin/echo -e "${CURS_ZERO}${FAILURE_PREFIX}${SET_COL}${FAILURE_SUFFIX}" # Strip non-printable characters from log file timespec logmessage=`echo "${@}" | sed 's/\\\033[^a-zA-Z]*.//g'` /bin/echo -e "${STAMP} ${logmessage} FAIL" >> ${BOOTLOG} return 0 } log_failure_msg2() { /bin/echo -n -e "${BMPREFIX}${@}" /bin/echo -e "${CURS_ZERO}${FAILURE_PREFIX}${SET_COL}${FAILURE_SUFFIX}" echo "FAIL" >> ${BOOTLOG} return 0 } ################################################################################ # log_warning_msg() # # Usage: log_warning_msg ["message"] # # # # Purpose: Print a warning status message to the screen and # # a boot log file. # # # # Return values: Not used # ################################################################################ log_warning_msg() { /bin/echo -n -e "${BMPREFIX}${@}" /bin/echo -e "${CURS_ZERO}${WARNING_PREFIX}${SET_COL}${WARNING_SUFFIX}" # Strip non-printable characters from log file logmessage=`echo "${@}" | sed 's/\\\033[^a-zA-Z]*.//g'` timespec /bin/echo -e "${STAMP} ${logmessage} WARN" >> ${BOOTLOG} return 0 } log_skip_msg() { /bin/echo -n -e "${BMPREFIX}${@}" /bin/echo -e "${CURS_ZERO}${SKIP_PREFIX}${SET_COL}${SKIP_SUFFIX}" # Strip non-printable characters from log file logmessage=`echo "${@}" | sed 's/\\\033[^a-zA-Z]*.//g'` /bin/echo "SKIP" >> ${BOOTLOG} return 0 } ################################################################################ # log_info_msg() # # Usage: log_info_msg message # # # # Purpose: Print an information message to the screen and # # a boot log file. Does not print a trailing newline character. # # # # Return values: Not used # ################################################################################ log_info_msg() { /bin/echo -n -e "${BMPREFIX}${@}" # Strip non-printable characters from log file logmessage=`echo "${@}" | sed 's/\\\033[^a-zA-Z]*.//g'` timespec /bin/echo -n -e "${STAMP} ${logmessage}" >> ${BOOTLOG} return 0 } log_info_msg2() { /bin/echo -n -e "${@}" # Strip non-printable characters from log file logmessage=`echo "${@}" | sed 's/\\\033[^a-zA-Z]*.//g'` /bin/echo -n -e "${logmessage}" >> ${BOOTLOG} return 0 } ################################################################################ # evaluate_retval() # # Usage: Evaluate a return value and print success or failyure as appropriate # # # # Purpose: Convenience function to terminate an info message # # # # Return values: Not used # ################################################################################ evaluate_retval() { local error_value="${?}" if [ ${error_value} = 0 ]; then log_success_msg2 else log_failure_msg2 fi } ################################################################################ # check_signal() # # Usage: check_signal [ -{signal} | {signal} ] # # # # Purpose: Check for a valid signal. This is not defined by any LSB draft, # # however, it is required to check the signals to determine if the # # signals chosen are invalid arguments to the other functions. # # # # Inputs: Accepts a single string value in the form or -{signal} or {signal} # # # # Return values: # # 0 - Success (signal is valid # # 1 - Signal is not valid # ################################################################################ check_signal() { local valsig # Add error handling for invalid signals valsig="-ALRM -HUP -INT -KILL -PIPE -POLL -PROF -TERM -USR1 -USR2" valsig="${valsig} -VTALRM -STKFLT -PWR -WINCH -CHLD -URG -TSTP -TTIN" valsig="${valsig} -TTOU -STOP -CONT -ABRT -FPE -ILL -QUIT -SEGV -TRAP" valsig="${valsig} -SYS -EMT -BUS -XCPU -XFSZ -0 -1 -2 -3 -4 -5 -6 -8 -9" valsig="${valsig} -11 -13 -14 -15" echo "${valsig}" | grep -- " ${1} " > /dev/null if [ "${?}" -eq "0" ]; then return 0 else return 1 fi } ################################################################################ # check_sig_type() # # Usage: check_signal [ -{signal} | {signal} ] # # # # Purpose: Check if signal is a program termination signal or a control signal # # This is not defined by any LSB draft, however, it is required to # # check the signals to determine if they are intended to end a # # program or simply to control it. # # # # Inputs: Accepts a single string value in the form or -{signal} or {signal} # # # # Return values: # # 0 - Signal is used for program termination # # 1 - Signal is used for program control # ################################################################################ check_sig_type() { local valsig # The list of termination signals (limited to generally used items) valsig="-ALRM -INT -KILL -TERM -PWR -STOP -ABRT -QUIT -2 -3 -6 -9 -14 -15" echo "${valsig}" | grep -- " ${1} " > /dev/null if [ "${?}" -eq "0" ]; then return 0 else return 1 fi } ################################################################################ # wait_for_user() # # # # Purpose: Wait for the user to respond if not a headless system # # # ################################################################################ wait_for_user() { # Wait for the user by default [ "${HEADLESS=0}" = "0" ] && read ENTER return 0 } ################################################################################ # is_true() # # # # Purpose: Utility to test if a variable is true | yes | 1 # # # ################################################################################ is_true() { [ "$1" = "1" ] || [ "$1" = "yes" ] || [ "$1" = "true" ] || [ "$1" = "y" ] || [ "$1" = "t" ] } # End /lib/lsb/init-functions
#!/bin/sh ######################################################################## # Begin mountvirtfs # # Description : Mount proc, sysfs, and run # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: mountvirtfs # Required-Start: # Should-Start: # Required-Stop: # Should-Stop: # Default-Start: S # Default-Stop: # Short-Description: Mounts /sys and /proc virtual (kernel) filesystems. # Mounts /run (tmpfs) and /dev (devtmpfs). # Description: Mounts /sys and /proc virtual (kernel) filesystems. # Mounts /run (tmpfs) and /dev (devtmpfs). # X-LFS-Provided-By: LFS ### END INIT INFO . /lib/lsb/init-functions case "${1}" in start) # Make sure /run is available before logging any messages if ! mountpoint /run >/dev/null; then mount /run || failed=1 fi mkdir -p /run/lock /run/shm chmod 1777 /run/shm /run/lock log_info_msg "Mounting virtual file systems: ${INFO}/run" if ! mountpoint /proc >/dev/null; then log_info_msg2 " ${INFO}/proc" mount -o nosuid,noexec,nodev /proc || failed=1 fi if ! mountpoint /sys >/dev/null; then log_info_msg2 " ${INFO}/sys" mount -o nosuid,noexec,nodev /sys || failed=1 fi if ! mountpoint /dev >/dev/null; then log_info_msg2 " ${INFO}/dev" mount -o mode=0755,nosuid /dev || failed=1 fi ln -sfn /run/shm /dev/shm (exit ${failed}) evaluate_retval exit $failed ;; *) echo "Usage: ${0} {start}" exit 1 ;; esac # End mountvirtfs
#!/bin/sh ######################################################################## # Begin modules # # Description : Module auto-loading script # # Authors : Zack Winkles # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: modules # Required-Start: mountvirtfs sysctl # Should-Start: # Required-Stop: # Should-Stop: # Default-Start: S # Default-Stop: # Short-Description: Loads required modules. # Description: Loads modules listed in /etc/sysconfig/modules. # X-LFS-Provided-By: LFS ### END INIT INFO # Assure that the kernel has module support. [ -e /proc/modules ] || exit 0 . /lib/lsb/init-functions case "${1}" in start) # Exit if there's no modules file or there are no # valid entries [ -r /etc/sysconfig/modules ] || exit 0 egrep -qv '^($|#)' /etc/sysconfig/modules || exit 0 log_info_msg "Loading modules:" # Only try to load modules if the user has actually given us # some modules to load. while read module args; do # Ignore comments and blank lines. case "$module" in ""|"#"*) continue ;; esac # Attempt to load the module, passing any arguments provided. modprobe ${module} ${args} >/dev/null # Print the module name if successful, otherwise take note. if [ $? -eq 0 ]; then log_info_msg2 " ${module}" else failedmod="${failedmod} ${module}" fi done < /etc/sysconfig/modules # Print a message about successfully loaded modules on the correct line. log_success_msg2 # Print a failure message with a list of any modules that # may have failed to load. if [ -n "${failedmod}" ]; then log_failure_msg "Failed to load modules:${failedmod}" exit 1 fi ;; *) echo "Usage: ${0} {start}" exit 1 ;; esac exit 0 # End modules
#!/bin/sh ######################################################################## # Begin udev # # Description : Udev cold-plugging script # # Authors : Zack Winkles, Alexander E. Patrakov # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: udev $time # Required-Start: # Should-Start: modules # Required-Stop: # Should-Stop: # Default-Start: S # Default-Stop: # Short-Description: Populates /dev with device nodes. # Description: Mounts a tempfs on /dev and starts the udevd daemon. # Device nodes are created as defined by udev. # X-LFS-Provided-By: LFS ### END INIT INFO . /lib/lsb/init-functions case "${1}" in start) log_info_msg "Populating /dev with device nodes... " if ! grep -q '[[:space:]]sysfs' /proc/mounts; then log_failure_msg2 msg="FAILURE:\n\nUnable to create " msg="${msg}devices without a SysFS filesystem\n\n" msg="${msg}After you press Enter, this system " msg="${msg}will be halted and powered off.\n\n" log_info_msg "$msg" log_info_msg "Press Enter to continue..." wait_for_user /etc/rc.d/init.d/halt stop fi # Start the udev daemon to continually watch for, and act on, # uevents /sbin/udevd --daemon # Now traverse /sys in order to "coldplug" devices that have # already been discovered /sbin/udevadm trigger --action=add --type=subsystems /sbin/udevadm trigger --action=add --type=devices /sbin/udevadm trigger --action=change --type=devices # Now wait for udevd to process the uevents we triggered if ! is_true "$OMIT_UDEV_SETTLE"; then /sbin/udevadm settle fi # If any LVM based partitions are on the system, ensure they # are activated so they can be used. if [ -x /sbin/vgchange ]; then /sbin/vgchange -a y >/dev/null; fi log_success_msg2 ;; *) echo "Usage ${0} {start}" exit 1 ;; esac exit 0 # End udev
#!/bin/sh ######################################################################## # Begin swap # # Description : Swap Control Script # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: swap # Required-Start: udev # Should-Start: modules # Required-Stop: localnet # Should-Stop: # Default-Start: S # Default-Stop: 0 6 # Short-Description: Mounts and unmounts swap partitions. # Description: Mounts and unmounts swap partitions defined in # /etc/fstab. # X-LFS-Provided-By: LFS ### END INIT INFO . /lib/lsb/init-functions case "${1}" in start) log_info_msg "Activating all swap files/partitions..." swapon -a evaluate_retval ;; stop) log_info_msg "Deactivating all swap files/partitions..." swapoff -a evaluate_retval ;; restart) ${0} stop sleep 1 ${0} start ;; status) log_success_msg "Retrieving swap status." swapon -s ;; *) echo "Usage: ${0} {start|stop|restart|status}" exit 1 ;; esac exit 0 # End swap
#!/bin/sh ######################################################################## # Begin setclock # # Description : Setting Linux Clock # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: # Required-Start: # Should-Start: modules # Required-Stop: # Should-Stop: $syslog # Default-Start: S # Default-Stop: # Short-Description: Stores and restores time from the hardware clock # Description: On boot, system time is obtained from hwclock. The # hardware clock can also be set on shutdown. # X-LFS-Provided-By: LFS BLFS ### END INIT INFO . /lib/lsb/init-functions [ -r /etc/sysconfig/clock ] && . /etc/sysconfig/clock case "${UTC}" in yes|true|1) CLOCKPARAMS="${CLOCKPARAMS} --utc" ;; no|false|0) CLOCKPARAMS="${CLOCKPARAMS} --localtime" ;; esac case ${1} in start) hwclock --hctosys ${CLOCKPARAMS} >/dev/null ;; stop) log_info_msg "Setting hardware clock..." hwclock --systohc ${CLOCKPARAMS} >/dev/null evaluate_retval ;; *) echo "Usage: ${0} {start|stop}" exit 1 ;; esac exit 0
#!/bin/sh ######################################################################## # Begin checkfs # # Description : File System Check # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # A. Luebke - luebke@users.sourceforge.net # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # # Based on checkfs script from LFS-3.1 and earlier. # # From man fsck # 0 - No errors # 1 - File system errors corrected # 2 - System should be rebooted # 4 - File system errors left uncorrected # 8 - Operational error # 16 - Usage or syntax error # 32 - Fsck canceled by user request # 128 - Shared library error # ######################################################################### ### BEGIN INIT INFO # Provides: checkfs # Required-Start: udev swap $time # Should-Start: # Required-Stop: # Should-Stop: # Default-Start: S # Default-Stop: # Short-Description: Checks local filesystems before mounting. # Description: Checks local filesystmes before mounting. # X-LFS-Provided-By: LFS ### END INIT INFO . /lib/lsb/init-functions case "${1}" in start) if [ -f /fastboot ]; then msg="/fastboot found, will omit " msg="${msg} file system checks as requested.\n" log_info_msg "${msg}" exit 0 fi log_info_msg "Mounting root file system in read-only mode... " mount -n -o remount,ro / >/dev/null if [ ${?} != 0 ]; then log_failure_msg2 msg="\n\nCannot check root " msg="${msg}filesystem because it could not be mounted " msg="${msg}in read-only mode.\n\n" msg="${msg}After you press Enter, this system will be " msg="${msg}halted and powered off.\n\n" log_failure_msg "${msg}" log_info_msg "Press Enter to continue..." wait_for_user /etc/rc.d/init.d/halt stop else log_success_msg2 fi if [ -f /forcefsck ]; then msg="/forcefsck found, forcing file" msg="${msg} system checks as requested." log_success_msg "$msg" options="-f" else options="" fi log_info_msg "Checking file systems..." # Note: -a option used to be -p; but this fails e.g. on fsck.minix if is_true "$VERBOSE_FSCK"; then fsck ${options} -a -A -C -T else fsck ${options} -a -A -C -T >/dev/null fi error_value=${?} if [ "${error_value}" = 0 ]; then log_success_msg2 fi if [ "${error_value}" = 1 ]; then msg="\nWARNING:\n\nFile system errors " msg="${msg}were found and have been corrected.\n" msg="${msg} You may want to double-check that " msg="${msg}everything was fixed properly." log_warning_msg "$msg" fi if [ "${error_value}" = 2 -o "${error_value}" = 3 ]; then msg="\nWARNING:\n\nFile system errors " msg="${msg}were found and have been been " msg="${msg}corrected, but the nature of the " msg="${msg}errors require this system to be rebooted.\n\n" msg="${msg}After you press enter, " msg="${msg}this system will be rebooted\n\n" log_failure_msg "$msg" log_info_msg "Press Enter to continue..." wait_for_user reboot -f fi if [ "${error_value}" -gt 3 -a "${error_value}" -lt 16 ]; then msg="\nFAILURE:\n\nFile system errors " msg="${msg}were encountered that could not be " msg="${msg}fixed automatically.\nThis system " msg="${msg}cannot continue to boot and will " msg="${msg}therefore be halted until those " msg="${msg}errors are fixed manually by a " msg="${msg}System Administrator.\n\n" msg="${msg}After you press Enter, this system will be " msg="${msg}halted and powered off.\n\n" log_failure_msg "$msg" log_info_msg "Press Enter to continue..." wait_for_user /etc/rc.d/init.d/halt stop fi if [ "${error_value}" -ge 16 ]; then msg="FAILURE:\n\nUnexpected failure " msg="${msg}running fsck. Exited with error " msg="${msg} code: ${error_value}.\n" log_info_msg $msg exit ${error_value} fi exit 0 ;; *) echo "Usage: ${0} {start}" exit 1 ;; esac # End checkfs
#!/bin/sh ######################################################################## # Begin mountfs # # Description : File System Mount Script # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: $local_fs # Required-Start: udev checkfs # Should-Start: # Required-Stop: swap # Should-Stop: # Default-Start: S # Default-Stop: 0 6 # Short-Description: Mounts/unmounts local filesystems defined in /etc/fstab. # Description: Remounts root filesystem read/write and mounts all # remaining local filesystems defined in /etc/fstab on # start. Remounts root filesystem read-only and unmounts # remaining filesystems on stop. # X-LFS-Provided-By: LFS ### END INIT INFO . /lib/lsb/init-functions case "${1}" in start) log_info_msg "Remounting root file system in read-write mode..." mount --options remount,rw / >/dev/null evaluate_retval # Remove fsck-related file system watermarks. rm -f /fastboot /forcefsck # Make sure /dev/pts exists mkdir -p /dev/pts # This will mount all filesystems that do not have _netdev in # their option list. _netdev denotes a network filesystem. log_info_msg "Mounting remaining file systems..." mount --all --test-opts no_netdev >/dev/null evaluate_retval exit $failed ;; stop) # Don't unmount virtual file systems like /run log_info_msg "Unmounting all other currently mounted file systems..." # Ensure any loop devies are removed losetup -D umount --all --detach-loop --read-only \ --types notmpfs,nosysfs,nodevtmpfs,noproc,nodevpts >/dev/null evaluate_retval # Make sure / is mounted read only (umount bug) mount --options remount,ro / # Make all LVM volume groups unavailable, if appropriate # This fails if swap or / are on an LVM partition #if [ -x /sbin/vgchange ]; then /sbin/vgchange -an > /dev/null; fi ;; *) echo "Usage: ${0} {start|stop}" exit 1 ;; esac # End mountfs
#!/bin/sh ######################################################################## # Begin udev_retry # # Description : Udev cold-plugging script (retry) # # Authors : Alexander E. Patrakov # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # Bryan Kadzban - # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: udev_retry # Required-Start: udev # Should-Start: $local_fs # Required-Stop: # Should-Stop: # Default-Start: S # Default-Stop: # Short-Description: Replays failed uevents and creates additional devices. # Description: Replays any failed uevents that were skipped due to # slow hardware initialization, and creates those needed # device nodes # X-LFS-Provided-By: LFS ### END INIT INFO . /lib/lsb/init-functions case "${1}" in start) log_info_msg "Retrying failed uevents, if any..." # As of udev-186, the --run option is no longer valid #rundir=$(/sbin/udevadm info --run) rundir=/run/udev # From Debian: "copy the rules generated before / was mounted # read-write": for file in ${rundir}/tmp-rules--*; do dest=${file##*tmp-rules--} [ "$dest" = '*' ] && break cat $file >> /etc/udev/rules.d/$dest rm -f $file done # Re-trigger the uevents that may have failed, # in hope they will succeed now /bin/sed -e 's/#.*$//' /etc/sysconfig/udev_retry | /bin/grep -v '^$' | \ while read line ; do for subsystem in $line ; do /sbin/udevadm trigger --subsystem-match=$subsystem --action=add done done # Now wait for udevd to process the uevents we triggered if ! is_true "$OMIT_UDEV_RETRY_SETTLE"; then /sbin/udevadm settle fi evaluate_retval ;; *) echo "Usage ${0} {start}" exit 1 ;; esac exit 0 # End udev_retry
#!/bin/sh ######################################################################## # Begin cleanfs # # Description : Clean file system # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: cleanfs # Required-Start: $local_fs # Should-Start: # Required-Stop: # Should-Stop: # Default-Start: S # Default-Stop: # Short-Description: Cleans temporary directories early in the boot process. # Description: Cleans temporary directories /var/run, /var/lock, and # optionally, /tmp. cleanfs also creates /var/run/utmp # and any files defined in /etc/sysconfig/createfiles. # X-LFS-Provided-By: LFS ### END INIT INFO . /lib/lsb/init-functions # Function to create files/directory on boot. create_files() { # Input to file descriptor 9 and output to stdin (redirection) exec 9>&0 < /etc/sysconfig/createfiles while read name type perm usr grp dtype maj min junk do # Ignore comments and blank lines. case "${name}" in ""|\#*) continue ;; esac # Ignore existing files. if [ ! -e "${name}" ]; then # Create stuff based on its type. case "${type}" in dir) mkdir "${name}" ;; file) :> "${name}" ;; dev) case "${dtype}" in char) mknod "${name}" c ${maj} ${min} ;; block) mknod "${name}" b ${maj} ${min} ;; pipe) mknod "${name}" p ;; *) log_warning_msg "\nUnknown device type: ${dtype}" ;; esac ;; *) log_warning_msg "\nUnknown type: ${type}" continue ;; esac # Set up the permissions, too. chown ${usr}:${grp} "${name}" chmod ${perm} "${name}" fi done # Close file descriptor 9 (end redirection) exec 0>&9 9>&- return 0 } case "${1}" in start) log_info_msg "Cleaning file systems:" if [ "${SKIPTMPCLEAN}" = "" ]; then log_info_msg2 " /tmp" cd /tmp && find . -xdev -mindepth 1 ! -name lost+found -delete || failed=1 fi > /var/run/utmp if grep -q '^utmp:' /etc/group ; then chmod 664 /var/run/utmp chgrp utmp /var/run/utmp fi (exit ${failed}) evaluate_retval if egrep -qv '^(#|$)' /etc/sysconfig/createfiles 2>/dev/null; then log_info_msg "Creating files and directories... " create_files # Always returns 0 evaluate_retval fi exit $failed ;; *) echo "Usage: ${0} {start}" exit 1 ;; esac # End cleanfs
#!/bin/sh ######################################################################## # Begin console # # Description : Sets keymap and screen font # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # Alexander E. Patrakov # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: console # Required-Start: # Should-Start: $local_fs # Required-Stop: # Should-Stop: # Default-Start: S # Default-Stop: # Short-Description: Sets up a localised console. # Description: Sets up fonts and language settings for the user's # local as defined by /etc/sysconfig/console. # X-LFS-Provided-By: LFS ### END INIT INFO . /lib/lsb/init-functions # Native English speakers probably don't have /etc/sysconfig/console at all [ -r /etc/sysconfig/console ] && . /etc/sysconfig/console is_true() { [ "$1" = "1" ] || [ "$1" = "yes" ] || [ "$1" = "true" ] } failed=0 case "${1}" in start) # See if we need to do anything if [ -z "${KEYMAP}" ] && [ -z "${KEYMAP_CORRECTIONS}" ] && [ -z "${FONT}" ] && [ -z "${LEGACY_CHARSET}" ] && ! is_true "${UNICODE}"; then exit 0 fi # There should be no bogus failures below this line! log_info_msg "Setting up Linux console..." # Figure out if a framebuffer console is used [ -d /sys/class/graphics/fb0 ] && use_fb=1 || use_fb=0 # Figure out the command to set the console into the # desired mode is_true "${UNICODE}" && MODE_COMMAND="echo -en '\033%G' && kbd_mode -u" || MODE_COMMAND="echo -en '\033%@\033(K' && kbd_mode -a" # On framebuffer consoles, font has to be set for each vt in # UTF-8 mode. This doesn't hurt in non-UTF-8 mode also. ! is_true "${use_fb}" || [ -z "${FONT}" ] || MODE_COMMAND="${MODE_COMMAND} && setfont ${FONT}" # Apply that command to all consoles mentioned in # /etc/inittab. Important: in the UTF-8 mode this should # happen before setfont, otherwise a kernel bug will # show up and the unicode map of the font will not be # used. for TTY in `grep '^[^#].*respawn:/sbin/agetty' /etc/inittab | grep -o '\btty[[:digit:]]*\b'` do openvt -f -w -c ${TTY#tty} -- \ /bin/sh -c "${MODE_COMMAND}" || failed=1 done # Set the font (if not already set above) and the keymap [ "${use_fb}" == "1" ] || [ -z "${FONT}" ] || setfont $FONT || failed=1 [ -z "${KEYMAP}" ] || loadkeys ${KEYMAP} >/dev/null 2>&1 || failed=1 [ -z "${KEYMAP_CORRECTIONS}" ] || loadkeys ${KEYMAP_CORRECTIONS} >/dev/null 2>&1 || failed=1 # Convert the keymap from $LEGACY_CHARSET to UTF-8 [ -z "$LEGACY_CHARSET" ] || dumpkeys -c "$LEGACY_CHARSET" | loadkeys -u >/dev/null 2>&1 || failed=1 # If any of the commands above failed, the trap at the # top would set $failed to 1 ( exit $failed ) evaluate_retval exit $failed ;; *) echo "Usage: ${0} {start}" exit 1 ;; esac # End console
#!/bin/sh ######################################################################## # Begin localnet # # Description : Loopback device # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: localnet # Required-Start: $local_fs # Should-Start: # Required-Stop: # Should-Stop: # Default-Start: S # Default-Stop: 0 6 # Short-Description: Starts the local network. # Description: Sets the hostname of the machine and starts the # loopback interface. # X-LFS-Provided-By: LFS ### END INIT INFO . /lib/lsb/init-functions [ -r /etc/sysconfig/network ] && . /etc/sysconfig/network [ -r /etc/hostname ] && HOSTNAME=`cat /etc/hostname` case "${1}" in start) log_info_msg "Bringing up the loopback interface..." ip addr add 127.0.0.1/8 label lo dev lo ip link set lo up evaluate_retval log_info_msg "Setting hostname to ${HOSTNAME}..." hostname ${HOSTNAME} evaluate_retval ;; stop) log_info_msg "Bringing down the loopback interface..." ip link set lo down evaluate_retval ;; restart) ${0} stop sleep 1 ${0} start ;; status) echo "Hostname is: $(hostname)" ip link show lo ;; *) echo "Usage: ${0} {start|stop|restart|status}" exit 1 ;; esac exit 0 # End localnet
#!/bin/sh ######################################################################## # Begin sysctl # # Description : File uses /etc/sysctl.conf to set kernel runtime # parameters # # Authors : Nathan Coulson (nathan AT linuxfromscratch D0T org) # Matthew Burgress (matthew AT linuxfromscratch D0T org) # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: sysctl # Required-Start: mountvirtfs # Should-Start: # Required-Stop: # Should-Stop: # Default-Start: S # Default-Stop: # Short-Description: Makes changes to the proc filesystem # Description: Makes changes to the proc filesystem as defined in # /etc/sysctl.conf. See 'man sysctl(8)'. # X-LFS-Provided-By: LFS ### END INIT INFO . /lib/lsb/init-functions case "${1}" in start) if [ -f "/etc/sysctl.conf" ]; then log_info_msg "Setting kernel runtime parameters..." sysctl -q -p evaluate_retval fi ;; status) sysctl -a ;; *) echo "Usage: ${0} {start|status}" exit 1 ;; esac exit 0 # End sysctl
#!/bin/sh ######################################################################## # Begin sysklogd # # Description : Sysklogd loader # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: $syslog # Required-Start: localnet # Should-Start: # Required-Stop: $local_fs sendsignals # Should-Stop: # Default-Start: 3 4 5 # Default-Stop: 0 1 2 6 # Short-Description: Starts kernel and system log daemons. # Description: Starts kernel and system log daemons. # /etc/fstab. # X-LFS-Provided-By: LFS ### END INIT INFO # Note: sysklogd is not started in runlevel 2 due to possible # remote logging configurations . /lib/lsb/init-functions case "${1}" in start) log_info_msg "Starting system log daemon..." parms=${SYSKLOGD_PARMS-'-m 0'} start_daemon /sbin/syslogd $parms evaluate_retval log_info_msg "Starting kernel log daemon..." start_daemon /sbin/klogd evaluate_retval ;; stop) log_info_msg "Stopping kernel log daemon..." killproc /sbin/klogd evaluate_retval log_info_msg "Stopping system log daemon..." killproc /sbin/syslogd evaluate_retval ;; reload) log_info_msg "Reloading system log daemon config file..." pid=`pidofproc syslogd` kill -HUP "${pid}" evaluate_retval ;; restart) ${0} stop sleep 1 ${0} start ;; status) statusproc /sbin/syslogd statusproc klogd ;; *) echo "Usage: ${0} {start|stop|reload|restart|status}" exit 1 ;; esac exit 0 # End sysklogd
#!/bin/sh ######################################################################## # Begin network # # Description : Network Control Script # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # Nathan Coulson - nathan AT linuxfromscratch D0T org # Kevin P. Fleming - kpfleming@linuxfromscratch.org # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: $network # Required-Start: $local_fs swap localnet # Should-Start: $syslog # Required-Stop: $local_fs swap localnet # Should-Stop: $syslog # Default-Start: 3 4 5 # Default-Stop: 0 1 2 6 # Short-Description: Starts and configures network interfaces. # Description: Starts and configures network interfaces. # X-LFS-Provided-By: LFS ### END INIT INFO case "${1}" in start) # Start all network interfaces for file in /etc/sysconfig/ifconfig.* do interface=${file##*/ifconfig.} # Skip if $file is * (because nothing was found) if [ "${interface}" = "*" ] then continue fi /sbin/ifup ${interface} done ;; stop) # Unmount any network mounted file systems umount --all --force --types nfs,cifs,nfs4 # Reverse list net_files="" for file in /etc/sysconfig/ifconfig.* do net_files="${file} ${net_files}" done # Stop all network interfaces for file in ${net_files} do interface=${file##*/ifconfig.} # Skip if $file is * (because nothing was found) if [ "${interface}" = "*" ] then continue fi /sbin/ifdown ${interface} done ;; restart) ${0} stop sleep 1 ${0} start ;; *) echo "Usage: ${0} {start|stop|restart}" exit 1 ;; esac exit 0 # End network
#!/bin/sh ######################################################################## # Begin sendsignals # # Description : Sendsignals Script # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: sendsignals # Required-Start: # Should-Start: # Required-Stop: $local_fs swap localnet # Should-Stop: # Default-Start: # Default-Stop: 0 6 # Short-Description: Attempts to kill remaining processes. # Description: Attempts to kill remaining processes. # X-LFS-Provided-By: LFS ### END INIT INFO . /lib/lsb/init-functions case "${1}" in stop) log_info_msg "Sending all processes the TERM signal..." killall5 -15 error_value=${?} sleep ${KILLDELAY} if [ "${error_value}" = 0 -o "${error_value}" = 2 ]; then log_success_msg else log_failure_msg fi log_info_msg "Sending all processes the KILL signal..." killall5 -9 error_value=${?} sleep ${KILLDELAY} if [ "${error_value}" = 0 -o "${error_value}" = 2 ]; then log_success_msg else log_failure_msg fi ;; *) echo "Usage: ${0} {stop}" exit 1 ;; esac exit 0 # End sendsignals
#!/bin/sh ######################################################################## # Begin reboot # # Description : Reboot Scripts # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: reboot # Required-Start: # Should-Start: # Required-Stop: # Should-Stop: # Default-Start: 6 # Default-Stop: # Short-Description: Reboots the system. # Description: Reboots the System. # X-LFS-Provided-By: LFS ### END INIT INFO . /lib/lsb/init-functions case "${1}" in stop) log_info_msg "Restarting system..." reboot -d -f -i ;; *) echo "Usage: ${0} {stop}" exit 1 ;; esac # End reboot
#!/bin/sh ######################################################################## # Begin halt # # Description : Halt Script # # Authors : Gerard Beekmans - gerard AT linuxfromscratch D0T org # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## ### BEGIN INIT INFO # Provides: halt # Required-Start: # Should-Start: # Required-Stop: # Should-Stop: # Default-Start: 0 # Default-Stop: # Short-Description: Halts the system. # Description: Halts the System. # X-LFS-Provided-By: LFS ### END INIT INFO case "${1}" in stop) halt -d -f -i -p ;; *) echo "Usage: {stop}" exit 1 ;; esac # End halt
#!/bin/sh ######################################################################## # Begin scriptname # # Description : # # Authors : # # Version : LFS x.x # # Notes : # ######################################################################## ### BEGIN INIT INFO # Provides: template # Required-Start: # Should-Start: # Required-Stop: # Should-Stop: # Default-Start: # Default-Stop: # Short-Description: # Description: # X-LFS-Provided-By: ### END INIT INFO . /lib/lsb/init-functions case "${1}" in start) log_info_msg "Starting..." start_daemon fully_qualified_path ;; stop) log_info_msg "Stopping..." killproc fully_qualified_path ;; restart) ${0} stop sleep 1 ${0} start ;; *) echo "Usage: ${0} {start|stop|restart}" exit 1 ;; esac exit 0 # End scriptname
######################################################################## # Begin /etc/sysconfig/modules # # Description : Module auto-loading configuration # # Authors : # # Version : 00.00 # # Notes : The syntax of this file is as follows: # <module> [<arg1> <arg2> ...] # # Each module should be on its own line, and any options that you want # passed to the module should follow it. The line deliminator is either # a space or a tab. ######################################################################## # End /etc/sysconfig/modules
######################################################################## # Begin /etc/sysconfig/createfiles # # Description : Createfiles script config file # # Authors : # # Version : 00.00 # # Notes : The syntax of this file is as follows: # if type is equal to "file" or "dir" # <filename> <type> <permissions> <user> <group> # if type is equal to "dev" # <filename> <type> <permissions> <user> <group> <devtype> # <major> <minor> # # <filename> is the name of the file which is to be created # <type> is either file, dir, or dev. # file creates a new file # dir creates a new directory # dev creates a new device # <devtype> is either block, char or pipe # block creates a block device # char creates a character deivce # pipe creates a pipe, this will ignore the <major> and # <minor> fields # <major> and <minor> are the major and minor numbers used for # the device. ######################################################################## # End /etc/sysconfig/createfiles
######################################################################## # Begin /etc/sysconfig/udev_retry # # Description : udev_retry script configuration # # Authors : # # Version : 00.00 # # Notes : Each subsystem that may need to be re-triggered after mountfs # runs should be listed in this file. Probable subsystems to be # listed here are rtc (due to /var/lib/hwclock/adjtime) and sound # (due to both /var/lib/alsa/asound.state and /usr/sbin/alsactl). # Entries are whitespace-separated. ######################################################################## rtc # End /etc/sysconfig/udev_retry
#!/bin/sh ######################################################################## # Begin /sbin/ifup # # Description : Interface Up # # Authors : Nathan Coulson - nathan AT linuxfromscratch D0T org # Kevin P. Fleming - kpfleming@linuxfromscratch.org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # DJ Lucas - dj AT linuxfromscratch D0T org # # Version : LFS 7.7 # # Notes : The IFCONFIG variable is passed to the SERVICE script # in the /lib/services directory, to indicate what file the # service should source to get interface specifications. # ######################################################################## up() { log_info_msg "Bringing up the ${1} interface..." if ip link show $1 > /dev/null 2>&1; then link_status=`ip link show $1` if [ -n "${link_status}" ]; then if ! echo "${link_status}" | grep -q UP; then ip link set $1 up fi fi else log_failure_msg "Interface ${IFACE} doesn't exist." exit 1 fi evaluate_retval } RELEASE="7.7" USAGE="Usage: $0 [ -hV ] [--help] [--version] interface" VERSTR="LFS ifup, version ${RELEASE}" while [ $# -gt 0 ]; do case "$1" in --help | -h) help="y"; break ;; --version | -V) echo "${VERSTR}"; exit 0 ;; -*) echo "ifup: ${1}: invalid option" >&2 echo "${USAGE}" >& 2 exit 2 ;; *) break ;; esac done if [ -n "$help" ]; then echo "${VERSTR}" echo "${USAGE}" echo cat << HERE_EOF ifup is used to bring up a network interface. The interface parameter, e.g. eth0 or eth0:2, must match the trailing part of the interface specifications file, e.g. /etc/sysconfig/ifconfig.eth0:2. HERE_EOF exit 0 fi file=/etc/sysconfig/ifconfig.${1} # Skip backup files [ "${file}" = "${file%""~""}" ] || exit 0 . /lib/lsb/init-functions if [ ! -r "${file}" ]; then log_failure_msg "Unable to bring up ${1} interface! ${file} is missing or cannot be accessed." exit 1 fi . $file if [ "$IFACE" = "" ]; then log_failure_msg "Unable to bring up ${1} interface! ${file} does not define an interface [IFACE]." exit 1 fi # Do not process this service if started by boot, and ONBOOT # is not set to yes if [ "${IN_BOOT}" = "1" -a "${ONBOOT}" != "yes" ]; then exit 0 fi # Bring up the interface if [ "$VIRTINT" != "yes" ]; then up ${IFACE} fi for S in ${SERVICE}; do if [ ! -x "/lib/services/${S}" ]; then MSG="\nUnable to process ${file}. Either " MSG="${MSG}the SERVICE '${S} was not present " MSG="${MSG}or cannot be executed." log_failure_msg "$MSG" exit 1 fi done if [ "${SERVICE}" = "wpa" ]; then log_success_msg; fi # Create/configure the interface for S in ${SERVICE}; do IFCONFIG=${file} /lib/services/${S} ${IFACE} up done # Set link up virtual interfaces if [ "${VIRTINT}" == "yes" ]; then up ${IFACE} fi # Bring up any additional interface components for I in $INTERFACE_COMPONENTS; do up $I; done # Set MTU if requested. Check if MTU has a "good" value. if test -n "${MTU}"; then if [[ ${MTU} =~ ^[0-9]+$ ]] && [[ $MTU -ge 68 ]] ; then for I in $IFACE $INTERFACE_COMPONENTS; do ip link set dev $I mtu $MTU; done else log_info_msg2 "Invalid MTU $MTU" fi fi # Set the route default gateway if requested if [ -n "${GATEWAY}" ]; then if ip route | grep -q default; then log_warning_msg "Gateway already setup; skipping." else log_info_msg "Adding default gateway ${GATEWAY} to the ${IFACE} interface..." ip route add default via ${GATEWAY} dev ${IFACE} evaluate_retval fi fi # End /sbin/ifup
#!/bin/bash ######################################################################## # Begin /sbin/ifdown # # Description : Interface Down # # Authors : Nathan Coulson - nathan AT linuxfromscratch D0T org # Kevin P. Fleming - kpfleming@linuxfromscratch.org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # # Notes : the IFCONFIG variable is passed to the scripts found # in the /lib/services directory, to indicate what file the # service should source to get interface specifications. # ######################################################################## RELEASE="7.0" USAGE="Usage: $0 [ -hV ] [--help] [--version] interface" VERSTR="LFS ifdown, version ${RELEASE}" while [ $# -gt 0 ]; do case "$1" in --help | -h) help="y"; break ;; --version | -V) echo "${VERSTR}"; exit 0 ;; -*) echo "ifup: ${1}: invalid option" >&2 echo "${USAGE}" >& 2 exit 2 ;; *) break ;; esac done if [ -n "$help" ]; then echo "${VERSTR}" echo "${USAGE}" echo cat << HERE_EOF ifdown is used to bring down a network interface. The interface parameter, e.g. eth0 or eth0:2, must match the trailing part of the interface specifications file, e.g. /etc/sysconfig/ifconfig.eth0:2. HERE_EOF exit 0 fi file=/etc/sysconfig/ifconfig.${1} # Skip backup files [ "${file}" = "${file%""~""}" ] || exit 0 . /lib/lsb/init-functions if [ ! -r "${file}" ]; then log_warning_msg "${file} is missing or cannot be accessed." exit 1 fi . ${file} if [ "$IFACE" = "" ]; then log_failure_msg "${file} does not define an interface [IFACE]." exit 1 fi # We only need to first service to bring down the interface S=`echo ${SERVICE} | cut -f1 -d" "` if ip link show ${IFACE} > /dev/null 2>&1; then if [ -n "${S}" -a -x "/lib/services/${S}" ]; then IFCONFIG=${file} /lib/services/${S} ${IFACE} down else MSG="Unable to process ${file}. Either " MSG="${MSG}the SERVICE variable was not set " MSG="${MSG}or the specified service cannot be executed." log_failure_msg "$MSG" exit 1 fi else log_warning_msg "Interface ${1} doesn't exist." fi # Leave the interface up if there are additional interfaces in the device link_status=`ip link show ${IFACE} 2>/dev/null` if [ -n "${link_status}" ]; then if [ "$(echo "${link_status}" | grep UP)" != "" ]; then if [ "$(ip addr show ${IFACE} | grep 'inet ')" == "" ]; then log_info_msg "Bringing down the ${IFACE} interface..." ip link set ${IFACE} down evaluate_retval fi fi fi # End /sbin/ifdown
#!/bin/sh ######################################################################## # Begin /lib/services/ipv4-static # # Description : IPV4 Static Boot Script # # Authors : Nathan Coulson - nathan AT linuxfromscratch D0T org # Kevin P. Fleming - kpfleming@linuxfromscratch.org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## . /lib/lsb/init-functions . ${IFCONFIG} if [ -z "${IP}" ]; then log_failure_msg "\nIP variable missing from ${IFCONFIG}, cannot continue." exit 1 fi if [ -z "${PREFIX}" -a -z "${PEER}" ]; then log_warning_msg "\nPREFIX variable missing from ${IFCONFIG}, assuming 24." PREFIX=24 args="${args} ${IP}/${PREFIX}" elif [ -n "${PREFIX}" -a -n "${PEER}" ]; then log_failure_msg "\nPREFIX and PEER both specified in ${IFCONFIG}, cannot continue." exit 1 elif [ -n "${PREFIX}" ]; then args="${args} ${IP}/${PREFIX}" elif [ -n "${PEER}" ]; then args="${args} ${IP} peer ${PEER}" fi if [ -n "${LABEL}" ]; then args="${args} label ${LABEL}" fi if [ -n "${BROADCAST}" ]; then args="${args} broadcast ${BROADCAST}" fi case "${2}" in up) if [ "$(ip addr show ${1} 2>/dev/null | grep ${IP}/)" = "" ]; then log_info_msg "Adding IPv4 address ${IP} to the ${1} interface..." ip addr add ${args} dev ${1} evaluate_retval else log_warning_msg "Cannot add IPv4 address ${IP} to ${1}. Already present." fi ;; down) if [ "$(ip addr show ${1} 2>/dev/null | grep ${IP}/)" != "" ]; then log_info_msg "Removing IPv4 address ${IP} from the ${1} interface..." ip addr del ${args} dev ${1} evaluate_retval fi if [ -n "${GATEWAY}" ]; then # Only remove the gateway if there are no remaining ipv4 addresses if [ "$(ip addr show ${1} 2>/dev/null | grep 'inet ')" != "" ]; then log_info_msg "Removing default gateway..." ip route del default evaluate_retval fi fi ;; *) echo "Usage: ${0} [interface] {up|down}" exit 1 ;; esac # End /lib/services/ipv4-static
#!/bin/sh ######################################################################## # Begin /lib/services/ipv4-static-route # # Description : IPV4 Static Route Script # # Authors : Kevin P. Fleming - kpfleming@linuxfromscratch.org # DJ Lucas - dj AT linuxfromscratch D0T org # Update : Bruce Dubbs - bdubbs AT linuxfromscratch D0T org # # Version : LFS 7.0 # ######################################################################## . /lib/lsb/init-functions . ${IFCONFIG} case "${TYPE}" in ("" | "network") need_ip=1 need_gateway=1 ;; ("default") need_gateway=1 args="${args} default" desc="default" ;; ("host") need_ip=1 ;; ("unreachable") need_ip=1 args="${args} unreachable" desc="unreachable " ;; (*) log_failure_msg "Unknown route type (${TYPE}) in ${IFCONFIG}, cannot continue." exit 1 ;; esac if [ -n "${GATEWAY}" ]; then MSG="The GATEWAY variable cannot be set in ${IFCONFIG} for static routes.\n" log_failure_msg "$MSG Use STATIC_GATEWAY only, cannot continue" exit 1 fi if [ -n "${need_ip}" ]; then if [ -z "${IP}" ]; then log_failure_msg "IP variable missing from ${IFCONFIG}, cannot continue." exit 1 fi if [ -z "${PREFIX}" ]; then log_failure_msg "PREFIX variable missing from ${IFCONFIG}, cannot continue." exit 1 fi args="${args} ${IP}/${PREFIX}" desc="${desc}${IP}/${PREFIX}" fi if [ -n "${need_gateway}" ]; then if [ -z "${STATIC_GATEWAY}" ]; then log_failure_msg "STATIC_GATEWAY variable missing from ${IFCONFIG}, cannot continue." exit 1 fi args="${args} via ${STATIC_GATEWAY}" fi if [ -n "${SOURCE}" ]; then args="${args} src ${SOURCE}" fi case "${2}" in up) log_info_msg "Adding '${desc}' route to the ${1} interface..." ip route add ${args} dev ${1} evaluate_retval ;; down) log_info_msg "Removing '${desc}' route from the ${1} interface..." ip route del ${args} dev ${1} evaluate_retval ;; *) echo "Usage: ${0} [interface] {up|down}" exit 1 ;; esac # End /lib/services/ipv4-static-route
为了方便,在本附录中列出 udev 规则。它们的安装过程一般通过 第 6.77 节 “Eudev-3.2.8” 的命令完成。
# /etc/udev/rules.d/55-lfs.rules: Rule definitions for LFS. # Core kernel devices # This causes the system clock to be set as soon as /dev/rtc becomes available. SUBSYSTEM=="rtc", ACTION=="add", MODE="0644", RUN+="/etc/rc.d/init.d/setclock start" KERNEL=="rtc", ACTION=="add", MODE="0644", RUN+="/etc/rc.d/init.d/setclock start" # Comms devices KERNEL=="ippp[0-9]*", GROUP="dialout" KERNEL=="isdn[0-9]*", GROUP="dialout" KERNEL=="isdnctrl[0-9]*", GROUP="dialout" KERNEL=="dcbri[0-9]*", GROUP="dialout"
许可证在法律上以原文为准,译者没有翻译,以防律师函。
本书在 Creative Commons Attribution-NonCommercial-ShareAlike 2.0 License 下发行。
从本书中能够提取出的计算机指令在 MIT License 下发行。
Creative Commons Legal Code
Attribution-NonCommercial-ShareAlike 2.0
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
License
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
Definitions
"Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
"Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
"Licensor" means the individual or entity that offers the Work under the terms of this License.
"Original Author" means the individual or entity who created the Work.
"Work" means the copyrightable work of authorship offered under the terms of this License.
"You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
"License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, Noncommercial, ShareAlike.
Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
to create and reproduce Derivative Works;
to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works;
The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Sections 4(e) and 4(f).
Restrictions.The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any reference to such Licensor or the Original Author, as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any reference to such Licensor or the Original Author, as requested.
You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-NonCommercial-ShareAlike 2.0 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and give the Original Author credit reasonable to the medium or means You are utilizing by conveying the name (or pseudonym if applicable) of the Original Author if supplied; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
For the avoidance of doubt, where the Work is a musical composition:
Performance Royalties Under Blanket Licenses. Licensor reserves the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work if that performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
Mechanical Rights and Statutory Royalties. Licensor reserves the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions), if Your distribution of such cover version is primarily intended for or directed toward commercial advantage or private monetary compensation. 6. Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor reserves the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions), if Your public digital performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor reserves the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions), if Your public digital performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
Representations, Warranties and Disclaimer
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
Termination
This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
Miscellaneous
Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
Creative Commons may be contacted at http://creativecommons.org/.
Copyright © 1999-2019 Gerard Beekmans
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.