该软件包的详细信息位于 第 6.16.2 节 「Binutils 内容」。
版权 © 1999-2019 Gerard Beekmans
Copyright © 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 From Scratch 项目诞生了。
这本 Linux From Scratch 手册是该项目的重中之重。它为你提供了设计和构建自己的系统所需的背景知识和指令。虽然本手册给出了一个模板,一个最后能正常工作的系统,但你可以自由地改变指令以适应自身的需求,从某种程度上说,这是也本项目的一个重要部分。你仍然可以控制一切,我们只是在你起航之前助你一臂之力。
我衷心地希望你能享受构建属于自己的 Linux From Scratch 系统的过程,并对坐拥一个真正属于自己的系统所带来诸多好处能做到甘之如饴。
--
Gerard Beekmans
gerard AT linuxfromscratch D0T org
说到为什么要读这本书,我想,原因一定有很多。有些人可能会提出这样的疑问:「都已经有现成的 Linux 系统可以下载和安装了,你为什么还要多此一举的从从头构建一个呢?」
此项目存在的一个重要原因是帮助你了解 Linux 的内部是如何工作的。通过构建 LFS 系统,你可以更好的理解 Linux 的工作原理,以及程序是如何协同工作和相互依赖的。最棒的是,这个学习经历能给你提供自定义 Linux 系统以满足你自己独特需求的能力。
另一个重要的原因是,你对系统本身有更多的控制权,而无需去关心别人的 Linux 是如何实现的。在 LFS 下,你就是主宰,系统的各个方面都需要你亲力亲为。
LFS 可以让你创建极其精简的 Linux 系统。在安装那些常规的 Linux 系统时,你往往会迫不得已而安装那些你用不到的(甚至你都不知道它们是干什么用的)程序。这些程序也许会浪费你的硬件资源。或许你可能会说,现在计算机的资源那么丰富,稍微浪费一些又有什么关系呢。但是你依旧要考虑到可引导 CD、U 盘或者是一些嵌入式的环境,它们对资源高度敏感,这也恰恰是 LFS 所擅长的地方。
另一个优势便是,自定义的 Linux 系统有着更高的安全性。通过从源码构建一个完整的系统,你有权审核所有的代码以及所打入的安全补丁。也就没必要去在等别人编译修复该安全漏洞的二进制程序了。而且,除非你亲自检查了补丁文件并且做了完整的测试,否则你又怎么能确信,新的二进制程序确实是正确的编译且解决了问题呢?
从头构建一个基本可用的 Linux 系统是本书的目标,你即使不打算这么做,也还是能从这本书的内容中有所收获。
支撑你从头构建属于自己的 LFS 系统的原因实在是太多了。但说到最后,其中最重要的一个原因还是教学。随着你的 LFS 经验愈趋熟稔,这些信息和知识真正给你带来的力量,你也便了然于胸了。
LFS 当前主要支持 AMD/Intel 的 x86(32 位)和 x86_64(64 位)构架的 CPU。另外,本文档也涉及一些更改以允许 LFS 顺利地在 Power PC 和 ARM CPU 上运行。为了顺利构建 LFS,除了后面几页的内容外,你主要需要一个可以在当前 CPU 上正常运行的 Linux 系统,例如:早先版本的 LFS、Ubuntu、Red Hat/Fedora、SuSE 或者是在你的架构上可以运行的其它发行版。另外注意 32 位的发行版是可以在 64 位的 AMD/Intel 处理器上作为宿主机正常安装和运作的。
这里也说明一下关于 64 位系统的一些情况。与 32 位相比,大体上程序运行的速度虽稍微地快了那么一点点,但体积也稍微的大了那么一点点。以在 Core 2 Duo 处理器上运行的 LFS-6.5 系统为例,以下便是实测数据:
Architecture Build Time Build Size
32-bit 198.5 minutes 648 MB
64-bit 190.6 minutes 709 MB
正如你看到的,64 位程序仅仅比 32 位程序快了 4%,而体积大了 9%。由此可见,单纯的追逐 64 位系统其实并没有太大的必要。但是,假如你的电脑的内存超过了 4 GB 又或者说需要操作大于 4 GB 的数据,64 位系统的优势就比较明显了。
上述讨论只适用于对比相同硬件下的构建过程。现代的 64 位系统会比老的 64 位系统更快。LFS 作者建议,如果有机会,尽量在 64 位系统上进行构建。
假如按照本文的默认方式构建,那么你将得到一个「纯」64 位系统——这意味着你仅能够执行 64 位的程序。构建「multi-lib」并不是不可以,但是这意味着很多的程序都需要编译两次:一次为 32 位程序编译,一次为 64 位程序编译。不过,本文档并不涉及这部份的内容,因为这些内容会干扰用户学习如何构建一份最基本的 Linux 系统。你可以通过阅读 Cross Linux From Scratch 的相关内容获得有关该话题的更多帮助。
LFS 的结构尽可能的遵循 Linux 的标准。主要的标准有:
LSB 有四个独立的标准:核心(Core)、桌面(Desktop)、运行时语言(Runtime Languages)和成像(Imaging)。除了通用要求,还有架构特定要求。此外还有两个领域在试行:分别是 Gtk3 和图形(Graphics)。LFS 试图遵从前一节中所讨论的架构要求。
很多人不认同 LSB 的要求。定义它的主要目的是确保私有软件能够在兼容的系统上安装并正常运行。由于 LFS 是基于源代码的,用户对于需要什么软件包有完全的控制权,有很多人就选择不安装 LSB 规范要求的软件包。
完全可以创建一个能够通过 LSB 认证测试的完整 LFS 系统,但这需要很多 LFS 范围之外的额外软件包。在 BLFS 中有这些额外软件包的安装说明。
LSB 核心: |
Bash, Bc, Binutils, Coreutils, Diffutils, File, Findutils, Gawk, Grep, Gzip, M4, Man-DB, Ncurses, Procps, Psmisc, Sed, Shadow, Tar, Util-linux, Zlib |
LSB 桌面: |
无 |
LSB 运行时语言: |
Perl |
LSB 成像: |
无 |
LSB Gtk3 和 LSB 图形(试用): |
无 |
LSB 核心: |
At, Batch (At 的一部分), Cpio, Ed, Fcrontab, Initd-tools, Lsb_release, NSPR, NSS, PAM, Pax, Sendmail (或 Postfix 或 Exim), time |
LSB 桌面: |
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 运行时语言: |
Python, Libxml2, Libxslt |
LSB 成像: |
CUPS, Cups-filters, Ghostscript, SANE |
LSB Gtk3 和 LSB 图形(试用): |
GTK+3 |
正如前文所述,LFS 的目标是构建一个完整可用的基础级系统。这其中便包含了,所有地软件包需要复制自身,直至为用户提供出一个相对最小的基础,然后根据用户的选择,在这个基础上定制出一个更完善的系统。这便意味着 LFS 并不是最小可用系统的代名词。其中包含的几个重要软件包,也并非严格需求的。下面的列表介绍了本书中选择每个软件包的理由。
Acl
这个软件包中,包含管理访问控制列表(ACL)的工具,用于定义文件和目录更细粒度的自主访问权。
Attr
这个软件包中包含的程序,用于管理文件系统对象的扩展属性。
Autoconf
这个软件包中包含的程序,能根据开发者的模板自动生成配置源代码的 shell 脚本。通常需要在更新构建过程之后重新构建一个软件包。
Automake
这个软件包中,包含了通过模板生成 Make 文件的程序。通常需要在更新构建过程之后重新构建一个软件包。
Bash
这个软件包用于能满足 LSB 核心的需求,为系统提供 Bourne Shell 接口。选择它而非其它 shell 软件包的理由,是因为它的通用性以及在基本 shell 功能上的扩展能力。
Bc
这个软件包提供了一种任意精度的数值处理语言。在构建 Linux 内核时需要它。
Binutils
这个软件包中,包含了一个链接器、一个汇编器和其它处理对象文件的工具。编译 LFS 以及 BLFS 系统中的大部分软件包,包需要该软件包中的程序。
Bison
这个软件包中有 GNU 版的 yacc(Yet Another Compiler Compiler),用于构建一些其它的 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 会用到。
Grep
这个软件包中,包含了一些在文件中搜索的程序。大部分软件包的构建脚本会用到。
Groff
这个软件包中,包含了处理和格式化文本的程序。其中一个重要的功能是格式化 man 页面。
GRUB
这个软件包是 Grand Unified Boot Loader。是诸多可用的引导加载器中的一个,但是最灵活。
Gzip
这个软件包中,包含了一些压缩和解压缩文件的程序。解压很多 LFS 以及 BLFS 的软件包时需要用到。
Iana-etc
这个软件包中,包含提供了网络服务和协议的数据。启用合适的网络功能时会用到。
Inetutils
这个软件包中,包含基本网络管理的程序。
Intltool
这个软件包中,包含能从源文件中抽取可翻译字符串的工具。
IProute2
这个软件包中,包含了一些基本和高级的 IPv4 和 IPv6 网络的程序。由于它的 IPv6 功能,所以选择它来取代其它的常见网络工具包(net-tools)。
Kbd
这个软件包中,包含一些键盘映射文件,用于非 US 键盘的键盘工具以及一些控制台字体。
Kmod
这个软件包中,包含一些用于管理 Linux 内核模块的程序。
Less
这个软件包中,包含了一个很好的文本文件查看器,允许查看文件的时候向上或向下滚动。Man-DB 用它来查看 man 页面。
Libcap
这个软件包中,包含实现了可以用于 Linux 内核的,从用户空间到 POSIX 1003.1e 的接口。
Libelf
elfutils 项目为 ELF 文件和 DWARF 数据提供了库和工具。该软件包中的大部分实用程序都可以在别的软件包中使用,但是该库需求使用默认(且最有效的)配置构建 Linux 内核。
Libffi
这个软件包实现了一个可移植的、高级编程接口,以适应各种调用惯例。在编译时,一些程序可能还不知道给函数传递的是什么参数。例如,一个解释器可能会在运行时才被告知函数调用中所传递的参数的个数和类型。Libffi 可以应用于这些程序,充当从解释程序到被编译代码的桥梁。
Libpipeline
这个软件包中,包含一个以灵活和便捷的方式操作子进程流水线的库。Man-DB 软件包会用到。
Libtool
这个软件包中,包含了一些 GNU 通用库支持脚本。它降低了在一致、可移植的接口上使用共享库的复杂度。在其他 LFS 软件包的测试套件里需要它。
Linux 内核
这个软件包就是操作系统。即我们常说的「GNU/Linux」中的「Linux」。
M4
这个软件包中,包含一个普通的文本宏处理器,作为其它程序的构建工具使用。
Make
这个软件包中,包含了一个指导软件包构建的程序。LFS 中的几乎每个包都需要它。
Man-DB
这个软件包中,包含了一些查找和查看 man 页面的程序。由于其更好的国际化功能,用来代替 man 软件包。它提供了 man 程序。
Man-pages
这个软件包中,包含基本的 Linux man 页面的真正内容。
Meson
这个软件包提供了自动化构建软件的工具。Meson 的主要目标是最小化软件开发者,在其构建的系统上,花费的配置时间。
MPC
这个软件包中,包含复数运算的函数。Gcc 需要它。
MPFR
这个软件包中,包含多精度运算的函数。Gcc 需要它。
Ninja
这个软件包中,包含了一个专注于速度的小型构建系统。其输入文件由更高层次的构建系统生成,并且尽可能快速地运行构建过程。
Ncurses
这个软件包中,包含了一些处理字符界面的不依赖特定终端的库。通常用来为菜单系统提供光标控制。一些 LFS 的软件包会用到。
Openssl
这个软件包中,提供了与加密相关管理工具和库。提供的这些加密功能对于他软件包(包括 Linux 内核)而言非常有用。
Patch
这个软件包中,包含了一个通过 patch 文件来修改或新建文件的程序,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 软件包测试套件中使用的工具命令语言。仅安装在临时工具链中。
Texinfo
这个软件包中,包含了一些读、写以及转换信息页面的程序。在很多 LFS 软件包的安装过程中会使用它。
Util-linux
该软件包中,包含了许多工具。其中有处理文件系统、控制台、分区和消息的工具。
Vim
这个软件包中,包含一个编辑器。由于 vi 编辑器的经典以及大量的强大功能而选择它。对很多用户来说一个编辑器是一个非常个人的选择,如果需要的话也可以选择其它编辑器。
XML::Parser
这个软件包是和 Expat 交互的 Perl 模块。
XZ Utils
这个软件包中,包含了一些压缩和解压缩文件的程序。通常它的压缩率最高,在解压 XZ 或者 LZMA 格式的软件包时非常有用。
Zlib
这个软件包中,包含了一些程序所使用的压缩和解压缩功能。
构建一个 LFS 系统并不是一个简单的任务。它要求对 Unix 系统管理有一定水平的了解,以便可以解决问题并正确地执行所列出的命令。特别是,至少你应该拥有使用命令行(shell),复制或移动文件和目录、列出目录和文件内容、切换当前目录的能力。同时也希望你拥有使用和安装 Linux 软件的基本知识。
因为 LFS 书中假设你至少有这些基本的技能,所以众多的 LFS 提供的论坛看起来在这方面也给你提供不了太多的帮助。你会发现对于这些基本知识的问题并不会得到解答,或者会简单的建议你去看一下 LFS 主要的提前阅读列表。
构建 LFS 系统之前,我们建议阅读以下内容:
构建软件 HOWTO http://www.tldp.org/HOWTO/Software-Building-HOWTO.html
这是一个在 Linux 上编译和安装「通用」Unix 软件包的综合指南。尽管成文已有一段时间,但其对于编译和安装软件中所需的基本技能,还算一个不错的总结。
初学者源码安装指南 http://moi.vonos.net/linux/beginners-installing-from-source/
该指南很好的总结了从源码构建软件时所需要的基础技能和技巧。
为了能更更容易理解,这里有一些全书使用的排版约定。本段,会包含一些来自 Linux From Scratch 中排版格式的示例。
./configure --prefix=/usr
这种形式的文本,设计出来就是为了让你照着输入的,除非周围文本中另有说明。部分的解释段落和会用它,以指出引用的那条命令。(译注:由于文字排版原因,其中的内容即便是注释,也不会翻译,下同。)
某些情况下,会在一行的行末添加反斜,将一个逻辑行的内容分写成两个或更多的物理行。
CC="gcc -B/usr/bin/" ../binutils-2.18/configure \ --prefix=/tools --disable-nls --disable-werror
注意,反斜杠的后面必须紧跟一个回车符。其它的空白字符,例如空格和 Tab 将导致错误的结果。
install-info: unknown option '--dir-file=/mnt/lfs/usr/info/dir'
这种形式的文本(等宽文本)用于显示屏幕输出,通常是所运行命令的输出结果。这种形式也用于文件名的显示,例如:
/etc/ld.so.conf
。
强调
这种形式的文本在本书中有多重目的。主要目的是强调重要的内容或项目。
http://www.linuxfromscratch.org/
这种格式用来表示 LFS 社区内部及外部网页的超链接。包括 HOWTO,下载地址和网站等。
cat > $LFS/etc/group << "EOF"
root:x:0:
bin:x:1:
......
EOF
这种格式在创建配置文件中会使用。第一个命令告诉系统新建一个文件 $LFS/etc/group
,然后后面的行中不论在输入了什么都会输入到文件中,直至遇到文件终结符(EOF)。因此,这整个部分会如看到的那样输入。
<需要替换的文本>
这种格式用来封装需要替换为合适内容的文本,或者复制粘贴操作。
[可选的文本]
这种格式用来封装可选项文本。
passwd(5)
这种格式用来表示特定的手册 (man) 页面。括号内的数字表示该手册的特定部分。例如 passwd 有两个手册页面。在 LFS
安装说明中,这两个手册页面会表示为 /usr/share/man/man1/passwd.1
以及 /usr/share/man/man5/passwd.5
。当书中使用 passwd(5)
时它特指 /usr/share/man/man5/passwd.5
。man passwd
会输出它找到匹配「passwd」的第一个手册页面,也就是 /usr/share/man/man1/passwd.1
。在这个例子中,你需要执行
man 5 passwd
才能阅读指定的手册页面。应该注意的是,大部分的手册页面在不同部分不会有重复的页面名字。因此,man <program
name>
通常就足够了。
本书分为以下几个部分。
第一部分解释了一些安装 LFS 时的重要注意事项。同时还提供了本书的基本信息。
第二部分描述了构建的一些准备工作,包括分区,下载软件包,编译一些临时工具。
第三部分引导用户开始 LFS 系统的构建——逐一的编译和安装所有的软件包,设置启动脚本,安装内核。生成的 Linux 系统是继续编译其它一系列软件的基础,通过那些软件来扩展系统,系统才能更好地满足我们的需求。在本书的最后,我们还给出了一个便于使用的引用列表,包括安装好的程序、库和一些重要文件。
构建 LFS 系统的软件是在不断的更新和改进的,也许在这本 LFS 书发布以后就会出现一些安全警告或者是修复了一些 bug。请在着手构建之前访问 http://www.linuxfromscratch.org/lfs/errata/9.0/ ,检查此版本 LFS 软件包的版本或说明,是否需要修改以解决安全漏洞或其它 bug。你应该关注本书的一切变化,且将其应用到 LFS 系统的构建中去。
如果 LFS 的中文手册翻译的有问题,或是你对 LFS 中文手册有自己的建议和意见。可以通过中文手册的 Github 库 LFS-BOOK https://github.com/LCTT/LFS-BOOK 找到并告诉我们。
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(切换成 root 用户的)命令来进入一个虚拟环境并启动一个新的 shell,且整个虚拟环境就将作为 LFS 分区的根目录。这个过程,和重起电脑并指示内核将 LFS 分区挂载为根分区十分相像。只是系统并没有真正重启,而是通过 chroot 实现的,而这样做的原因是想要实现一个正真可以启动的系统还有很多工作没有完成。使用「chrooting」最大的好处在于,构建 LFS 的同时可以继续使用宿主系统。在等待包编译的过程中,你可以继续正常的使用电脑。
在完成基本的安装过程之后,您还需要阅读 第 7 章 对系统做简单的配置,然后阅读 第 8 章 配置内核和引导器(bootloader)。第 9 章 包含了一些 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 日。如果距离这个时间已超过 6 个月,那么应该已经有更新和更好的版本了。你可以访问 http://www.linuxfromscratch.org/mirrors.html 的任意镜像站点来获取他们。
下面是本书上一次发布之后的更新列表。
更新日志条目:
2019-09-01
[bdubbs] - LFS-9.0 发布。
2019-08-14
2019-08-04
[bdubbs] - 通过将 include 文件添加到 glibc 头文件来解决 linux-5.2 导致的问题。
2019-08-03
2019-07-21
2019-07-14
[bdubbs] - 修复对于 binutils-2.32 gold linker 的测试。修复 #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 报告了这个问题。通过在 make 安装命令中删除 unrecognized/unused --docdir 并替换成 "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 bootscripts 的外观更改。
2019-05-19
2019-05-03
2019-04-23
[bdubbs] - 应用一个变更,让最新版本的 gcc 可以正确构建 Perl。
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
查找 #LFS-support 加入讨论。
LFS 项目在世界范围内有许多镜像站点,方便大家访问我们的网站以及下载所需文件。请访问 LFS 站点 http://www.linuxfromscratch.org/mirrors.html 查看最新的镜像站点列表。
如果在使用本书的过程中有疑问或碰到问题,可以先去看下 FAQ 页面http://www.linuxfromscratch.org/faq/#generalfaq。那里已经解决了很多经常遇到的问题。如果你的问题在那里找不到答案,可以先尝试找出问题的原因。下面页面里的提示可以提供一些帮你定位问题的帮助:http://www.linuxfromscratch.org/hints/downloads/files/errors.txt。
如果在 FAQ 里找不到你遇到的问题,还可以在这个邮件列表里搜索一下:http://www.linuxfromscratch.org/search.html。
我们还有一个很棒的 LFS 社区,大家都很乐意通过邮件列表和 IRC 提供协助(参看本书的 第 1.4 节 「资源」)。不过,我们每天收到的支持问题中有很多其实可以通过查看 FAQ 和搜索邮件列表轻松解决。所以为了让我们能最大可能地提供更好的协助,希望你碰到问题能自己先研究一下。这样可以让我们有精力去关注更罕见问题。如果你自己搜索不到解决方式,请在你的帮助请求里收集所有相关信息(下面提到的)。
除了对遇到的问题的简短描述外,帮助请求里还需要包含的以下的关键信息:
所用手册的版本(当前版本为 9.0 )
用来构建 LFS 的宿主机器的 Linux 发行版以及版本
本手册 宿主系统需求 中脚本的所有输出信息
出现问题的软件包或本手册的章节
精确的错误信息或表现形式
注明你是否已经脱离了本书的内容
脱离本手册的内容并不意味着我们就一定不会帮你。毕竟,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://catb.org/~esr/faqs/smart-questions.html 中的文章告知人们如何在互联网上寻求帮助,可以去看一下,且遵循其中的提问原则进行提问,可以提高获得帮助的概率(亦让别人对你伸出援手更加方便)。
你的主机系统应具有以下指定最低版本的软件。 对于大多数现代 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,则需使用更新版本的内核将其替换。有两种方法可以解决这个问题。首先,查看你的 Linux 供应商是否提供 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 的构建需要一气呵成。但是如果 LFS 恢复时的状态和之前不同,则某些程序必须在此之后重新构建。
当在主机系统各种完成此章节的内容,重启时,需要注意一下的内容:
程序在 2.4 节以后都是以 root 用户完成的。所以需要重新将 LFS 的环境变量设置为 ROOT。
/mnt/lfs 分区必须挂载。
第 5 章涉及的所有指令都应该由 lfs 用户执行。所以在执行第 5 章的任务之前请先执行 su - lfs。
第 5.3 节 「通用编译指南」 中提及的程序是十分重要的。如果在安装任何软件包时存在问题,请确保所有之前已经解压的软件包已经删除。重新将它们解压后再完成此章节中的所有命令。
/mnt/lfs 分区必须挂载。
当进入 chroot 环境,LFS 环境变量必须设置为 root 用户。否则不得使用 LFS 变量。
必须挂载虚拟文件系统。这可以在进入 chroot 之前或者之后通过改变主机的虚拟终端,以 root 用户执行 第 6.2.2 节 「挂载和激活 /dev」 和 第 6.2.3 节 「挂载虚拟文件系统」 中的命令。
与绝大多数其它操作系统相同,安装 LFS 通常需要专门的分区。构建 LFS 系统比较推荐的方法是使用可用的空分区,或者如果条件允许,最好是在未分区的空间里新建分区。
最小化的系统需要大约 6 GB 的分区,这足以存储所有的源码包及满足编译的需求。但如果要将 LFS 作为主要的 Linux 系统,可能需要安装其它附加的软件,这将需要额外的空间。考虑到了日后所需的空间,一个 20 GB 的分区是比较合理的。LFS 系统本身并不会占用这么多的空间,但大分区将能提供充裕的临时储存空间,并为完成 LFS 以后添加附加功能留有余地。编译软件包可能需要较大的磁盘空间,但这些空间可以在软件包安装后回收。
由于编译过程中所需的内存(RAM)可能不足,用一个小型的磁盘分区作为 swap
空间是个不错的想法。内核会在此分区中储存较少使用的数据,从而为活动进程提供更多的内存。LFS 系统可以与宿主系统共用
swap
分区,这样就没有必要再新建一个了。
启动磁盘分区程序,通过如 cfdisk 或 fdisk 加上新分区所在的磁盘名——例如
/dev/sda
作为主要的磁盘。创建一个 Linux
本地分区,并按需创建 swap
分区。如果需要,可以参考
cfdisk(8)
或 fdisk(8)
。
对于有经验的用户,也可自行定制分区分案。新版 LFS 系统支持软件 RAID 阵列或 LVM 然而,这些方案需要用到 initramfs,这涉及到比较复杂的话题。并不建议首次尝试 LFS 的用户使用这样的分区方法。
请记住新分区的位置(例如,sda5
)。本书中将称其为 LFS
分区。还需要记住 swap
分区的位置。这些将会在/etc/fstab
文件中用到。
LFS 邮件列表中经常有人问到关于系统分区的建议。这一话题非常主观。大多数发行版默认情况下会使用整个磁盘,仅为交换分区保留一小部分空间,但由于种种原因,这并不适合 LFS。这样做会降低灵活性,使得多个发行版或几个 LFS 版本之间共享数据变得困难,也让备份更耗时,还会导致文件系统结构分配不合理而浪费磁盘空间。
为 LFS 根分区(不要与 /root
目录混淆)分配
10 GB 的空间是比较好的折中方案。这为构建 LFS 和大多数 BLFS
提供了足够的空间,也小到可以轻易创建多个分区用于实验。
大多数发行版会自动创建交换分区。一般来说,交换分区的推荐大小为物理内存的两倍左右,然而鲜少需要这样做。若是磁盘空间有限,可以设置为 2 GB的交换分区并查看磁盘的交换量。
发生内存交换其实并不好。通常,你只需要观察磁盘活动以及系统对命令的响应程度就能知道这个系统是否在进行交换。通常在使用非常不合理的命令时才会发生交换,如尝试编辑一个大小为 5 GB 的文件时。如果交换在你的系统上是常态,那最好的办法就是为你的系统添置更多的物理内存。
如果是在 GPT(GUID Partition Table,GUID 分区表)下分 boot 磁盘,那么必然会产生一个约 1 MB 左右的小分区,如果之前没有的话。这个分区可能还没有被格式化,但是它必须存在,GRUB 会在安装引导器的时候用到。如果使用 fdisk,该分区通常会被标记为「BIOS Boot」。而如果使用的是 gdisk,则分区代码应为 EF02 。
Grub Bios 分区必须位于 BIOS 用于引导系统的磁盘驱动器上。却不一定要和 LFS 的根分区位于同一个磁盘驱动器。一个系统里的磁盘可能会用到不同类型的分区表。该磁盘仅与 boot 磁盘的分区表类型有关。
在分配磁盘时,有些分区不是必须的,但却值得你考虑。以下列表并不全面,仅供参考。
/boot – 强烈推荐。此分区用于存储内核和其它启动信息。为了减少大容量磁盘启动时的潜在问题,尽量将该分区设为磁盘驱动器上第一个物理分区。100 MB 的空间就十分充裕了。
/home – 强烈推荐。home 目录可用于跨发行版或多个 LFS 版本之间共享用户自定义内容。应该将尽量多的磁盘都分配给 home 分区。
/usr – 独立的 /usr 分区常见于瘦客户端服务器或无盘工作站。LFS 通常不需要。5 GB 大小足以应付大部分安装。
/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 文件系统的最新版。提供了很多新的特性,包括纳秒级时间戳、创建和使用巨型文件(16TB)、以及速度的提升。
其它文件系统,包括 FAT32、NTFS、ReiserFS、JFS 和 XFS 等都在专门领域有其独到的作用。关于这些文件系统更多的信息可以参考http://en.wikipedia.org/wiki/Comparison_of_file_systems。
LFS 假设要将根分区(/)的文件系统的类型设置为 ext4。运行如下指令,为 LFS 分区创建 ext4
文件系统:
mkfs -v -t ext4 /dev/<xxx>
请替换 <xxx>
为实际的
/
分区设备名。
如果你已经有了现成的 swap
分区,不需要重新格式化。如果是新建的 swap
分区,需要用下面的命令初始化:
mkswap /dev/<yyy>
请替换 <yyy>
为实际的
swap
分区设备名。
在整本书里,会多次用到环境变量 LFS
。你应该确保这个变量在整个 LFS
构建过程中总是定义了的。它应该被设置为你将要构建的 LFS 系统的目录名——这里我们使用 /mnt/lfs
作为例子,但是选择哪一个目录是你的自由。如果你把 LFS
构建到一个单独的分区里,这个目录将成为那个分区的挂载点。选择一个本地目录并用以下命令设置该变量:
export LFS=/mnt/lfs
设置这个变量的好处在于有些命令可以直接写成类似 mkdir -v $LFS/tools这样。在处理这条命令时,会自动替换「$LFS」为「/mnt/lfs」(或任何这个变量所指的地方)。
不论何时,当你离开又重新进入这个工作环境时都不要忘了检查 LFS
是否设置(比如当你使用 su
切换到 root
或是另一个用户时)。使用如下命令检查
LFS
变量是否正确设置:
echo $LFS
请确保输出的是你构建 LFS 的那个目录的路径,如果你是按照例子设置的,那就是 /mnt/lfs
。如果你输出的路径不正确,请使用本页前一部分提供的命令把
$LFS
重新设置到正确的目录。
确保 LFS
变量一直为设置的一个方法是编辑你 home 目录下的
.bash_profile
文件和 /root/.bash_profile
,并输入上述 export 命令。另外,在
/etc/passwd
文件中,为所有需要的用户指定
LFS
变量的 shell 需要是 bash,以确保
/root/.bash_profile
文件作为登录过程的一部分,并入其中。
另一个考虑是登入宿主系统的方法。如果是通过图形显示管理器登入,那么就不会像寻常那样在虚拟终端启动时,采用用户的
.bash_profile
文件。在这种情况下,需要将
export 命令添加至 root 用户的 .bashrc
中。另外,有些发行版还有指令,在非交互式 bash 的调用中不让 .bashrc
指令运行。在非交互式使用的测试前请保证 export 命令已经添加。
至此,文件系统已经创建妥当,下一步就是访问这些分区了。为此,需要将这些建立的分区挂载到选定的挂载点。本书假定的挂载点为环境变量
LFS
指向的地址,如前文所述。
创建挂载点并用下面的命令挂载 LFS 文件系统:
mkdir -pv $LFS
mount -v -t ext4 /dev/<xxx>
$LFS
请用 LFS 分区替代 <xxx>
。
如果 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
分区时设置了什么参数。如果设置了 nosuid
和/或
nodev
参数,就需要重新挂载了。
上述说明假定你在 LFS 的过程中不会重启你的计算机。如果你关闭了你的系统,你将需要在每次重新开始构建过程时重新挂载 LFS 分区,或者修改你宿主系统的 /etc/fstab 文件,让每次重新启动后都自动挂载它。示例如下:
/dev/<xxx>
/mnt/lfs ext4 defaults 1 1
如果你还需使用其它可选分区,那就确保将它们也一并添加。
如果你要使用 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
设置目录的写权限和粘滞模式。「粘滞模式」是指,即便多个用户对某个目录有写权限,但仅有文件的所有者,能在粘滞目录中删除该文件。运行以下命令将开启目录的写权限和粘滞模式:
chmod -v a+wt $LFS/sources
一个简单的一口气下载所有软件包和补丁的方法是使用 wget-list 作为 wget 的输入。例如:
wget --input-file=wget-list --continue --directory-prefix=$LFS/sources
LCTT 译注:由于这些文件都分布在国外的不同站点上,因此有些下载的会很慢。非常感谢中国科学技术大学镜像站提供的 LFS 软件包:http://mirrors.ustc.edu.cn/lfs/lfs-packages/9.0/。可以使用我们制作的 wget-list-ustchttps://github.com/LCTT/LFS-BOOK/blob/9.0-translating/wget-list-ustc 方便下载。
另外,从 LFS-7.0 开始,多了一个单独的文件 md5sums,可以在正式开始前校验所有的文件是否都正确。将这个文件复制到
$LFS/sources
目录中并执行:
pushd $LFS/sources md5sum -c md5sums popd
下载或使用别的方式获取如下软件包:
下载: 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 内核版本应该都可以使用,除非在勘误页有其他说明。
对于网速较慢或带宽费用较高的用户如果希望更新 Linux 内核,可以将基线版本的软件包和补丁分开下载。针对次要版本中一连串的补丁程度的更新,这种操作方式也许能让你少费些时间,或者说功夫。
主页: 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 软件包花费时间的单位,或称「SBUs」,并给出一些关于软件包测试套件的信息。
所有 第 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
文件夹,也就是说编译器、汇编器以及链接器无论是在第五章中(我们仍然使用宿主机的一些工具的时)还是之后(当我们「chrooted」到
LFS 分区时)都可以工作。
当以 root
用户登录时,犯一个小错误可能会破坏或摧毁整个系统。因此,我们建议在本章中以非特权用户编译软件包。你当然可以使用你自己的用户名,但为了使其更容易建立一个干净的工作环境,创建一个名为
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
这个参数通过改变输入位置为特殊的空(null)设备,以防止可能从框架目录(默认是 /etc/skel
)复制文件。
lfs
这是创建的组和用户的实际名称。
要以 lfs
用户身份登录(相较于以 root
身份登录的情况下切换到 lfs
用户时,无需为 lfs
用户设置密码),需要给 lfs
用户一个密码:
passwd lfs
通过更改目录所有者为 lfs
,为用户 lfs
赋予了访问 $LFS/tools
目录的所有权限:
chown -v lfs $LFS/tools
如果你按照建议创建了单独的工作目录,给 lfs
用户赋予这个目录的所有权:
chown -v lfs $LFS/sources
下一步,以 lfs
用户身份登录。可以能通过一个虚拟控制台、显示控制器,或者下面的切换用户命令完成:
su - lfs
这个「-
」授意 su 启动登录 shell,而非 non-login
shell。关于这两种 shell 类型的区别,可以在 bash(1)
和 info
bash 中查看详细详情。
通过为 bash shell
创建两个开机启动的文件,设置合适的工作环境。当以 lfs
用户身份登录时,运行以下命令创建一个新的 .bash_profile
文件:
cat > ~/.bash_profile << "EOF"
exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash
EOF
当以 lfs
用户身份登录时,初始 shell 通常是一个
login 的 shell,它先读取宿主机的
/etc/profile
文件(很可能包括一些设定和环境变量),然后是 .bash_profile
文件。.bash_profile
中的命令 exec env -i.../bin/bash 用一个除了
HOME
,TERM
和 PS1
变量外,其他环境完全为空的新 shell 代替运行中的
shell。这能确保不会有潜在的和意想不到的危险环境变量,从宿主机泄露到构建环境中。这样做主要是为了确保环境的干净。
新的 shell 实例是一个 non-login
的 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
进行检索的时间和对同一个可执行文件的重复寻找。然而,新工具在安装后,应马上使用。通过关闭哈希功能,程序执行的时候就会一直搜索
PATH
。如此,新编译的工具一旦可用,shell 便能在马上在文件夹
$LFS/tools
中找到它们,而不会去记录存在于不同地方的旧版该程序。
设置用户文件新建时的掩码(umask)为 022,以确保新建的文件和目录只有其所有者可写,但任何人都可读可执行(假设系统调用的
open(2)
使用的是默认模式,新文件将使用 664
权限模式、文件夹为 755 模式)。
LFS
变量应设置成选定的挂载点。
LC_ALL
变量控制某些程序的本地化,使它们的消息遵循特定国家的惯例。设置 LC_ALL
为「POSIX」或「C」(两者是等价的),确保在 chroot
环境中一切能如期望的那样进行。
LFS_TGT
变量设置了一个虽非默认,但在构建交叉编译器、连接器和交叉编译临时工作链时,用得上到的兼容的机器说明。第 5.2 节 「工具链技术说明」中包含更多信息。
通过把 /tools/bin
放在标准 PATH
变量的前面,使得所有在 第 5 章 中安装的程序,一经安装 shell
便能马上使用。与之配合的关闭哈希功能,能在第五章环境中的程序在可用的情况下,限制使用宿主机中旧程序的风险。
最后,启用刚才创建的用户配置,为构建临时工具完全准备好环境:
source ~/.bash_profile
在着手之前,不少人想知道编译和安装一个软件包到底需要多长的时间。因为 Linux From Scratch 能够运行于众多的硬件上,所以具体的编译时间无法一概而论。举一个简单的例子:在最快的系统上,编译最大的软件包(Glibc)需要约 20 分钟,但在慢的系统上有可能需要 3 天!所以,这里使用标准构建单元(SBU)来代替具体的编译时间。
SBU 衡量方式如下。我们编译的第一个软件包是 第 5 章 的 Binutils。将这个软件包在编译时所需要的时间,作为标准构建单元(SBU)。其它软件的构建时间,都以其为标准进行比较。
举个例子,假如编译某个软件耗时 4.5 SBUs。这意味着如果这个系统,在编译 Binutils 需要 10 分钟,大概需要约 45 分钟来构建此软件包。很幸运,大部分的系统在生成 Binutils 时花的时间都要比这个系统要短。
一般来说,SBU 的结果并不完全准确,因为影响编译的因素太多,包括宿主机系统中 GCC 的版本也会影响到。所以更多的时候,这仅仅是提供一个编译和安装时间的预估。然而,在某些情况下,这个数字可能偏差约十几分钟。
对于大多数带有多个处理器(或内核)的现代操作系统而言,可以通过设置环境变量或者是告知 make 程序具体可用的处理器数目,通过「并行 make」来减少编译的时间。例如,对于 Core2Duo 可以通过以下参数实现两个处理器同时编译:
export MAKEFLAGS='-j 2'
或者直接这样来构建:
make -j2
以该方式使用多处理器时,SBU 值可能比书中的正常值还要大。某些情况下,make 过程仅仅是简单的就失败了。分析错误日志也十分困难:因为不同处理器之间的执行路线是交错的。如果你在构建过程中遇到了问题,为了正确分析错误信息,最好返回单处理器。
很多软件包都提供相应的测试套件。为新构建的软件包运行测试套件是非常好的习惯,因为这样做可以「保证」所有功能都已编译正确。经由一系列的测试,套件往往能够检查出软件包的功能是否都如开发人员预想的那样。然而,这并不能保证所测试的软件包万无一失。
有一些测试套件要相较而言更为重要。例如,核心工具链软件包——GCC, Binutils 和 Glibc——对于对于一个系统的正常运转起到至关重要的作用。要完成 GCC 和 Glibc 的测试套件可能要花费很长的时间,特别是对于硬件比较慢的设备来说,但还是强烈推荐完成它们!
经验表明,在 第 5 章 中运行测试套件并不是什么好主意。在该章节运行测试将不可避免地面临一个问题,宿主机或多或少会对测试产生影响,这经常导致一些令人摸不着头脑的错误信息。因为在 第 5 章 中构建的这些工具只是临时的,最终我们并不需要它们,所以我们并不推荐普通读者在 第 5 章 中运行测试套件。虽然为测试者和开发者提供了测试套件的说明,但是这依旧是可选项。
对 Binutils 和 GCC
执行测试套件时可能会使伪终端(PTYs)耗尽。造成大量的测试失败。造成问题的原因有很多,但最有可能的原因是宿主系统没能正确设置
devpts
文件系统。针对这个问题在 http://www.linuxfromscratch.org/lfs/faq.html#no-ptys
中有更详尽的讨论。
还有一些测试套件运行错误,是开发人员已知且视为不重要的。查看 http://www.linuxfromscratch.org/lfs/build-logs/9.0/ 中的日志,确认这些失败信息是否都是意料之中的。该网址中涉及的内容会贯穿全书所有的测试。
本章将向您展示如何构造一个最小的 Linux 系统。该系统将包含刚好足够构建 第 6 章 中最终 LFS 系统所需的工具,以及一个比最小环境具有更好用户便利性的工作环境。
构建这个最小系统有两个步骤。第一步,是构建一个与宿主系统无关的新工具链(编译器、汇编器、链接器、库和一些有用的工具)。第二步则是使用该工具链,去构建其它的基础工具。
本章中编译得到的文件将被安装在目录 $LFS/tools
中,以确保在下一章中安装的文件和宿主系统生成的目录相互分离。由于此处编译的软件包都是临时性的,因此,我们不愿意它们污染后面即将构成的
LFS 系统。
这一节解释总体构建方法之中的某些基本原理和技术细节。本节中的所有问题并无需马上消化。在进行实际构建的过程中,绝大部分的信息会变得愈加清晰。过程中随时都可以查阅本小节的内容。
纵览 第 5 章 的目标是生成一个临时的系统,这个系统中包含一个已知的较好工具集,并且工具集可以独立于宿主系统。通过使用 chroot,其余各章中的命令将被包含在此环境中,以保证目标 LFS 系统能够洁净且无故障地生成。该构建过程的设计就是为了使得新读者承担最少的风险,同时还能有最好的指导价值。
在继续前,请留心工作平台的名称,它时常被称为目标三元组。要确定目标三元组的名称有一个简单的法子,那就是运行许多软件包的源码附带的脚本
config.guess。解压
Binutils 的源码并运行脚本:./config.guess
并注意它的输出。举一个例子,Intel 的 32 位处理器输出会是 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 <name of binary> | grep interpreter
并注意它的输出。覆盖全平台的权威参考在 Glibc 源码树根目录的 shlib-versions
文件中。
下面是 第 5 章 构建方法的几个关键技术点:
通过改变 LFS_TGT
变量的目标系统三段式中的「vendor(供应商)」字段,稍微调整一下工作平台的名称,以保证第一遍构建
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
参数,可显示
ld
都链接了哪些文件。例如,gcc dummy.c
-Wl,--verbose 2>&1 | grep succeeded
将显示链接过程中成功打开的所有文件。
下一个安装的包是 GCC。下面便是运行 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
-print-prog-name=ld
可获知 gcc 使用是何种标准链接器。
在编译 dummy(假)程序时,向 gcc 传递命令行选项 -v
可获得详细信息。例如,gcc -v dummy.c
将显示预处理器、编译和汇编阶段的详细信息,包括 gcc 的 include 搜索路径及其顺序。
下一个安装的是经过净化的 Linux API 头文件。这些头文件可使得标准 C 库(Glibc)与 Linux 内核提供的特性进行交行交互。
下一个安装的软件包是 Glibc。构建 Glibc
时,最重要的几个注意点是编译器、二进制工具和内核头文件。编译器通常不成问题,因为 Glibc
将一直使用传递给它配置脚本的,有关 --host
参数的编译器。如,在我们的这个场景中,编译器就是
i686-lfs-linux-gnu-gcc。而二进制工具和内核头文件可能就要复杂一些了。因此,请不要冒险,并利用可用的配置开关来强制正确的选择。configure
运行完毕,目录 glibc-build
下的文件
config.make
包含有所有的重要细节。需要注意的是,CC="i686-lfs-gnu-gcc"
用来控制使用哪个二进制工具,-nostdinc
和 -isystem
标志用来控制编译器的
include 搜索路径。这些条目强调了 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
。
最后,有两个重要事项必须强调:
编译指南假定你已经正确地设置了 宿主系统要求 和符号链接:
shell 使用的是 bash
sh 是到 bash 的符号链接。
/usr/bin/awk 是到 gawk 的符号链接。
/usr/bin/yacc 是到 bison 的符号链接,或者是一个执行 bison 的小脚本。
Binutils 软件包包含一个链接器、一个汇编器、以及其它处理目标文件的工具。
返回前面章节重新阅读注意事项。了解标记为重要的注意事项能在后面帮你省去很多麻烦。
第一个编译的软件包是 Binutils 软件包,这点很重要,因为 Glibc 和 GCC 都会对可用的链接器和汇编器执行各种测试,以决定启用它们自身的哪些功能。
Binutils 手册建议,在源码目录之外一个专门的编译目录里面编译 Binutils:
mkdir -v build cd build
为了衡量在本书中其余部分所使用 SBU
值,我们要测量一下这个软件包从配置到包括第一次安装在内的编译时间。为了轻松的做到这点,会用类似
time { ./configure ...
&& ... && make install; }
的方式将命令包裹在 time 命令中。
第五章中,大致的构建 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
这会禁止国际化(i18n),因为国际化对临时工具来说没有必要。
--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
目录中的。并将 /usr/include
从 GCC 的 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/i368/linux64.h
,给复制的文件加上「.orig」后缀。然后第一个
sed
表达式在每个「/lib/ld」,「/lib64/ld」或者「/lib32/ld」实例前面增加「/tools」,第二个
sed 表达式替换「/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 支持的代码。
--without-headers
在创建完整的交叉编译器时,GCC 要求标准头文件和目标系统兼容。对于我们的目的来说,不需要这些头文件。这个选项可以防止 GCC 查找它们。
--with-local-prefix=/tools
GCC 会查找本地已安装的 include 文件的系统位置。默认是 /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、libcilkrts 和 C++ 标准库的支持。这些功能在编译交叉编译器的时候会导致编译失败,对于交叉编译临时 libc 来说也没有必要。
--disable-multilib
在 x86_64 机器上,LFS 还不支持 multilib 配置。这个选项对 x86 来说无害。
--enable-languages=c,c++
这个选项确保只编译 C 和 C++ 编译器。这些是现在唯一需要的语言。
运行命令编译 GCC:
make
现在编译完成了。在这里,通常会运行测试套件,但正如前面提到的,测试套件框架还没有准备好。在此进行测试的并没有太多好处,因为第一遍编译的程序很快会被取代。
安装软件包:
make install
该软件包的详细信息请参见: 第 6.21.2 节 「GCC 软件包内容」。
Linux API 头文件(在 linux-5.2.8.tar.xz 里)会将内核 API 导出给 Glibc 使用。
Linux 内核需要展示供系统 C 库(在 LFS 中是 Glibc)使用的应用程序编程接口(API)。这通过在 Linux 内核源代码 tar 包中包括一些 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 以及之后的内核库。更早的内核版本不受支持。
--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 软件包的一部分,主机发行版应该提供了。
有报告说用「parallel make」编译这个软件包的时候会失败。如果出现这种情况,用「-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
在后面的编译 Binutils 章节时会再一次检查工具链是否正确编译。如果 Binutils 编译失败,说明之前安装 Binutils、GCC、或者 Glibc 时某些地方出现了错误。
该软件包的详细信息请参见: 第 6.9.3 节 「Glibc 软件包内容」。
Libstdc++ 是标准的 C++ 库。需要用它来编译 C++ 代码(GCC 的一部分是用 C++ 写的),但是在构建 gcc-第 1 遍 时,我们需要推迟它的安装进程,因为依赖的 glibc,还未部署在 /tools 目录中。
Libstdc++ 是标准的 C++ 库。g++ 编译器正确运行需要它。
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++ 编译器搜索标准 include 文件的位置。在一般的编译中,这个信息自动从顶层文件夹中传入 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 功能使链接器可以找到包括在其命令行中的其它共享对象明确需要的共享对象。否则的话,在某些主机上一些软件包可能会编译不成功。
编译软件包:
make
安装软件包:
make install
现在,为下一章的「Re-adjusting」阶段准备链接器:
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
子目录中的所有文件。在命令行中指定 Makefile 的 LIB_PATH
变量可以使我们能够重写临时工具的默认值并指向正确的最终路径。该变量的值指定链接器的默认库搜索路径。下一章中会用到这个准备。
该软件包的详细信息请参见: 第 6.16.2 节 「Binutils 内容」。
GCC 软件包包括 GNU 编译器集,其中有 C 和 C++ 的编译器。
我们第一次编译 GCC 的时候安装了一些内部系统头文件。其中的一个 limits.h
会反过来包括对应的系统头文件 limits.h
,在我们的例子中,是 /tools/include/limits.h
。但是,第一次编译 gcc 的时候
/tools/include/limits.h
并不存在,因此
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 软件包。解压 tar 包并把它们重名为到所需的文件夹名称:
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++
编译预编译的头文件(PCH)。这会花费很多时间,却对我们没有用处。
--disable-bootstrap
对于原生编译的 GCC,默认是做一个「引导」构建。这不仅会编译 GCC 一次,而是会编译很多次。使用初次编译的程序去编译其自身第二次,然后同样地进行第三次。比较第二次和第三次迭代确保其能完美地复制自身。这也能预示编译是正确地。但是,LFS 的构建方法能够提供一个稳定的编译器,避免每次都需要重新引导。
编译软件包:
make
安装软件包:
make install
作为画龙点睛,这里创建一个符号链接。很多程序和脚本执行 cc 而不是 gcc 来保证程序的通用性,并且在所有的 Unix 类型的系统上都能用,而非仅局限于安装了 GCC 的 Unix 类型的系统。运行 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
再次进行全面的检查。如果能运行,就符号链接 /tools/bin/cc
就不见了。像上面介绍的那样新建符号链接。下一步,确认
PATH
是正确的。这能通过运行 echo $PATH 检验,验证
/tools/bin
在列表的前面。如果
PATH
是错误的,这意味着你可能不是以
lfs
用户的身份登录或者在前面 第 4.4 节 「设置环境」 中某些地方出现了错误。
一旦一切都顺利,清理测试文件:
rm -v dummy.c a.out
该软件包的详细信息请参见: 第 6.21.2 节 「GCC 软件包内容」。
Tcl 软件包包含工具命令语言(Tool Command Language)相关程序。
此软件包和后面两个包(Expect 和 DejaGNU)用来为 GCC 和 Binutils 还有其他的一些软件包的测试套件提供运行支持。仅仅为了测试目的而安装三个软件包,看上去有点奢侈,虽然因为大部分重要的工具都能正常工作而并不需要去做测试。尽管在本章中并没有执行测试套件(并不做要求),但是在 第 6 章 中都要求执行这些软件包自带的测试套件。
注意,这里的 Tcl 软件包用的是最小化安装的版本,仅仅是为了运行 LFS 测试。需要完整版的软件包,可参考 BLFS 的 Tcl 流程。
配置 Tcl 准备编译:
cd unix ./configure --prefix=/tools
编译软件包:
make
现在编译已经完成。之前说过,不强求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Tcl 的话可以用下面的命令:
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 的 configure 配置脚本使用 /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
这个选项可以保证 configure 配置脚本会从临时工具目录里找 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
这个选项会保证 Ncurse 不会编译对宿主机系统里可能存在的 Ada 编译器的支持,而这在我们 chroot 切换环境后就不再可用。
--enable-overwrite
这个选项会告诉 Ncurses 安装它的头文件到 /tools/include
目录,而不是 /tools/include/ncurses
目录,保证其他软件包可以正常找到 Ncurses 的头文件。
--enable-widec
这个选项会控制编译宽字符库(比如,libncursesw.so.6.1
)而不是默认的普通库(比如,libncurses.so.6.1
)。这些宽字符库在多字节和传统的 8 位环境下使用,而普通库只能用于 8
位环境。宽字符库和普通库的源代码是兼容的,但并不是二进制兼容。
编译软件包:
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 终端的程序创建一个符号链接:
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 软件包包含查找文件的程序。这些程序提供递归搜索目录树、创建、管理以及搜索数据库(通常比递归式的 find 要快,但如果数据库最近没有更新的话结果不可靠)。
首先,对应 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(Native Language Support,本地语言支持) 编译程序,使得能以用户的本地语言输出信息。
对于我们这次用到的临时工具集,我们只需要安装 Gettext 软件包里的 3 个程序。
配置 Gettext 准备编译:
./configure --disable-shared
配置脚本参数的含义:
--disable-shared
这次我们不需要安装任何的 Gettext 动态库,所以不需要编译。
编译软件包:
make
由于环境受限,建议不要在此阶段运行测试套件。
安装 msgfmt、msgmerge 和 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 库,而在下一章里通过 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 amd
-Ulocincpth
这些条目中未定义的变量,会促使配置过程去寻找宿主系统中已经存在的本地安装的组件。
编译软件包:
make
虽然 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
译注:PDF 文档在复制上述命令时,需注意「\」到「return」之间要保留 2 个 Tab、或 8 个空格的缩进。
配置 Python 准备编译:
./configure --prefix=/tools --without-ensurepip
配置选项的含义:
--without-ensurepip
该选项用于禁用现阶段好不需要的 Python 软件包安装程序。
编译软件包:
make
编译完成。测试套件需求 TK 和 X Windows,此时无法运行。
安装软件包:
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 软件包包含用于读、写以及转换信息页的程序。
配置 Texinfo 准备编译:
./configure --prefix=/tools
作为配制过程的一部分,有一个测试会指出 TestXS_la-TestXS.lo 有一处错误。这与 LFS 没有关系,可以忽略。
编译软件包:
make
现在编译已经完成。之前说过,不要求为本章中所构建的临时工具运行测试套件。不过你仍然要测试 Texinfo 的话可以用下面的命令:
make check
安装软件包:
make install
关于这个软件包的详细资料请参见: 第 6.70.2 节 「Texinfo 软件包内容」。
Xz 软件包包含用于压缩和解压文件的程序。它提供 lzma 和更新的 xz 压缩格式功能。和传统的 gzip 或 bzip2 命令相比,用 xz 压缩文本文件能获得更好的压缩率。
配置 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
这个时候,你应该在 $LFS
分区中为下个阶段编译安装 Glibc 和
GCC 预留至少 3GB 剩余空间。如果你可以编译安装 Glibc,那其他的就不会有问题了。
本书余下部分的命令都必须以 root
用户身份执行而不再是
lfs
用户。另外,再次确认下 $LFS
变量在 root
用户环境下也有定义。
当前,$LFS/tools
目录属于 lfs
用户,这是一个只存在于宿主系统上的帐号。如果继续保持 $LFS/tools
目录的现状,其中的文件将属于一个没有相关联帐号的用户
ID。这很危险,因为随后创建的用户有可能会分配到相同的用户 ID,从而变成 $LFS/tools
目录及其中所有文件的属主,以致留下恶意操作这些文件的可能。
为了解决这个问题,你可以在随后新的 lfs
系统里创建
/etc/passwd
文件时增加一个 lfs
用户,并注意给它分配和宿主系统里相同的用户和组 ID。不过更好的方式是,通过下面的命令将 $LFS/tools
目录的属主改为 root
用户:
chown -R root:root $LFS/tools
尽管 $LFS/tools
目录可以在 LFS
系统构建完成后删除,但仍然可以保留下来用于 构建额外的相同版本 LFS 系统。备份 $LFS/tools
目录到底有多少好处取决于你个人。
如果你想保留临时工具用来构建新的 LFS 系统,现在就要备份好。本书随后第六章中的指令将对当前的工具做些调整,导致在构建新系统时会失效。
在本章中,我们会进入构建环境开始认真地构建 LFS 系统了。我们将 chroot 到之前准备好的临时迷你 Linux 系统,做一些最后的准备工作,然后就开始安装软件包。
安装软件很简单。尽管很多时候安装指令能更短而且更具通用性,但我们还是选择为每个软件包都提供完整的指令,以减小引起错误的可能性。了解 Linux 系统如何工作的关键就是知道每个软件包的作用以及为什么你(或系统)需要它。
我们不建议在编译时使用优化。这虽然可以让程序运行得快那么一点点,但是却也有可能增加编译难度,以及在运行时出问题。如果在打开优化后编译失败,请试一下关闭优化编译看看。就算打开优化通过了编译,考虑到源代码和编译工具之间的复杂交互,仍然存在编译不正确的风险。还有需要注意
-march
和 -mtune
选项除了本书指定的值都未经测试。这有可能导致工具链软件包(Binutils、GCC 和
Glibc)发生问题。对比使用编译优化带来的好处与风险,这样做经常是得不偿失。第一次构建 LFS
系统还是推荐不要使用自定义优化。这样构建出来的系统一样会运行得很快,于此同时还很稳定。
本章里安装软件包的顺序需要严格遵守,这是为了保证不会有程序意外地依赖与 /tools
路径的硬链相关的目录。同样的理由,不要同时编译不同的软件包。并行地编译也许能节省一点时间(特别是在双 CPU
电脑上),但是它可能会导致程序里存在包含到 /tools
目录的硬链接,这样的话在这个目录移除后程序就不能正常工作了。
在安装指令之前,每个页面都提供了关于软件包的信息,包括其中所包含内容的精确描述,构建需求的大致时间,以及在过程中需求磁盘空间的大小。在安装指令之后,是一个该软件包即将安装的程序和库(及概要说明)的列表。
第六章里软件包的 SBU 数值和所需磁盘空间包含了可能存在的测试套件数据。
总的来说,LFS 的编辑们并不推荐构建和安装静态库。许多静态库的初衷已经赶不上现在的 Linux 系统了。而且将静态库链接到程序还有不好之处。假设库更新需要移除一个安全问题,所有使用该静态库的程序都需要重新链接到新的库。由于静态库并不会总那么明显,有哪些相关的程序(以及需要链接的程序)很可能都不知道。
第六章的程序,我们移除或禁止了大部分静态库的安装。通常通过在 configure 命令中使用
--disable-static
项,便可以做到。有些情况下,可能用到其他代替的办法。当然也有少数情况,特别是 glibc 和 gcc
使用的静态库在软件包的构建过程中是必不可少的。
更多关于库的讨论,请参考 BLFS 中 库:静态还是共享? 章节。
内核会挂载几个文件系统用于自己和用户空间程序交换信息。这些文件系统是虚拟的,并不占用实际磁盘空间,它们的内容会放在内存里。
开始先创建将用来挂载文件系统的目录:
mkdir -pv $LFS/{dev,proc,sys,run}
在内核引导系统的时候,它依赖于几个设备节点,特别是 console
和 null
两个设备。设备点必须创建在硬盘上以保证在 udevd 启动前是可用的,特别是在使用
init=/bin/bash
启动
Linux 时。运行以下命令创建设备节点:
mknod -m 600 $LFS/dev/console c 5 1 mknod -m 666 $LFS/dev/null c 1 3
通常激活 /dev
目录下设备的方式是在
/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 创建的设备节点属于 GID 5 之下。这个 ID 日后将用于
tty
组。我们使用 GID
来代替组名,原因是宿主系统可能为 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 系统是如何构建出来的。
存在很多软件包管理的解决方案,每一个都有自己的长处和缺点。很难选择一种适合所有人的方式。
关于软件包管理有很多资料,可以访问 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
。注意在所有依赖软件包重新编译完成之前,请勿删除之前的库文件。
下面介绍一些常见的软件包管理技巧。在决定用哪种包管理方式之前,先研究一下各种不同的技术,特别是某些方案的不足之处。
是的,这也算一种软件包管理技术。有些人觉得不需要管理软件包,是因为他们非常熟悉软件包,知道每个包都安装了哪些文件。也有些用户不需要管理软件包,是因为他们会在某个软件包有更改后重建整个系统。
这是一种简单的软件包管理方式,不需要其他额外的软件来管理软件的安装。每一个软件包都被装到一个独立的目录里。例如,软件包
foo-1.1 安装到目录 /usr/pkg/foo-1.1
中并创建一个符号链接 /usr/pkg/foo
指向 /usr/pkg/foo-1.1
。在安装新版本 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 标准规范 中指定的包管理器),pkg-utils,Debian 的 apt,和 Gentoo 的 Portage 系统。如何在 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 中能找到。在这种方式里,每个软件包都由一个单独的用户安装到标准的位置。文件属于某个软件包可以通过检查用户 ID 轻松识别出来。关于这种方式的利弊比较复杂,就不再本节中赘述了。详细的信息请参看 http://www.linuxfromscratch.org/hints/downloads/files/more_control_and_pkg_man.txt。
LFS 系统的一个优点是没有那种需要依赖其在磁盘系统中的位置的文件。克隆一份 LFS
到和宿主机器相似配置的机器上,简单到只要对包含根目录的 LFS 分区(对于一个基本的 LFS 构建不压缩的话大概有
250MB)使用 tar
命令打包,然后通过网络传输或光盘拷贝到新机器上展开即可。在这之后,需要调整一些配置文件。需要更新的配置文件包括:
/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
。
根据系统硬件和原始内核配置文件的差异,可能还需要重新编译一下内核。
据报告,当这样的复制发生在两个相近却又不完全相同的架构时会发生问题。例如,Intel 系统的指令集就和 AMD 处理器的不同,还有一些较新版的处理器可能会有一些在较早版本中不能支持的指令。
最后,通过 第 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
将会把 TERM
设定成 chroot 外环境相同的值。许多程序需要这个变量才能正常工作,比如
vim 和
less。如果还需设定其他变量,如
CFLAGS
或 CXXFLAGS
,正好在这一起设置了。
在这之后,LFS
变量就不再需要了,因为后面所有工作都将被限定在 LFS
文件系统中。因为我们已经告诉 Bash 终端 $LFS
就是当前的根目录(/
)目录。
注意要将 /tools/bin
放在 PATH
变量的最后。意思是在每个软件的最后版本编译安装好后就不再使用临时工具了。这还需要让
shell 不要「记住」每个可执行文件的位置——这样的话,还要给 bash 加上 +h
选项来关闭其哈希功能。
注意一下 bash 的提示符是
I have no name!
这是正常的,因为这个时候 /etc/passwd
文件还没有被创建。
非常重要,从本章开始,后续章节中的命令都要在 chroot 环境下运行。如果因为某种原因(比如说重启)离开了这个环境,请保证要按照 第 6.2.2 节 「挂载和激活 /dev」 和 第 6.2.3 节 「挂载虚拟文件系统」 中所说的那样挂载虚拟内核文件系统,并在继续构建之前重新运行 chroot 进入环境。
现在准备创建 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} 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
的 home 目录,另一个是存放临时文件的目录。
第一个模式改动能保证不是所有人都能进入 /root
目录——同样一般用户也需要为他/她的 home 目录设置这样的模式。第二个模式改动能保证所有用户对目录
/tmp
和 /var/tmp
都是可写的,但又不能移除其他用户的文件。后面的这个限制是由所谓的「粘滞位」实现的,即位掩码 1777 中的最高位(1)。
这个目录树是基于文件系统目录结构标准(FHS)(参看 https://refspecs.linuxfoundation.org/fhs.shtml)。FHS
标准还规定了要有 /usr/local/games
和
/usr/share/games
目录。我们只创建了我们需要的目录。然而,如果你更喜欢严格遵守 FHS 标准,创建这些目录也无妨。
有些程序里会使用写死的路径调用其它暂时还未安装的程序。为了满足这种类型程序的需要,我们将创建一些符号链接,在完成本章内容后这些软件会安装好,并替代之前的符号链接:
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 需要这个让 pthreads 库正常工作。
/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
用户能正常登录,而且「root」的名字能被正常识别,必须在文件 /etc/passwd
和 /etc/group
中写入相应的内容。
运行下面的命令创建 /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 标准规范 (LSB,参见 http://www.linuxbase.org)只要求以组
ID(GID)为 0 创建用户组 root
以及以 GID 为
1 创建用户组 bin
。系统管理员可以自由分配其它所有用户组名字和 GID,因为优秀的程序不会依赖
GID 数字,而是使用组名。
为了移除「I have no name!」的提示符,可以打开一个新 shell。由于完整的 Glibc 已经在
第 5 章 里装好了,而且已经创建好了
/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)调用。这通过整理 Linux 内核源码包中的多个 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 构建系统是独立的,并且会完美地安装,即使编译器指定的文件和链接器仍然指向 /tools
。在 Glibc 安装之前,不能调整指定的文件和链接器,因为
Glibc 的 autoconf 测试会给出错误结果并阻碍目标实现干净的构建。
有些 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 文档里建议在 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
接着,安装可以使系统响应不同语言的地域设置。没有一个地域是必需的,但是如果缺少了其中的某些,可能会导致在未来的软件包测试套件中,跳过了重要的测试项目。
单独的语言环境可以用 localedef 程序安装。例如,下面第一个
localedef 命令将
/usr/share/i18n/locales/cs_CZ
字符表定义组合在一起,并将结果附加到 /usr/share/i18n/charmaps/UTF-8.gz
文件末尾。下面的命令将安装能完美覆盖测试所需语言环境的最小集合:
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
另外,安装适合你自己国家/地区、语言和字符集的语言环境。
或者,也可以一次性安装在 glibc-2.30/localedata/SUPPORTED
文件里列出的所有语言环境(包括以上列出的所有语言环境以及其它更多),执行下面这个非常耗时的命令:
make localedata/install-locales
你需要的语言环境几乎不太可能没有列在 glibc-2.30/localedata/SUPPORTED
文件中,但如果真的没有可以使用 localedef 命令创建和安装。
当前,Glibc 在解决国际化域名时使用 libidn2。此为运行时依赖。如果需要此功能,可以参考 BLFS 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 时区数据放到
zoneinfo
目录下,否则很多测试套件会报错。在嵌入式平台,如果存储空间紧张而且你也不准备更新时区,也可以不用
posix
目录从而节省
1.9MB,但是一些应用程序或测试套件也许会出错。
zic -L
leapseconds ...
这会创建包含时间补偿的 right
时区数据。在嵌入式平台,空间比较紧张而且你也不打算更新时区或者不需要准确时间,你可以忽略
right
目录从而节省 1.9MB。
zic ...
-p ...
这会创建 posixrules
文件。我们使用纽约是因为 POSIX 要求夏令时规则与 US 标准一致。
一种确定本地时区的方式是运行下面的脚本:
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
查找程序运行时所需的动态库文件。如果库文件不在
/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
如果需要的话,动态库加载器也可以查找目录并包含里面配置文件的内容。通常在这个包含目录下的文件只有一行字指向库目录。运行下面的命令增加这个功能:
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 处理某些目标文件 |
|
输出当前语言环境的大量信息 |
|
编译语言环境规格 |
|
根据输入的文本创建简单数据库 |
|
读取并解析内存跟踪文件,然后用方便人阅读的格式显示一个摘要 |
|
一个后台服务程序,提供最常用名字服务请求的缓存 |
|
通过 PC 分析生成的 dump 信息 |
|
列出运行中进程正在使用的动态共享目标 |
|
一个静态链接的 ln 程序 |
|
跟踪指定命令里的动态库函数调用 |
|
读取并显示共享目标分析数据 |
|
询问用户该系统的地理位置并给出相应的时区描述 |
|
跟踪程序执行过程并打印当前执行的函数 |
|
时区数据输出工具 |
|
时区数据编译工具 |
|
用于动态库执行的辅助程序 |
|
Glibc 内部的一个粗暴破解用来修复损坏程序(比如,一些 Motif 应用)。查看文件
|
|
段错误信号处理函数,catchsegv 会用到 |
|
一个异步名字查找库 |
|
主要的 C 库 |
|
密码学函数库 |
|
动态链接接口函数库 |
|
不包含函数的一个空库。以前是 g++ 的运行时库 |
|
数学运算函数库 |
|
链接这个库后会打开内存分配检查 |
|
Used by memusage 命令用它来协助收集应用程序里内存使用信息 |
|
网络服务函数库 |
|
名称服务切换函数库,包含了解析主机名、用户名、组名、别称、服务、协议等等的函数 |
|
可以预加载到 PC 配置文件中的可执行文件 |
|
POSIX 线程函数库 |
|
包含了创建、发送和解析互联网域名服务器封包的函数 |
|
包含了实现 POSIX.1b 实时扩展里规定的大部分接口的函数 |
|
包含了方便构建多线程程序调试工具的函数 |
|
包含各种 Unix 应用程序中用到的「标准」函数的代码 |
现在最后的 C 语言库已经装好了,是时候调整工具链,让新编译的程序链接到这些新的库上。
首先,备份 /tools
链接器,然后用我们在第五章调整过的链接器代替它。我们还会创建一个链接,链接到 /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 参数文件,让它指向新的动态连接器。只需删除所有「/tools」的实例,这样应该可以留下到达动态链接器的正确路径。还要调整参数文件,这样 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
直观地检查参数文件来确认预期的变化已确实完成是个好办法。
确保已调整的工具链的基本功能(编译和链接)都能如期进行是非常必要的。怎样做呢?执行下面这条命令:
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 中的符号链接访问。
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
如果显示的结果不一样或者根本没有显示,那就出了大问题。检查并回溯之前的步骤,找到出错的地方并改正。最有可能的原因是参数文件的调整出了问题。在进行下一步之前所有的问题都要解决。
确保一切正常之后,清除测试文件:
rm -v dummy.c a.out dummy.log
Zlib 软件包包括一些程序所使用的压缩和解压缩例程。
准备编译 Zlib:
./configure --prefix=/usr
编译软件包:
make
输入命令查看结果:
make check
安装软件包:
make install
共享库需要移动到 /lib
,因此需要重建
.so
里面的 /usr/lib
文件:
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 会使旧的库移动到 <libraryname>.old。通常来说这并不是什么问题,但一些情况下可能引起 ldconfig 链接错误。可以通过下面的两个 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 软件包包含一个链接器、一个汇编器、以及其它处理目标文件的工具。
通过一个简单测试验证在 chroot 环境下 PTY 工作正常:
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
构建原来的 bdf 链接器并将其安装为 ld(默认链接器)和 ld.bfd。
--enable-plugins
为链接器启用插件支持。
--enable-64-bit-bfd
启用 64 位支持(针对字宽较窄的主机)。64 位系统可能没什么必要,但也不会有什么坏处。
--with-system-zlib
使用安装的 zlib 库替代自带的版本构建。
编译软件包:
make tooldir=/usr
make 参数的含义:
tooldir=/usr
一般来说,tooldir (最终存放可执行文件的目录) 设置为 $(exec_prefix)/$(target_alias)
。例如,x86_64
机器会把它扩展为 /usr/x86_64-unknown-linux-gnu
。因为这是个自定制的系统,并不需要
/usr
中的特定目标目录。如果系统用于交叉编译(例如,在 Intel
机器上编译能生成在 PowerPC 机器上运行的代码的软件包)会使用 $(exec_prefix)/$(target_alias)
。
本章节中的 Binutils 测试套件至关重要,任何情况下都不能跳过。
查看结果:
make -k check
PC 相关的 offset 测试和 debug_msg.sh 中的测试已知会在 LFS 环境中测试失败。
安装软件包:
make tooldir=/usr install
转换程序地址为文件名称和行号;给定一个地址和可执行文件的名称,它使用可执行文件中的调试信息来判断与该地址关联的源文件以及行号。 |
|
创建、更改以及抽取归档文件。 |
|
一个将 gcc 的输出汇编到目标文件的汇编器。 |
|
链接器用来过滤 C++ 和 Java 符号以及防止重载函数冲突。 |
|
DWARF 打包实用程序 |
|
更新 ELF 文件的 ELF 文件头。 |
|
显示调用关系图配置数据。 |
|
一个将多个目标文件和归档文件合并为单一文件,重定位数据及绑定符号引用的链接器。 |
|
一个阉割版的 ld,仅支持 elf 对象文件格式 |
|
到 ld 的硬链接。 |
|
列出指定目标文件中出现的符号。 |
|
转换某种类型的目标文件到另一种类型。 |
|
显示给定目标文件的信息,用选项可以控制显示特定信息;显示的信息对于使用编译工具的程序员非常有用。 |
|
生成归档文件内容的索引并保存到归档文件;索引列出了所有归档文件成员——可重定位的目标文件定义的符号。 |
|
显示 ELF 类型的二进制文件的信息。 |
|
列出所给目标文件各部分大小和总的大小。 |
|
对每个给定文件,输出不低于指定长度(默认是4)的可打印字符序列;对于目标文件,它默认只打印初始化和引导部分的字符串,而对于其它类型的文件扫描整个文件。 |
|
从目标文件中去除符号。 |
|
二进制文件描述库。 |
|
一个库用于处理操作码——「可读文本」版的处理器指令;用于构建类似 objdump 的工具。 |
GMP 软件包包含一些数学库。这里有对任意精度数值计算很有用的函数。
如果你是为 32 位的 x86 系统编译,但是你的 CPU 可以运行 64 位代码并且环境中你有指定的 CFLAGS
,那么配置脚本会尝试配置为 64 位并导致失败。用下面的
方式执行配置命令来避免这个问题:
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 中的代码对于其构建的处理器进行了高度优化。有时,检测处理器的代码会误认系统的功能,并在测试中报错,或是在其他应用使用 GMP 库的时候显示消息「Illegal instruction(非法指令)」。在这种情况下,GMP 需要重新配置选项 --build=x86_64-unknown-linux-gnu 并重新构建。
确认测试套件中所有的 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 软件包包含以安全方式处理密码的程序。
如果你喜欢强制使用更强的密码,在编译 Shadow 之前可以根据
http://www.linuxfromscratch.org/blfs/view/9.0/postlfs/cracklib.html
安装 CrackLib。然后在下面的 configure 命令中增加
--with-libcrack
。
禁用对 groups 程序以及相应 man 手册的安装,Coreutils 已经提供了更棒的版本。同时也避免了安装已由 第 6.8 节 「Man-pages-5.02」 安装过的手册页:
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
该软件包包含增加、更改、以及删除用户和组的工具;设置和修改密码;执行其它特权级任务。软件包解压后的
doc/HOWTO
文件有关于 password shadowing 的完整解释。如果使用
Shadow 支持,记住需要验证密码(显示管理器、FTP 程序、pop3 守护进程等)的程序必须和 Shadow
兼容。也就是说,它们要能使用 Shadow 加密的密码。
运行下面的命令启用 shadow 密码:
pwconv
运行下面的命令启用 shadow 组密码:
grpconv
用于 useradd 工具的
Shadow 配置有一些需要解释的注意事项。首先,useradd
工具的默认操作是创建用户以及和用户名相同的组。默认情况下,用户 ID(UID) 和组 ID(GID) 的数字从 1000
开始。这意味着如果你不传递参数给 useradd,系统中的每个用户都会属于一个不同的组。如果不需要这样的结果,你需要传递参数
-g
到 useradd。默认参数保存在
/etc/default/useradd
文件中。你需要修改该文件中的两个参数来实现你的特定需求。
/etc/default/useradd
参数解释
GROUP=1000
该参数设定 /etc/group 文件中使用的起始组序号。你可以把它更改为任何你需要的数字。注意
useradd
永远不会重用 UID 或
GID。如果该参数指定的数字已经被使用了,将会使用它之后的下一个可用数字。另外注意如果你系统中没有序号为
1000 的组,第一次使用 useradd 而没有参数
-g
的话,你会在终端中看到一个提示信息: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 |
|
验证组文件 |
|
从普通组文件创建或升级为 shadow 组文件 |
|
从 |
|
报告所有用户或指定用户的最近一次登录 |
|
用于系统让用户登录进来 |
|
用于强制限制登录时间和端口的守护进程 |
|
用于设置用户命名空间的 gid 映射 |
|
用于在一次登录会话中更改当前 GID |
|
用于设置用户命名空间的 uid 映射 |
|
用于批量创建或更新用户账户 |
|
显示一个账户不可用的信息;它用于来作为不可登录的账户的默认 shell |
|
用来更改用户或组账户的密码 |
|
验证密码文件 |
|
从普通密码文件创建或升级 shadow 密码文件 |
|
从 |
|
当用户的 GID 被设置为指定组的 GID 时执行一个特定命令 |
|
用替换的用户和组 ID 运行 Shell |
|
用指定的名称新建用户或更新新用户的默认信息 |
|
删除指定的用户账户 |
|
用于更改指定用户的登录名称、UID、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 Book 了解如何编译 GCC 支持的所有语言的指令。
新配置选项的含义:
SED=sed
设置环境变量防止访问到硬编码的 /tools/bin/sed 路径。
--with-system-zlib
这个选项告诉 GCC 链接系统安装的 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/ 上的相比较。
已知与 get_time 相关的六个测试失败。 这些显然与 en_HK 语言环境有关。
两个 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.2.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
增加一个兼容符号链接启用编译程序时进行链接时间优化(Link Time Optimization,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
如果显示的结果不一样或者根本没有显示,那就出了大问题。检查并回溯之前的步骤,找到出错的地方并改正。最有可能的原因是参数文件的调整出了问题。在进行下一步之前所有的问题都要解决。
确保一切正常之后,清除测试文件:
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 配置文件 dump 工具 |
|
离线 gcda 配置文件处理工具 |
|
Address Sanitizer(地址消毒剂)运行时库。 |
|
GCC 原子内置运行时库 |
|
C 预处理库 |
|
包含用于 gcc 的运行时支持 |
|
当指示 GCC 启用分析时该库会被链接到程序中 |
|
用于 C/C++、Fortran 语言的多平台共享内存并行编程的 OpenMP API 的 GNU 实现 |
|
泄漏消毒(Leak Sanitizer)剂运行时库 |
|
GCC 的链接时间优化(LTO)插件,允许 GCC 跨编译单元进行优化 |
|
GCC 四精度数学库 API |
|
包含支持 GCC 堆栈溢出保护功能的例程 |
|
标准 C++ 库 |
|
ISO/IEC TS 18822:2015 文件系统库 |
|
为 C++ 编程语言提供支持例程 |
|
Thread Sanitizer(数据速率检测工具) 运行时库 |
|
未定义行为消毒剂(Undefined Behavior Sanitizer)运行时库 |
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
准备编译 Bzip:
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 块排序文本压缩算法压缩文件;压缩率比传统的用「Lempel-Ziv」算法的压缩器要好,比如 gzip。 |
|
尝试从损坏的 bzip 压缩文件中恢复数据 |
|
对 bzip 压缩的文件运行 less 命令 |
|
对 bzip 压缩的文件运行 more 命令 |
|
用 Burrows-Wheeler 算法实现的无损的块排序数据压缩库 |
pkg-config 软件包包含一个在配置和 make 文件运行时把 include 路径和库路径传递给编译工具的工具。
准备编译 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 中没有可用的外部版本。
--disable-host-tool
此选项取消创建到 pkg-config 程序的不必要的硬链接。
编译软件包:
make
用以下命令检查结果:
make check
安装软件包:
make install
Ncurses 软件包包含用于不依赖于特定终端的字符屏幕处理的库。
不要安装静态库,它不受配置控制:
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
位本地字符,而常规的库只能用于 8 位本地字符。宽字符库和常规的库是源文件兼容的,而不是二进制文件兼容的。
--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 库,因为没有从源文件中编译安装的软件包会在运行时链接它们。然而,已知的仅有二进制应用程序并能链接到非等宽字符的库,需要第 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 描述 |
|
P为 ncurses 提供配置信息 |
|
重新初始化终端为默认设置 |
|
清空终端并设置制表符长度 |
|
将 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。如果同时运行多个测试会导致测试失败(-j 选项大于 1)。输入命令检查结果:
make check
安装软件包:
make install
需要移动共享库到 /lib
,因此需要重建
.so
中的 /usr/lib
文件:
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 库构建 Coreutils 后,Acl 测试才能在支持访问控制的文件系统上运行。如果需要的话,可以在本章后面构建完 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 之后,可以使用 make check 命令重新编译并检查 bison 软件包。
安装软件包:
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
仿真模式运行 flex
的符号链接 lex:
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
该选项禁用编译过时的 Inetutils 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 软件包包含实用信息抽取与报告语言。
首先创建其中一个 Perl 配置文件和可选测试套件引用的基本 /etc/hosts
文件:
echo "127.0.0.1 localhost $(hostname)" > /etc/hosts
该版本的 Perl 会编译 Compress::Raw::Zlib 和 Compress::Raw::BZip2 模块。Perl 默认会使用内部的源码用于构建。用以下的命令使 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
构建某些 perl 模块需要的共享 libperl。
-Dusethreads
构建支持线程的 perl。
编译软件包:
make
用以下命令测试结果(大概 11 SBU):
make -k test
因为使用了最新的 gdbm 从而会导致一项测试失败。
安装软件包并清理:
make install unset BUILD_ZLIB BUILD_BZIP2
Module::CoreList 的命令行前端 |
|
用命令行与综合 Perl 归档网络(Comprehensive Perl Archive Network,CPAN)交互 |
|
从 Unicode 字符映射或 Tcl 编码文件为 Encode 模块编译 Perl 扩展 |
|
猜测一个或多个文件的编码类型 |
|
转换 |
|
转换 |
|
用于检查安装的 Perl 模块的 shell 脚本,并且能从一个安装的模块中创建 tar 包 |
|
在特定输入输出格式之间转换数据 |
|
可用于配置 |
|
将 C,sed,awk 以及 sh 一些最好的特性结合到一个单一的强大语言 |
|
到 perl 的硬链接 |
|
用于生成关于 Perl、或者一起发布的模块的 bug 报告,并用邮件通知 |
|
用嵌入到 Perl 安装目录或 Perl 脚本中的 pod 格式显示文档 |
|
Perl 安装验证程序;能用于验证 Perl 和它的库是否正确安装 |
|
用于生成发送到 Perl 开发者的感谢邮件 |
|
Perl 版本的字符编码转换程序 iconv |
|
用于将 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 解析器的 Perl Expat 接口。
准备编译 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 脚本的程序。
准备编译 Autoconf:
首先,修复一个 Perl 5.28 引入的问题。
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 一同使用的 Makefile 的程序。
准备编译 Automake:
./configure --prefix=/usr --docdir=/usr/share/doc/automake-1.16.1
编译软件包:
make
因为各个测试之间存在内部延时,故建议就算是在单核处理器的设备上,也使用 -j4 编译选项加速测试过程。用以下命令测试结果:
make -j4 check
已知 LFS 环境中 subobj.sh 测试会失败。
安装软件包:
make install
Xz 软件包包含用于压缩和解压文件的程序。它提供 lzma 和更新的 xz 压缩格式功能。和传统的 gzip 或 bzip2 命令相比,用 xz 压缩文本文件能获得更好的压缩率。
准备编译 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-Ziv-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(Native Language Support,本地语言支持) 编译程序,使得能以用户的本地语言输出信息。
准备编译 Gettext:
./configure --prefix=/usr \ --disable-static \ --docdir=/usr/share/doc/gettext-0.20.1
编译软件包:
make
用以下命令测试结果(需要较长一段时间,大概 3 SBUs):
make check
安装软件包:
make install chmod -v 0755 /usr/lib/preloadable_libintl.so
复制标准 Gettext 基础文件到源码包 |
|
shell 格式字符串的替代环境变量 |
|
通过查看信息目录中的转换将原来语言信息转换为用户语言 |
|
主要作为 gettext 的一个 shell 函数库 |
|
复制所有标准 Gettext 文件到指定软件包的顶层目录以开始国际化 |
|
根据属性过滤翻译目录的信息并操作属性 |
|
连接和合并给定 |
|
比较两个 |
|
查找给定 |
|
转换翻译目录到不同的字符编码 |
|
创建一个英语翻译目录 |
|
对翻译目录的所有翻译运行命令 |
|
对翻译目录的所有翻译应用过滤器 |
|
从翻译目录生成一个二进制信息目录 |
|
从翻译目录中抽取所有符合指定模式或属于特定源文件的信息 |
|
创建一个新的 |
|
合并两个原译到一个单独的文件 |
|
反向编译一个二进制信息目录为原译文本 |
|
统一重复翻译为一个翻译目录 |
|
显示语法形式取决于多种母语翻译的文本信息的本地语言翻译 |
|
对 Serbian 文本从 Cyrillic 重新编码为 Latin 脚本 |
|
从指定源文件中抽取可翻译信息行用于生成第一个翻译模板 |
|
定义 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 库为各种调用规范提供了一个可移植的,高级编程接口。允许程序员在运行时,通过调用接口描述调用任意指定函数。
Libffi 与 GMP 相似,构建时会根据使用的处理器优化。如果需要构建的是另一个系统,设定 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
配置选项的含义:
--with-gcc-arch=native
确保 GCC 为当前系统进行优化。如果没有指定则会进行猜测,这样生成的代码在某些系统上是不正确的。如果生成的代码想要从本机系统复制到功能较弱的系统,则应该以功能较弱的系统作为参数。有关替代系统类型的详细信息,请参阅 gcc 手册中的 x86 选项。
编译软件包:
make
用以下命令测试结果:
make check
安装软件包:
make install
OpenSSL 软件包包含管理工具和加密相关的库。其中提供的这些加密功能对于其他软件包,比方说 OpenSSH,email 应用和网页浏览器(访问 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 Windows 会话,直至 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
and
--no-same-permissions
确保安装文件的归属和权限是正确的。没有这个选项的话,运行 tar 时会以上游创建者的身份安装软件包内的文件。
Ninja 是一个专注于速度的小型构件系统。
在运行时,ninja 通常会并行最大数量的进程。默认值是系统的核心数乘以二。有些时候会导致 CPU 过热,或者内存容量不足。如果是命令行运行,通过传递 -jN 参数可以限制并行的进程数,但是有些软件包虽然潜入了 ninja 的执行却不会传递 -j 参数。
通过使用下方 可选 过程,让用户能够通过环境变量 NINJAJOBS 来限制并行进程的数量。例如, 设定:
export NINJAJOBS=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 重新构建自身以适应当前系统。
查看测试结果,输入:
./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 中的程序即使在多字节语言环境也能正确识别字符边界。下面的补丁修复这个不兼容性以及其它一些和国际化相关的错误。
patch -Np1 -i ../coreutils-8.31-i18n-1.patch
之前在这个补丁中发现了很多错误。当向 Coreutils 维护者报告新错误的时候,请先检查没有该补丁是否可以重现该错误。
抑制测试在某些机器上可能出现的无限循环:
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
在启动的早期和晚期阶段可能不可用,这些二进制文件需要被存在放在根分区(/bin 目录)以保证 FHS 合规性:
mv -v /usr/bin/{head,nice,sleep,touch} /bin
根据 base32(RFC 4648)规范编码和解码数据 |
|
根据 base64(RFC 4648)规范编码和解码数据 |
|
打印或查看 BLAKE2 (512-bit) 校验和 |
|
从文件名称中抽取路径和后缀 |
|
使用各种算法对数据进行编码或解码 |
|
将文件连接到标准输出 |
|
改变文件和目录的安全上下文 |
|
更改文件和目录的组所有者 |
|
更改每个文件为指定模式的权限;模式可以是要实现更改的符号表示 或者表示新权限的十进制数字 |
|
更改文件和目录的用户和/或组所有者 |
|
使用指定目录作为 |
|
输出指定文件的循环冗余检验(CRC)校验码和字节数目 |
|
比较两个排序后的文件,以三列输出不用的行和相同的行 |
|
复制文件 |
|
将指定文件分割为几个新的文件,根据指定的模式或者行数分割并 输出每个新文件的字节数 |
|
根据指定的域或位置,选择输出行的组成 |
|
根据指定格式显示当前时间,或设置系统日期 |
|
用给定的块大小和数目复制文件,同时可以进行一些转换 |
|
报告所有挂载的文件系统中可用(和已用)磁盘空间,或只是 含有指定文件的文件系统 |
|
列出指定目的的内容(和 ls 命令相同) |
|
输出设置 |
|
从一个文件名称中抽取非目录后缀 |
|
报告当前目录使用的磁盘空间,根据指定的每个文件夹(包括子文件夹) 或每个指定的文件 |
|
显示给定的字符串 |
|
在更改后的环境中运行命令 |
|
把 tab 键转换为空格 |
|
计算表达式 |
|
输出所有指定整数的质数因子 |
|
什么都不做;总是以指示失败的状态码退出 |
|
重新格式化给定文件中的段落 |
|
折叠指定文件的行 |
|
报告一个用户的组成员 |
|
输出指定文件的前十行(或指定数目行数) |
|
报告主机的数字标识符(以十六进制) |
|
报告当前用户或指定用户的有效 用户 ID、组 ID 以及组成员 |
|
复制文件的同时设置权限模式,如果可以的话包括用户和组 |
|
从两个单独的文件中连接有相同域的行 |
|
用指定的名称创建到一个文件的硬链接 |
|
在文件之间建立硬链接或软(符号)链接 |
|
报告当前用户的登录名 |
|
列出给定目录的内容 |
|
报告或检查消息摘要 5(MD5) 校验码 |
|
用指定的名称新建目录 |
|
用指定的名称在 UNIX 中创建先进先出(FIFO) 的「命名管道」 |
|
用指定的名称创建设备结点;设备结点是一个特殊字符文件、特殊块文件或先进先出 |
|
以安全方式新建临时文件;在脚本中使用 |
|
移动或重命名文件或目录 |
|
以更改后的调度优先级运行程序 |
|
标记指定文件的行号 |
|
以不能被挂起方式运行命令,输出重定向到一个日志文件 |
|
输出进程可用的处理单元数目 |
|
转换数字为人可读字符串或者相反 |
|
以十进制或其他格式转储文件 |
|
合并指定文件,用 tab 字符分隔,以行并列方式连续合并 |
|
检查文件名是否可用 |
|
轻量级的 finger 客户端;报告指定用户的信息 |
|
对文件进行分页分行用于打印 |
|
输出环境 |
|
根据指定格式打印指定参数,类似于 C 语言的 printf 函数 |
|
用文中的每个关键字,根据文件的内容中建立重排索引 |
|
报告当前工作目录的名称 |
|
报告指定符号链接的值 |
|
打印解释后路径 |
|
删除文件或目录 |
|
如果目录为空则删除 |
|
以指定安全上下文运行命令 |
|
用指定的范围和增长步长输出一序列数字 |
|
打印或检查 160-bit 安全哈希算法1(SHA1) 校验码 |
|
打印或检查 224-bit 安全哈希算法校验码 |
|
打印或检查 256-bit 安全哈希算法校验码 |
|
打印或检查 384-bit 安全哈希算法校验码 |
|
打印或检查 512-bit 安全哈希算法校验码 |
|
用复杂形式多次重写指定文件,使得难以恢复其中的数据 |
|
打乱文本行 |
|
暂停指定时间 |
|
排序给定文件的行 |
|
根据大小或行数分割文件为多个块 |
|
显示文件或文件系统状态 |
|
用改变后的缓冲操作在标准流上运行命令 |
|
设置或报告终端行设置 |
|
打印指定文件的校验码和块数目 |
|
清空文件系统缓存;强制更改块到磁盘并更新超级块 |
|
反向输出给定文件 |
|
输出每个给定文件的最后十行(或给定数目的行) |
|
从标准输入读入并写出到标准输出和指定文件 |
|
比较值并检查文件类型 |
|
有限时间内运行命令 |
|
更改文件时间戳,设置指定文件的访问和修改时间为当前时间; 如果文件不存在则创建空文件 |
|
从标准输入转换、压缩并删除指定字符 |
|
不做任何事情,总是成功;总是以表示成功的状态码退出 |
|
压缩或扩展文件到特定大小 |
|
进行拓扑排序;根据指定文件的部分排序写出完全有序列表 |
|
报告链接到标准输入的终端文件名称 |
|
报告系统信息 |
|
转换空格为 tab 键 |
|
忽略所有除非出现连续相同的行 |
|
移除指定文件 |
|
报告当前登录的用户名 |
|
和 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 软件包包含查找文件的程序。这些程序提供递归搜索目录树、创建、管理以及搜索数据库(通常比递归式的 find 要快,但如果数据库最近没有更新的话结果不可靠)。
首先,抑制测试中可能在某些机器中出现的无限循环:
sed -i 's/test-lock..EXEEXT.//' tests/Makefile.in
然后,对应 glibc-2.28 或更高版本 glibc 的需求做一些修复:
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 数据库的位置为 FHS
兼容的 /var/lib/locate
。
编译软件包:
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
更合适。尽管在编译的时候配置了默认页面大小,后面通过 echo「A4」或「letter」到 /etc/papersize
文件仍然可以修改。
准备编译 Groff:
PAGE=<paper_size>
./configure --prefix=/usr
该软件不支持并行构建。编译软件包:
make -j1
该软件包没有测试套具。
安装软件包:
make install
读 troff 字体文件并添加一些额外的 groff 系统使用的字体信息 |
|
创建用于和 groff 以及 grops 一起使用的字体文件 |
|
Gorff 预处理生成化学结构图 |
|
编译嵌入了 troff 输入文件的方程的描述为 troff 能理解的命令 |
|
转换 troff EQN (equation 方程)为裁剪图像 |
|
标记 groff/nroff/troff 文件的差异 |
|
将 lilypond 语言写成的乐谱转换至 groff语言 |
|
Groff 的预处理器,允许在 groff 中添加 perl 代码 |
|
Groff 的预处理器,允许在 groff 文件中添加类似欧洲语言的中文拼音。 |
|
转换 grap 图为裁剪位图图像 |
|
用于 gremlin 文件的 groff 预处理器 |
|
生成 TeX dvi 格式的 groff 驱动 |
|
groff 文档格式化系统前端;一般运行 troff 程序和适合选定文件的后处理器 |
|
在 X 和 tty 终端显示 groff 文件以及 man 页面 |
|
读文件并猜测打印文件需要的 groff 选项
|
|
用于 Canon CAPSL 打印机(LBP-4 和 LBP-8 系列激光打印机) 的 groff 驱动 |
|
生成适合于 HP LaserJet 4 打印机的 PCL5 格式输出的 groff 驱动 |
|
翻译 GNU troff 的输出至 PDF |
|
转换 GNU troff 输出为 PostScript |
|
转换 GNU troff 输出为适合于打字机设备的格式 |
|
从 HP标签字体规格文件创建和 groff -Tlj4 一起使用的字体文件 |
|
用和 refer,lookbib, 以及 lkbib 一起使用的指定文件为文献数据库创建倒排索引 |
|
搜索文献数据库中包含指定键的引用并报告找到的任何引用 |
|
在标准错误中输出提示(除非标准输入不是终端),从标准输入读取包含一系列关键字的行,在指定的文件中搜索文献数据库中包含那些关键字的引用,在标准输出中打印找到的任何引用,循环这些过程直到输入结束 |
|
groff 的简单预处理器 |
|
为美国标准信息交换码(ASCII) 输出格式化方程 |
|
用 nroff 模仿 groff 命令的脚本 |
|
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 文件到 PDFs |
|
转换 roff 文件为 ps 文件 |
|
转换 roff 文件为文本文件 |
|
转换 roff 文件到其它格式 |
|
读文件并用相应的 file 内容替换 .so file 格式的行 |
|
编译嵌入了 troff 输入文件的表的描述为 troff 能理解的命令 |
|
创建和 groff -Tdvi 一起使用的字体文件 |
|
和 Unix troff 高度兼容;通常应该使用 groff 命令调用,它也会以恰当的顺序和选项运行预处理器和后处理器 |
GRUB 软件包包含多重启动管理器(GRand Unified Bootloader)。
准备编译 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
会在 第 8.4 节 「使用 GRUB 设置启动过程」 介绍通过 GRUB 启动你的 LFS 系统。
grub-install 的帮助程序 |
|
编辑环境块的工具 |
|
检查 FILE 是否为指定的类型 |
|
调试文件系统驱动的工具 |
|
处理 ia32 和 amd64 的 EFI 镜像并按照 Apple 的格式将他们粘在一起 |
|
在你的驱动器上安装 GRUB |
|
转换 xkb 布局为 GRUB 可识别样式的脚本 |
|
以 Mac 的风格保护 HFS 或 HFS+ 文件 |
|
为和 GRUB 2 一起使用,转换引导装载程序(GRUB Legacy) |
|
生成 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
格式重新压缩文件format—— |
IPRoute2 软件包包含基于 IPV4 网络的基本和高级程序。
此软件包中包含的 arpd 因为依赖于 Berkeley DB,但是此软件并没有包含于 LFS 中,所以将不会进行编译。但是 arpd 的目录依旧会被安装。运行以下的命令来阻止这一动作。如果需要 arpd 的二进制文件,请查看 BLFS Book 的网页 http://www.linuxfromscratch.org/blfs/view/9.0/server/databases.html#db 以了解编译 Berkeley 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
配置网桥 |
|
连接状态工具 |
|
通用 netlink 多用途前端 |
|
ip 命令的 shell 脚本封装 [注意它需要 iputils 软件包中的 arping 和 rdisk 程序,可以在 http://www.skbuff.net/iputils/ 找到 iputils 软件包。] |
|
显示接口统计信息,包括接口发送和接收的包的数目 |
|
主要的可执行程序。它有多种不同功能:
ip link ip addr 允许用户查看地址和属性、增加新地址、删除旧地址 ip neighbor 允许用户查看邻居和它们的特性、增加新邻居、删除旧邻居 ip rule 允许用户查看路由策略并更改 ip route 允许用户查看路由表并更改路由表规则 ip tunnel 允许用户查看 IP 隧道及其特性、并进行更改 ip maddr 允许用户查看多播地址及其特性、并进行更改 ip mroute 允许用户设置、更改或删除多播路由 ip monitor 允许用户持续监视设置、地址和路由状态 |
|
提供 Linux 网络统计信息;是更通用、功能更完备的替代旧 rtstat 的程序 |
|
显示网络统计信息 |
|
ip route 的组件。用于清空路由表 |
|
ip route 的组件。用于列出路由表 |
|
显示 |
|
路由监视工具 |
|
转换 ip -o 输出为可读形式 |
|
路由状态工具 |
|
类似于 netstat 命令;显示活动连接 |
|
拥塞控制可执行程序;用于实现服务质量(Quality Of Service,QOS)和服务等级(Class Of Service,COS) tc qdisc 允许用户设置排队规则 tc class 允许用户基于排队规则调度策略设置等级 tc estimator 允许用户估计到一个网络的网络流量 tc filter 允许用户设置 QOS/COS 包过滤 tc policy 允许用户设置 QOS/COS 策略 |
Kbd 软件包包含键表文件、控制台字体和键盘工具。
在 Kbd 软件包中退格键(Backspace)和删除键(Delete)的行为和键映射并不一致。下面的补丁修复了 i386 键映射中的这个问题:
patch -Np1 -i ../kbd-2.2.0-backspace-1.patch
打补丁后,退格键生成编码为 127 的字符,删除键会生成一个著名的转义序列。
移除冗余的 resizecons 程序(它要求功能不全的 svglib 提供视频模式文件——用于正常使用 setfont 设置控制台字体大小)以及帮助手册。
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
由于通常使用的 CP1251 键映射假设使用 ISO-8859-5 编码,Kbd 软件包不能为某些语言(例如,白俄罗斯) 提供可用的键映射。使用这样的语言需要单独下载能工作的键映射。
如果需要的话,安装帮助文档:
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 字符表 |
|
更改控制台的增强图形适配器(Enhanced Graphic Adapter,EGA)和视频图形阵列(Video Graphics Array,VGA)的字体 |
|
加载内核扫描码到键码映射表条目;键盘上有异常键时非常有用 |
|
设置键盘标记和 LED 灯 |
|
定义键盘元键处理 |
|
设置所有虚拟终端中的控制台颜色映射 |
|
显示当前 EGA/VGA 控制台屏幕字体 |
|
报告键盘上按键的扫描码、键码以及 ASCII 码 |
|
设置键盘和控制台为 UNICODE 模式[别用该程序,除非你的键映射文件是 ISO-8859-1 编码。对于其它编码,该工具会输出错误结果。] |
|
从 UNICODE 模式恢复键盘和控制台为原来模式 |
Libpipeline 软件包包含以灵活方便方式管理管道和子进程的库。
准备编译 Libpipeline:
./configure --prefix=/usr
编译软件包:
make
用以下命令测试结果:
make check
安装软件包:
make install
Make 软件包包含一个用于编译软件的程序。
再来一次,解决由 glibc-2.27 或更新版本 glibc 导致的错误:
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
禁止 man
.
--enable-cache-owner=bin
用于将系统范围内的 cache 文件的拥有者设置为 bin 用户。
--with-...
这些参数用于设置一些默认程序。lynx 是一个基于文本的网络浏览器(查看 BLFS 获取安装指令),vgrind 将程序源码转换为 Groff 输入,grap 在 Groof 文档排版图中非常有用。查看手册页通常并不需要 vgrind 和 grap 程序。它们并不是 LFS 或 BLFS 的一部分,但是如果需要的话你自己应该能够在完成 LFS 之后安装它们。
--with-systemd...
这些参数可防止安装不需要的 systemd 目录和文件。
编译软件包:
make
用以下命令测试结果:
make check
安装软件包:
make install
下面的表格显示了 Man-DB 假定手册页安装到 /usr/share/man/<ll>
会使用的字符编码。除此之外,Man-DB 能正确判断安装到该目录的手册页是否采用 UTF-8 编码。
表 6.1. 传统 8 位手册页预期字符编码
语言 (缩写) | 编码 | 语言 (缩写) | 编码 |
---|---|---|---|
Danish (da) | ISO-8859-1 | Croatian (hr) | ISO-8859-2 |
German (de) | ISO-8859-1 | Hungarian (hu) | ISO-8859-2 |
English (en) | ISO-8859-1 | Japanese (ja) | EUC-JP |
Spanish (es) | ISO-8859-1 | Korean (ko) | EUC-KR |
Estonian (et) | ISO-8859-1 | Lithuanian (lt) | ISO-8859-13 |
Finnish (fi) | ISO-8859-1 | Latvian (lv) | ISO-8859-13 |
French (fr) | ISO-8859-1 | Macedonian (mk) | ISO-8859-5 |
Irish (ga) | ISO-8859-1 | Polish (pl) | ISO-8859-2 |
Galician (gl) | ISO-8859-1 | Romanian (ro) | ISO-8859-2 |
Indonesian (id) | ISO-8859-1 | Russian (ru) | KOI8-R |
Icelandic (is) | ISO-8859-1 | Slovak (sk) | ISO-8859-2 |
Italian (it) | ISO-8859-1 | Slovenian (sl) | ISO-8859-2 |
Norwegian Bokmal (nb) | ISO-8859-1 | Serbian Latin (sr@latin) | ISO-8859-2 |
Dutch (nl) | ISO-8859-1 | Serbian (sr) | ISO-8859-5 |
Norwegian Nynorsk (nn) | ISO-8859-1 | Turkish (tr) | ISO-8859-9 |
Norwegian (no) | ISO-8859-1 | Ukrainian (uk) | KOI8-U |
Portuguese (pt) | ISO-8859-1 | Vietnamese (vi) | TCVN5712-1 |
Swedish (sv) | ISO-8859-1 | Simplified Chinese (zh_CN) | GBK |
Belarusian (be) | CP1251 | Simplified Chinese, Singapore (zh_SG) | GBK |
Bulgarian (bg) | CP1251 | Traditional Chinese, Hong Kong (zh_HK) | BIG5HKSCS |
Czech (cs) | ISO-8859-2 | Traditional Chinese (zh_TW) | BIG5 |
Greek (el) | ISO-8859-7 |
手册页不支持不在列表中的语言。
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 软件包包含用于读、写以及转换信息页的程序。
准备编译 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
如果后面会安装 TeX 软件包,TEXMF
makefile 变量保存了作为 TeX 树的根位置。
该信息文档系统使用一个纯文本文件来存放菜单条目清单。文件保存在 /usr/share/info/dir
。不幸的是,由于不同软件包 Makefile
的偶然问题,有时候会和系统中安装的信息页不同步。如果需要重建 /usr/share/info/dir
文件,下面的可选命令能完成任务:
pushd /usr/share/info rm -v dir for f in * do install-info $f dir 2>/dev/null done popd
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
如果要把一个 X Window 系统安装在 LFS 系统上,可能得在安装完 X 系统后再重新编译 Vim。Vim 带有一个 GUI 版本,这个版本需要安装 X 和一些额外的库。想了解更多信息,请参考 Vim 文档和 BLFS http://www.linuxfromscratch.org/blfs/view/9.0/postlfs/vim.html 中 Vim 安装指导页。
默认情况下,vim 是以不兼容 vi 的模式运行的。这对于过去使用其它编辑器的用户可能是个新问题。下面列出了「非兼容性」设置以突出显示使用的新特性。它也提醒着那些想换成「兼容」模式的人,这是配置文件里第一个该被设置的地方。这非常有必要,因为它会改变其它的设置,而且覆写必须在这个设置之后。以下面的方式,创建一个默认的 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
兼容模式更有用。删掉「no」以保留旧的 vi 特性。设置 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
该选项将不会编译已经由 Util-linux 软件包安装了的 kill 命令。
编译软件包:
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
目录而不是通常的 /etc
目录作为 adjtime
文件的位置。首先新建目录用于存储 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 程序 |
|
丢弃设备上的扇区 |
|
用于定位和打印块设备属性的命令行工具 |
|
在给定的块设备上运行区域命令 |
|
允许用户在命令行中调用块设备的 ioctls |
|
显示一个简单的日历 |
|
管理指定设备的分区表 |
|
更改 CPU 的状态 |
|
配置内存 |
|
显示和调整 OOM-killer 分数 |
|
管理进程的的实时属性 |
|
过滤掉反向换行符 |
|
为终端过滤缺少某些功能,例如加粗和半行的 nroff 输出 |
|
过滤掉指定的列 |
|
格式化指定文件为多列 |
|
设置 Ctrl+Alt+Del 组合键的功能为硬或软复位 |
|
请求 Linux 内核移除一个分区 |
|
转储内核启动信息 |
|
弹出可移除媒体 |
|
为文件预分配空间 |
|
低级别格式化软盘 |
|
管理指定设备的分区表 |
|
计算核心中文件内容的页数 |
|
通过标签或通用唯一标识符(UUID)查找文件系统 |
|
libmount 库中用于和 mountinfo、fstab 和 mtab 文件工作的命令行接口 |
|
请求一个文件锁,然后用所持有的锁执行命令 |
|
用于检查或者修复文件系统 |
|
在指定设备的 Cramfs 文件系统上进行一致性检查 |
|
在指定设备的 Minix 文件系统上进行一致性检查 |
|
FIFREEZE/FITHAW ioctl 内核驱动操作的简单封装 |
|
丢弃已挂载的文件系统中未使用的块 |
|
解析给定命令行中的选项 |
|
以十六进制或其它指定格式转储给定文件 |
|
读取或设置系统硬件时钟,也称为实时时钟(RTC)或基本输入输出系统(BIOS)时钟 |
|
到 setarch 的符号链接 |
|
为某个程序获取或设置 io 调度类和优先级 |
|
创建多种 IPC(进程间通信) 资源 |
|
移除指定的进程间通信(IPC)资源 |
|
提供 IPC 状态信息 |
|
报告 iso9660 文件系统的大小 |
|
向进程发送信号 |
|
通过反向查找 |
|
根据 |
|
向行中添加行规则 |
|
到 setarch 的符号链接 |
|
到 setarch 的符号链接 |
|
输入给定的信息到系统日志 |
|
显示以指定字符串开头的行 |
|
设置和控制环路设备 |
|
以类似树的形式列出所有或指定块设备的信息 |
|
打印 CPU 架构信息 |
|
打印目前使用的 IPC 设备的信息 |
|
打印 CPU 架构信息 |
|
列出关于用户、组和系统账号的信息 |
|
列出可用内存的范围和在线状态 |
|
列出命名空间 |
|
为 xauth 生成 magic cookies(128位随机十六进制数) |
|
控制其它用户是否可以向当前用户终端发送信息 |
|
在设备上构建文件系统(通常是一个硬盘分区) |
|
创建 Santa Cruz Operations(SCO) bfs 文件系统 |
|
创建 cramfs 文件系统 |
|
创建 Minix 文件系统 |
|
初始化指定设备或文件作为交换空间使用 |
|
用于每次显示文本一页的过滤器 |
|
在文件系统树中挂载文件系统到给定设备的指定目录 |
|
检查目录是否是一个挂载点 |
|
显示给定路径名称的符号链接 |
|
在其他进程的命名空间中运行程序 |
|
告诉内核磁盘上存在的分区和编号 |
|
使指定文件系统作为当前进程的新的根文件系统 |
|
获取或设置进程资源限制 |
|
绑定 Linux 原始字符设备到一个块设备 |
|
读取内核分析信息 |
|
重命名指定文件,用另一个字符串替换指定字符串 |
|
更改运行中进程的优先级 |
|
请求 Linux 内核重新设置分区大小 |
|
反转指定文件的行 |
|
用于启用和禁用无线设备的工具 |
|
用于进入系统睡眠状态知道指定的唤醒时间 |
|
生成终端会话的打字稿 |
|
用定时信息播放打字稿 |
|
在新程序环境中更改报告架构并设置个性标签 |
|
在新会话中运行指定程序 |
|
设置终端属性 |
|
磁盘分区表管理器 |
|
允许 |
|
允许更改交换空间 UUID 和标签 |
|
停用设备和文件的分页和交换机制 |
|
启用设备和文件的分页和交换机制并列出当前使用的设备和文件 |
|
切换到另一个文件系统并把当前路径作为挂载树的根 |
|
跟踪日志文件的的增长;显示日志文件的最后 10 行然后继续显示日志文件中 添加的任何新条目 |
|
获取或设置一个进程的 CPU 亲和力 |
|
将强调转换为转义序列以表示强调正在使用的终端的过滤器 |
|
断开文件系统到系统文件树的连接 |
|
一个到 setarch 的符号链接 |
|
用一些父进程非共享的名字空间运行程序 |
|
以更友好的格式显示指定登录文件的内容 |
|
UUID 库用于生成基于时间的安全和保证唯一的 UUID 的守护进程 |
|
创建新的 UUID。在所有创建的 UUID 中,在本地系统或其它系统,在之前和以后,每个新的 UUID 都可以被认为是唯一的 |
|
解析唯一标识符的实用程序 |
|
在终端上显示所有当前登录用户的文件内容,或者默认的标准输出 |
|
显示硬件看门狗状态 |
|
报告指定命令的二进制文件、源代码或者 man 手册的位置 |
|
从设备中擦除文件系统签名 |
|
到 setarch 的符号链接 |
|
用于设置和控制 zram(压缩后的 ram 磁盘)的程序 |
|
包含用于设备识别和标记提取的例程 |
|
包含操作分区表的例程 |
|
包含用于块设备挂载和卸载的例程 |
|
包含以表格形式进行屏幕输出的例程 |
|
包含用于生成在本地系统之上可访问对象的唯一标识符的例程 |
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 的其中一个测试程序会试图分配 256M 的内存。如果你没有比这更多的 RAM,确保为测试启用了足够的交换空间。阅读 第 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
搜索设备(通常是一个磁盘分区)的坏块 |
|
更改 |
|
错误表编译器;它将错误代码名称和信息对照表转换为适用于 |
|
文件系统调试器;可用于检查和更改 |
|
对指定设备上的文件系统打印超级块和块组信息 |
|
报告空闲空间的碎片信息 |
|
用于检查或者修复 |
|
用于将重要 |
|
显示或更改指定设备上的 |
|
检查 ext4 文件系统的 MMP 状态 |
|
检查已挂载的 ext[234] 文件系统的内容 |
|
检查所有已挂载的 ext[234] 文件系统的错误 |
|
对设备上发现的 ext2/ext3/ext4 文件系统重做撤销日志 undo_log [ 这可用于取消一个 e2fsprogs 程序的失败操作。] |
|
Ext4 文件系统加密实用程序 |
|
ext4 文件系统的在线碎片整理器 |
|
报告一个文件可能的碎片化程度 |
|
默认检查 |
|
默认检查 |
|
默认检查 |
|
在日志文件中保存命令的输出 |
|
列出二级扩展文件系统中一个文件的属性 |
|
将命令名称和帮助信息的映射表转换为适用于 |
|
在指定设备上创建 |
|
默认创建 |
|
默认创建 |
|
默认创建 |
|
用于在 |
|
用于伸缩 |
|
调整 |
|
常用错误显示例程 |
|
用于 dumpe2fs,chattr,和 lsattr |
|
包含使用户层程序可以操作 |
|
用于 debugfs |
Sysklogd 软件包包含用于记录系统消息的程序,它们记录着,诸如一些异乎寻常的事件发生时,内核给出的那些消息。
首先,修复 klogd 在某些条件下会导致「segmentation fault」的问题,并修复一个过时的程序结构:
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 的软件包包含控制系统启动、运行和关闭的程序。
首先,打一个补丁移除一些其他软件包安装的程序,改正一个消息,并修复一个编译器的警告:
patch -Np1 -i ../sysvinit-2.95-consolidated-1.patch
编译软件包:
make
该软件包没有测试套件。
安装软件包:
make install
记录启动消息至日志文件 |
|
执行 fstab-encoded 参数的命令 |
|
通常调用命令 shutdown 连带着它的
|
|
内核在初始化硬件时第一个启动的进程,用于接管启动过程并启动所有在配置文件中被指定的进程 |
|
给所有进程发送信号,除了其本身所处的会话的进程,所以并不会杀死自己的父 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
现在创建一些目录,不仅仅是为了测试需求,一部分的安装操作也将用到:
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 倍。
因为大多数用户从来不会在他们的系统软件上使用调试器,没了这些调试符号可以省下很多磁盘空间。下一页将会告诉你如何剥离程序和库中所有的调试符号。
这个部分是可选的。如果预期的用户不是一个程序员或者不打算对系统软件进行任何调试,通过从二进制文件和库中删除调试符号能减少 90MB 的系统大小。除了不能完全调试软件,这不会导致任何不便。
大部分人使用下面提到的命令并不会感到任何困难。然而,很容易出现错误并导致新的系统不可用,因此在运行 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/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 系统中通常不需要它们。这些都是没有必要的东西。想要删除的话,运行:
find /usr/lib /usr/libexec -name \*.la -delete
关于 libtool 归档文件的更多信息,参考 BLFS 段落「关于 Libtool 归档(.la)文件」。
启动 Linux 系统牵涉到好些任务。该过程须挂载虚拟和现实两个文件系统,初始化设备,激活 swap,检查文件系统是否完整,挂载所有的 swap 分区和文件,设置系统时钟,配属网络,起动系统需要的守护进程,并完成用户想要完成的那些自定义的任务。该过程必须有组织有纪律,以确保任务能被井然有序地实施,并尽可能快地执行。
1983 年以来,System V 便是 Unix 和类 Unix (例如 Linux)系统中的经典启动过程。它包括小程序 init 用于启动诸如 login (由 getty 启动)这样的基础程序,并运行着名为 rc 的脚本。该脚本,控制着一众附加脚本的执行,而那些附加脚本便是实施系统初始化所需要的任务的脚本。
程序 init 由文件
/etc/inittab
控制着,并且被组织成用户能够运行的运行级别形式:
0 — 停止
1 — 单用户模式
2 — 多用户,无网络
3 — 完整的多用户模式
4 — 用户可定义
5 — 完整的多用户模式,附带显示管理
6 — 重启
常用的默认运行级为 3 或 5。
公认的,容易理解的体系。
方便定制。
启动较慢。中等速度的 LFS 的系统,从内核的第一个消息至登录提示需花费 8-12 秒。网络连接一般在登录提示后约 2 秒内建立。
串联执行启动任务。与上一点有些关联。过程中的任何一个,比方说,文件系统检查有延迟,就会拖延整个启动的过程。
不支持控制组群(control groups,缩写为 cgroups)这样的新特性,根据每个用户公平地分享调度。
添加脚本需手动的、静态的排序决策。
软件包 LFS-Bootscripts 包含一套在启动/关机时开始/停止 LFS 系统的脚本。自定义启动过程所需的配置文件和程序将在随后的段落中交代。
安装软件包:
make install
在文件系统被挂在前,检查它的完整性(基于日志和网络的文件系统除外) |
|
删除重启前后不应保留的文件,例如 |
|
载入当前键盘布局需要的键盘表;它还会设置屏幕的字体 |
|
包含一些启动脚本用到常规功能,例如异常和状态的检查 |
|
终止系统 |
|
停止一个网络设备 |
|
初始化一个网络设备 |
|
设置系统的主机名和本地回环设备 |
|
载入 |
|
挂载除标记为 noauto 的或是基于网络的文件系统外的所有的文件系统 |
|
挂载虚拟内核的文件系统,例如 |
|
设置网络接口,例如网卡,并设置默认网关(如果适用) |
|
主宰运行级的控制脚本;负责将所有其他的启动脚本一个接着一个地执行,通过处理过的符号链接名决定先后顺序 |
|
重新启动系统 |
|
确认所有的进程在系统重启或关闭之前已被终止 |
|
如果硬件时钟没有被设置成 UTC 则重新将内核时钟设置成本地时间 |
|
提供分配静态网络协议(IP)地址至网络接口的功能 |
|
启用和禁用 swap 文件和分区 |
|
如果文件 |
|
开始和停止系统和内核日志守护进程 |
|
为其他守护进程创建启动脚本时用到模板 |
|
准备 |
|
如有必要,重试失败的 udev uevents,并将生成的规则文件从 |
在 第 6 章 我们构建 eudev 时,已经安装了 udev 包。在我们详细说明 udev 系统的用法之前,我们先大致了解早先的设备控制方式。
传统的 Linux 不管硬件是否真实存在,都以创建静态设备的方法来处理硬件,因此需要在 /dev
目录下创建大量的设备节点文件(有时会有上千个)。这通常由
MAKEDEV
脚本完成,它通过大量调用 mknod
程序为这个世界上可能存在的每一个设备建立对应的主设备号和次设备号。
而使用 udev
方法,只有当内核检测到硬件接入,才会建立对应的节点文件。因为需要在系统启动的时候重新建立设备节点文件,所以将它存储在
devtmpfs
文件系统中(完全存在于内存中的虚拟文件系统)。设备节点文件无需太多的空间,所以占用的内存也很小。
2000 年 2 月,一种名叫 devfs
的文件系统被合并到 2.3.46 版本的内核之中,而在 2.4
系列的稳定内核中基本可用。尽管它存在于内核代码中,但是这种动态创建设备的方法却从来都没有到核心开发者的大力支持。
问题存在于它处理设备的检测、创建和命令的方式。其中最大的问题莫过于它对设备节点的命名方式。大部分开发者的观点是:设备的命名应该由系统的所有者决定,而不是开发者。而且
devfs
存在严重的竞争条件(race
condition)问题,如不对内核做大量的修改就无法修正这一问题。最终,因为缺乏有效的维护,在 2006 年 6
月终被移出内核源代码。
后来,有一种新的虚拟文件系统 sysfs
在 2.5
系列测试版本内核中引入,并且加入了 2.6 系列的稳定版本内核之中。sysfs
系统的任务就是将系统中的硬件配置状态导出至用户空间,而这给开发一种运行于用户空间的新型 devfs
系统带来了可能。
上文简单的提及了 sysfs
文件系统。有些人可能会问,sysfs
到底是如何知道当前系统有哪些设备、这些设备又该使用什么设备号呢。对于那些已经编译进内核的设备,会在内核检测到时被直接注册为
sysfs
对象(由 devtmpfs
内建)。对于编译为内核模块的设备,将会在模块载入的时候注册。一旦 sysfs
文件系统挂载到 /sys,已经在 sysfs
注册的硬件数据就可以被用户空间的进程使用,随后也就可以被
udevd 处理了(包括对设备节点进行修改)。
设备文件是通过内核中的 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:v00001319d00000801sv*sd*bc04sc01i*」的别名,总线驱动导出该驱动别名并通过
sysfs
处理相关设备。例如,文件
/sys/bus/pci/devices/0000:00:0d.0/modalias
应该会包含字符串「pci:v00001319d00000801sv00001319sd00001319bc04sc01i00」。udev
采用的默认规则会让 udevd 根据 uevent 环境变量
MODALIAS
的内容(它应该和 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
文件,那么就能驱动这个设备并可以直接操作它,但是如果该文件里没有包含设备别名,那意味着这个驱动有问题。我们可以先尝试不依靠
udev 直接加载驱动,并寄希望于这个问题能在日后得到解决。
如果在 /sys/bus
下的相应目录里没有
modalias
的话,意味着内核开发人员还没有为这个总线类型增加 modalias 支持。使用 Linux-5.2.8 内核,应该是
ISA 总线的问题。希望这个可以在后面的内核版本里得到解决。
udev 根本不打算加载 snd-pcm-oss 这样的「封装」驱动程序和 loop 这样的非硬件设备驱动。
如果「封装」模块只是强化其它模块的功能(比如,snd-pcm-oss 模块通过允许 OSS
应用直接访问声卡的方式加强了 snd-pcm 模块的功能),需要配置 modprobe 在 udev
加载硬件驱动模块后再加载相应的封装模块。为此,可以在对应的 /etc/modprobe.d/
文件中增加「softdep」行。例如:
<filename>
.conf
softdep snd-pcm post: snd-pcm-oss
请注意「softdep」也支持 pre:
的依赖方式,或者混合 pre:
和 post:
。查看 modprobe.d(5)
手册了解更多关于「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 被设计成并行处理 uevents 并加载模块,所以是不可预期的顺序。这个不会被「修复」。你不应该依赖稳定的内核模块名称。而是在检测到设备的稳定特征,比如序列号或 udev 安装的一些 *_id 应用的输出,来判断设备的稳定名称,之后创建自己的规则生成相应的软链接。可以参考 第 7.4 节 「设备管理」 和 第 7.5 节 「通用网络配置」。
其他的帮助文档可以参考下面的链接:
一般来说,udev 根据获取到的固件/BIOS 信息或者一些像总线号、插槽号或 MAC 地址等物理信息来命名网络设备。之所以采用这样的命名方式,是为了确保网络设备可以得到一个持久化的名称,且这个名称不会随着设备的发现时间而改变。例如,在一台同时拥有 Intel 网卡和 Realtek 网卡的双网卡计算机上,Intel 网卡可能会被命名为 eth0 而 Realtek 网卡可能会被命名为 eth1。在某些情况下,可能是一次重启使网卡重新编号了。
在持久化命名方式中,典型的网络设备名称看起来可能像 enp5s0 或者 wlp3s0 。如果你不希望采用这种命名方式,则还可以采用传统命名或者自定义的命名方式。
可以在内核命令行上添加 net.ifnames=0
来恢复传统命名方式,采用 eth0、eth1
等来命名。这种命名方式对于那些网络设备类型唯一的系统最为合适。笔记本电脑通常拥有多个以太网连接,常被命名为 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 地址并不是永久固定的。在这种情况下,不能采用持久化命名方式来确定设备名称。
这个文件应由一个注释段落开头。之后每个网卡都对应有两行信息。第一行以注释形式描述了该网卡的硬件信息(如果是 PCI 网卡,还有其 PCI 厂商标识和设备标识),如果对应的驱动程序存在,还应在随后的括号中注明。要注意,该注释行中的硬件标识和驱动程序都不能决定其接口名,仅做参考作用。第二行则是网卡的 udev 规则,根据该规则可以确定网卡的设备名称。
udev 规则总的来说是由逗号或空格分割的若干键值对组成的,规则中的每一个键的解释如下所示:
SUBSYSTEM=="net"
- 让
udev 忽略不是网卡的设备。
ACTION=="add"
- 让 udev
对增加 uevent 以外的操作,忽略该规则(删除 uevent 和改变 uevent
也会发生,但都不会导致网络接口重新命名)。
DRIVERS=="?*"
- 让 udev
忽略 VLAN
和桥接子接口。(由于这些子接口没有独立的驱动程序)。而且子接口的名称可能会与父接口的名字相冲突,因而会被忽略。
ATTR{address}
- 该键值表示网卡的
MAC 地址。
ATTR{type}=="1"
-
对于一些可能会产生多个虚拟接口的无线网卡驱动,该键用于确保规则只会应用于其首要接口。和之前的 VLAN
接口和桥接子接口的理由一样,次要接口会被忽略。
NAME
- 这个键中的值将是 udev
指定给接口的名称。
NAME
的健值相当重要。在进行操作之前,你已经为每一个网卡设备准备了一个名称,并且在之后的配置文件中使用对应的
NAME
键值。
你之后可能会安装的一些应用程序(如各种媒体播放器)需要如下两个符号链接:/dev/cdrom
和 /dev/dvd
存在并正确指向一个 CD-ROM 或 DVD-ROM 设备。另外,在
/etc/fstab
文件中添加对这些符号链接的引用,在用的时候会更方便。根据计算机中各设备之间的兼容性,udev
使用了一个脚本来生成创建这些符号链接的规则文件,但是你仍需要决定脚本以两种模式中的哪一种来运行。
第一种方式是采用「by-path」模式(该模式通常默认被 USB 设备和 FireWire 设备使用),这种方式根据 CD 或 DVD 设备的物理路径来创建规则。而第二种方式则是「by-id」模式 (这是 IDE 设备和 SCSI 设备的默认方式),这种方式根据存储在 CD 或 DVD 驱动器中的标识字符串来创建规则。设备的路径可以由 udev 的 path_id 脚本来获取,而标识字符串则是根据不同的设备类型,使用设备驱动程序自带的 ata_id 或者 scsi_id 命令来获取,具体是哪个取决于你的设备类型。
这两种命名方式各有其特定使用场景,应根据可能发生的硬件变化来选择所采用的命名方式。若你希望设备的物理地址(即,其插入的端口和/或插槽)发生改变,例如你计划把一个设备连接到另外一个 IDE 端口或另外一个 USB 连接器,那么应采用「by-id」模式来命名设备。在另一方面,如果你仅仅希望改变设备的标识字符串,则可以采用「by-path」方式。例如系统中某个设备发生了故障,在其所对应的物理接口上插入另一个功能完全兼容的同类型设备。
如果任意一种模式都是可行的,那就根据更可能发生的场景来选择命名方式。
外部设备(例如一个通过 USB 连接的 CD 驱动器)不应该使用「by-path」模式来命名,因为这种设备一旦插入不同的物理接口,其地址就可能会发生改变。不仅仅是 CD 和 DVD 驱动器,其他所有外部连接的设备都会产生这种问题,所以在创建 udev 规则的时候应该避免通过物理地址来识别外部设备。
如果你想查看 udev 将要对一个特定的 CD-ROM 设备将要使用的脚本,应该在 /sys
文件夹下查找相应的设备文件(例如 /sys/block/hdd
)并运行如下的相应命令:
udevadm test /sys/block/hdd
查看包含各种 *_id 程序输出内容的行。如果 ID_SERIAL 存在且内容非空,「by-id」模式将采取这个值来命名设备,否则会将 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
系统,而且我们推测这些符号链接于宿主机中已经存在了。当你首次引导进入 LFS 系统之后,这些规则和符号链接都会被自动创建。
然而如果你有多个 CD-ROM
设备,在首次创建符号链接的时候,同一个符号链接所指向的设备可能与宿主机中指向的设备不同。这是由于设备发现的顺序是不可预测的。首次引导进
LFS
系统并创建的符号连接已经可以认为是固定了的。所以这个问题仅会在你需求同一设备在不同的系统之间的符号链接相同时才会产生。如果有必要的话,你还可以在系统引导之后检查并修改生成的
/etc/udev/rules.d/70-persistent-cd.rules
文件,以确认系统创建的符号连接符合自己所期望的。
正如 第 7.3 节 「设备与模块管理概述」
中解释的,具有相同功能的设备出现在 /dev
目录下的顺序是随机的。假如你有一个 USB 摄像头和一个电视调谐器,/dev/video0
有可能是 USB 摄像头, /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"
# Persistent symlinks for webcam and tuner
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
依旧会随机分配给 USB 摄像头和电视调谐器,但是
/dev/tvtuner
和 /dev/webcam
将会固定的分配给正确的设备。
网络脚本开启和关闭哪些端口,一般取决于 /etc/sysconfig/
下的文件。此文件夹应该包含要配置接口的描述文件,例如
ifconfig.xyz
,其中的「xyz」用于描述网卡。(例如
eth0)就是常用的接口名称。文件的内容是此接口的属性信息,例如 IP 地址、子网掩码等。另外,文件名只能是
ifconfig。
如果没有实施过上一节的内容,那么 udev 将会根据网卡的物理属性分配适当的名称。如果你不确定网卡接口的名称,则可以在系统启动之后执行 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
网络脚本将在系统启动期间激活网卡,如果设置为其他非「yes」值则不会自动激活网卡。但是你可以通过命令
ifup 和
ifdown
手动激活或者是禁用网卡。
变量 IFACE
用于定义接口的名称,例如
eth0。所有的网络设备配置文件都依赖于它。文件扩展名必须与该值匹配。
变量 SERVICE
定义了获取 IP
地址的方式。LFS-Bootscripts 包中有模块化的 IP 分配格式,且在文件夹 /lib/services/
中建立文件以允许其他的 IP 获取方式。在 BLFS
书中,通常它被设置为动态主机配置协议(DHCP)。
变量 GATEWAY
应该包括默认的网关 IP
地址。如果不需要,应该直接将这行注释掉。
变量 PREFIX
表示子网所用的位数。IP 地址中每一个字节是 8
位。如果子网掩码是 255.255.255.0,那么就会使用最前面的 3 个字节(24位)表达网络号。如果子网掩码是
255.255.255.240,则使用最开始的 28 位。前缀如果大于 24 位,一般都用于 DSL 或者是有线
ISP。在此示例中(PREFIX=24),子网掩码是
255.255.255.0。根据你实际的指望调整此字段,如果省略此字段,则默认为 24。
如欲获得更多信息,请浏览 ifup 的 man 手册。
如果系统连接到互联网,则需要通过 DNS 服务将域名和 IP 地址进行相互的转换。你可以从 ISP
或者是网络管理员处获得可用的 DNS 服务器,并填入 /etc/resolv.conf
文件。可以通过以下命令建立此文件:
系统需要一些 DNS(域名服务)名称解析的方法,将 Internet 的域名解析成 IP
地址,反之亦然。最佳的办法就是将,从 ISP 或网络管理员那里获取的,DNS 服务器的 IP 地址填入
/etc/resolv.conf
文件。可以通过以下命令建立此文件:
cat > /etc/resolv.conf << "EOF"
# Begin /etc/resolv.conf
domain <Your Domain Name>
nameserver <IP address of your primary nameserver>
nameserver <IP address of your secondary nameserver>
# End /etc/resolv.conf
EOF
字段 domain
可以忽略或者是使用 search
字段替代。参考 man 手册的 resolv.conf 部分获得更多信息。
其中,<IP address of the
nameserver>
应该替换为合适的 DNS 服务器的 IP
地址。通常来说,一般会有多个 DNS 地址可供填写(需要备选服务器具有相关兼容性)。如果你只想要填写一个 DNS
服务器,那么将文件中的第二行 nameserver 移除即可。该 IP
地址也可能是本地网络中的路由。
Google 公共 IPv4 DNS 的服务器地址是 8.8.8.8 和 8.8.4.4。
在系统启动期间,文件 /etc/hostname
用于建立系统的主机名称。
通过以下命令建立 /etc/hostname
文件且向其中写入主机名:
echo "<lfs>
" > /etc/hostname
<lfs>
替换为你想要设置的名称。请不要输入完整域名(Fully Qualified Domain
Name,FQDN),那应该是放在 /etc/hosts
文件中的信息。
文件 /etc/hosts
用于 IP
地址和完全限定域名(FQDN)及可能的别名的对应。它的语法如下:
IP_address myhost.example.org aliases
除非计算机在互联网中可见(例如拥有注册的域名且分配了有效的 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,有效的完全限定域名可以是 lfs.example.org。
即使不需要使用网卡,也应该需要有效的完全限定域名。它的存在可以确保程序能正常运行。
通过以下命令建立 /etc/hosts
文件:
cat > /etc/hosts << "EOF"
# Begin /etc/hosts
127.0.0.1 localhost
127.0.1.1 <FQDN>
<HOSTNAME>
<192.168.1.1>
<FQDN>
<HOSTNAME>
[alias1] [alias2 ...]
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
# End /etc/hosts
EOF
其中,<192.168.1.1>
,<FQDN>
,和
<HOSTNAME>
的值应该根据用户和实际需要进行更改(如果已经通过网络/系统管理员获得了 IP
地址,那么设备就可以连接到已存在的网络)。其中可选的别名可以忽略。
Linux 使用的是一个名为 SysVinit,基于运行级(run-levels)概念的特殊启动工具。每个系统之间总会有些许不同,所以在某个特定的 Linux 发行版上适用的东西,并不意味着在 LFS 上也适用。LFS 自有独到的解决之道,不过也会遵守普世的标准。
SysVinit(或称「init」,下同)以运行级的方案运作。内含 7 个(数字
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 意味着停止(杀死)服务,S
意味着启动服务。而这些数字则决定了脚本的运行顺序,从 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,触发已经被内核创建好的「冷插拔(coldplug)」设备,并等待规则完成。该脚本还会解除
/sbin/hotplug
中默认的 uevent
处理。这是因为内核不再需要调用外部的二进制文件了。取而代之的是,udevd 会为内核引发的 uevent 去监听
netlink 的套接字。
初始化脚本 /etc/rc.d/init.d/udev_retry
负责重新触发子系统的事件,这些子系统的规则,可能会依赖于直到 mountfs 脚本运行后才挂载的文件系统(特别是
/usr
和 /var
)。该脚本会在 mountfs
脚本后运行,所以这些规则(若是被重新触发)应该会在第二次的时候成功。这些被配置在 /etc/sysconfig/udev_retry
文件中;这个文件中,除了注释以外的任何单词(word),都会被认为是在重试时触发的子系统名称。想要找到设备的子系统,使用
udevadm info --attribute-walk
<device>,其中的 <device> 是 /dev 或
/sys 中的绝对路径,例如,/dev/sr0 或是 /sys/class/rtc。
关于内核模块载入和 udev 的更多信息,请查看 第 7.3.2.3 节 「加载模块」。
setclock
脚本从硬件时钟,或称 BIOS 或 CMOS(互补金属氧化物半导体)时钟中,读取时间。如果硬件时钟被设置为
UTC,该脚本会使用 /etc/localtime
文件(它会告知程序 hwclock
用户的时区)将硬件时钟的时间转换为本地时间。因为无法检测硬件时钟是否为 UTC,所以需要手动去配置。
在内核的启动并检测硬件功能的时候,setclock 会通过 udev 运行。同样也可以通过 stop 参数手动地将系统时间存入 CMOS 时钟中。
如果你记不清是否将硬件时钟设置成 UTC,可以通过运行命令 hwclock --localtime --show
查看。这条命令会根据硬件时钟显示当前的时间。如果显示的时间和你的手表一致,那么硬件时间可能被设置成了本地时间。如果
hwclock
的输出不是本地时间,那就有可能是 UTC 时间。在 hwclock
的输出时间上加上或减去时区之间的时差。例如,如果你所处的时区是 MST,也就是 GMT -0700,在本地时间上加上 7
小时便是 UTC 时间了。
如果硬件时钟未设置成 UTC 时间,将
UTC
变量改成 0
(零)。
运行以下命令新建文件 /etc/sysconfig/clock
:
cat > /etc/sysconfig/clock << "EOF"
# Begin /etc/sysconfig/clock
UTC=1
# Set this to any options you might need to give to hwclock,
# such as machine hardware clock type for Alphas.
CLOCKPARAMS=
# End /etc/sysconfig/clock
EOF
这里安利一个链接
http://www.linuxfromscratch.org/hints/downloads/files/time.txt,它非常好地解释了如何在
LFS 中应对时间的问题。其中说明了诸如时区,UTC 和 TZ
环境变量之类的问题。
CLOCKPARAMS 和 UTC 参数也可以在文件 /etc/sysconfig/rc.site
中设置。
本段讨论如何配置 console
的启动脚本,用它设置键盘映射,控制台字体和控制台内核的日志级别。如果非 ASCII
字符(例如,版权标志,英镑符号和欧元符号)不会用到且键盘是 U.S.
的情况下,那本段的多数内容可以跳过。没有配置文件(,或是与 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)
手册,为这些程序选择正确的参数。
/etc/sysconfig/console
文件中的一行一行的格式应该像 VARIABLE="value" 这样。下面介绍几个常见变量:
该变量用于指定由 dmesg 设置的从内核发往控制台的消息的日志级别。有效的级别从「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 语言环境的地域十分有用,反之则可能产生危害。
很多的键盘布局,在 Kbd 的软件包现有的键盘映射中并未囊括。如果该变量设置的编码是键盘映射中非 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-glyph 的 LatArCyrHeb-16 字体,这里说明以下,在不使用 framebuffer 的情况下,控制台并不能支持如此鲜艳的色彩。如果你想在不使用 framebuffer 的情况下实现多彩的颜色,并且不会有需要使用其他语言字符的困扰,你可以使用指定语言的 256-glyph 字符,如下所示:
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 模式下会出现问题,比方说,希腊语便时常需要在「字母」上添加音调。解决的方法便是,要么避免使用 UTF-8,要么安装 X Window 系统,这样就不会有这种输入处理中的限制了。
对于中文,日语,韩语以及一些其他的语言需要的字符,Linux 的控制台还无法通过配置是之正常显示。用户若要使用这些语言,需要安装 X Window 系统,用于扩充所需字符域的字体,以及合适的输入法(例如,支持语言十分广泛的 SCIM)。
/etc/sysconfig/console
文件只负责
Linux 文本控制台的本地化。对于 X Window 系统,通过 ssh
的会话,以及串口的控制台的键盘布局和终端字体,以上的设定并不适用。所以这些情况下,不要应用列出的最后两项设置中提到限制。
有时,希望在启动时创建文件。例如,/tmp/.ICE-unix
目录就可能需要创建。这可以通过在 /etc/sysconfig/createfiles
配置脚本中创建一个条目来达成。该文件的格式可以参考默认配置文件中的注释。
sysklogd
脚本调用 syslogd 程序作为 System V
初始化的一部分。-m 0
选项会关闭
syslogd默认每 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
中还包含了另外一些可以自定义的启动过程的参数。设置 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 启动脚本会以相当效率的方式启动和关闭系统,不过你可以在 rc.site
文件中进行调整,来提升速度或是根据需求调整消息。若是有这样的需求,就去调整上面 /etc/sysconfig/rc.site
文件的设置吧!
在启动脚本 udev
运行时,会调用一次
udev
settle,完成检测需要很长时间。这段时间根据当前系统的设备,可花可不花。如果你需要的仅仅是简单的分区和单个网卡,在启动的过程中,就没有必要等待这个命令执行。通过设置变量
OMIT_UDEV_SETTLE=y,可以跳过此命令。
启动脚本 udev_retry
默认也执行udev
settle。该命令仅在 /var
目录是分开挂载的情况下需要。因为这种情况下时钟需要文件
/var/lib/hwclock/adjtime
。其他的自定义设置可能也需要等待
udev 执行完成,但是在许多的安装中不需要。设置变量 OMIT_UDEV_RETRY_SETTLE=y
跳过命令。
默认情况下,文件系统检测静默执行。看上去就像是启动过程中的一个延迟。想要打开 fsck 的输出,设置变量。
重起时,你可能想完全的跳过文件系统检测 fsck。为此,可以创建
/fastboot
文件或是以
/sbin/shutdown -f -r
now 命令重启系统。另一方面,你也可以通过创建 /forcefsck
,或是在运行 shutdown 命令时,用
-F
参数代替-f
,以此来强制检测整个文件系统。
设置变量 FASTBOOT=y 将禁用启动过程中的 fsck,直至你将其移除。不推荐长时间地使用该方式。
通常,/tmp
目录中的所有文件会在启动时删除。根据存在目录与文件的数量,该操作会导致启动过程中明显的延迟。如果要跳过移除文件的操作,设置变量
SKIPTMPCLEAN=y。
在关机的过程中,init 程序会向每一个已经启动的程序(例如,agetty)发送一个 TERM 信号,等一段时间(默认为 3 秒),然后给每个进程发送 KILL 信号。对没有被自身脚本关闭的进程,sendsignals 脚本会重复此过程。init 的延迟可以通过参数来设置。比方说,想去掉 init 的延迟,可以通过在关机或重启时使用 -t0 参数(如,/sbin/shutdown -t0 -r now)。sendsignals 脚本的延迟可以通过设置参数 KILLDELAY=0 跳过。
shell 程序 /bin/bash
(下文称为「shell」)使用一系列的启动文件来帮助建立将要运行的环境。每一个文件都有特定的用途,它可能会影响登陆和交互环境。在
/etc
目录下的文件提供了全局的配置。如果等效的文件存在于用户文件夹,则可能会覆盖全局设置。
交互式登录 shell 在成功登录后启动,它使用 /bin/login 去读取 /etc/passwd
。交互式非登录 shell 通过命令行(如 [prompt]$
/bin/bash )启动。非交互式 shell
通常是一段正在运行的 shell 脚本。它之所以是非交互的是因为它执行一段脚本却不需要用户在命令间输入指令。
更多信息,阅读 info bash 下 Bash Startup Files and Interactive Shells 章节。
当 shell 被作为交互登陆 shell 时,会读取文件 /etc/profile
和 ~/.bash_profile
。
本地语言的支持依赖于 /etc/profile
它包含不少于此相关的环境变量。更改此文件后,可能会出现以下的变化:
程序的输出则会翻译为本地语言
修正字符在字母、数字和其它类的分类。对于非英语区域设置来说,只有这样,bash 才能正常显示非 ASCII 字符
国家/地区顺序可以按照字母顺序正常排序(译者注:这里所谓的正常排序,应该是首先将国家/地区名称转换成本地语言的国家/地区名称,比如中国的开头字母是 Z,那么显示就很靠后了。但是假如显示为英文,就是 C 开头,那么将会靠前显示。)
默认纸张尺寸
货币、时间和日期值的格式
/etc/locale.conf 中 <ll>
使用语言代码代替(比如中文是「zh」,英文是「en」),<CC>
使用国家/地区代码代替(比如中国是「CN」,中国香港是「HK」,中国台湾是「TW」,美国是「US」),<charmap>
使用选定字符集指定的标准字符映射表替换。诸如「@euro」这样的可选修饰符也可使用。
运行以下命令可以获得当前 Glibc 支持的本地字符集。
locale -a
字符映射表可能存在很多的别名,比如「ISO-8859-1」可以写作
「iso8859-1」或「iso88591」。但是有一些程序不支持这些乱七八糟的写法(比如「UTF-8」只能写作「UTF-8」,「utf8」它就不认识了)。所以,为了安全起见,在设置的时候还是尽量的使用特定区域设置的规范名称。可以通过以下命令,查询在特定区域下的字符映射表标准名称,<locale
name>
为运行 locale
-a 输出的首选区域设置(这里以「zh_CN.utf8」为例)。
LC_ALL=<locale name>
locale charmap
对于「zh_CN.utf8」以上命令将会如下输出:
UTF-8
根据以上输出,我们再次修改 /etc/locale.conf,将字符映射表设置为标准形式(「zh_CN.utf8」变为「zh_CN.UTF-8」)。同理,也可一并查询以下设置的标准命令,然后将其添加到 bash 的启动文件中 (译者注:对于 bash 来说,启动文件为 .bashrc)。
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
以上的命令将会打印当前区域设置的语言、字符编码、本地货币单位以及电话国际编码。如果出现类似下文的错误输出,可能是你没有严格按照第六章指导的方法操作或者是你当前所用的 Glibc 不支持。
locale: Cannot set LC_* to default locale: No such file or directory
如果这种情况真的发生,你应该使用 localedef 命令安装对应的系统区域,或者是考虑更改为其它的区域。假如没有出现错误提示,我们就可以继续进行下一步操作了!
有一些 LFS 之外的包可能出现对你设置的区域支持很差劲的情况。比如 X 的库(X Windows System 的一部份),就可能在内部文件中输出以下消息:
Warning: locale not supported by Xlib, locale set to C
在若干情况下,Xlib 希望以带规范破折号的大写形式列出字符映射表。比「ISO-8859-1」而不应该写作「iso88591」。不过,也可以通过去除区域规范中的字符映射部分找到合适的规范。这可以通过运行 locale charmap 命令来检查。例如,需要更改「de_DE.ISO-8859-15@euro」为「de_DE@euro」以便 Xlib 能识别区域。
即便如此,也可能遇到某些程序因为区域设置和它们预置的不同而导致功能异常(可能不会显示任何的错误消息)。 如果出现这样的情况,可以通过查看其它的发行版是如何进行设置区域,从而得到启发。
一旦确定了到底该使用哪个区域设置,就可以创建 /etc/profile
文件了:
cat > /etc/profile << "EOF"
# Begin /etc/profile
export LANG=<ll>_<CC>.<charmap><@modifiers>
# End /etc/profile
EOF
「C」(默认)和「en_US」(推荐美国英语用户使用)这两种区域设置有所不同。 「C」使用 US-ASCII 7 位字符集,并把设置了最高位的字节作为无效字符。 这就是为什么类似 ls 的命令本地化时会用疑问号代替。同样,如果你想要使用 Mutt 或 Pine 发送包含有类似字符的邮件,将会得到如下消息:非 RFC 兼容字符(发送邮件中的字符集为「unknown 8-bit」)。 所以,如果你一定以及肯定一定不会用到 8 位的字符,那你可以仅使用「C」。
不少程序还不支持 UTF-8 区域设置。我们正在完善文档并修复类似问题, 可以查看http://www.linuxfromscratch.org/blfs/view/9.0/introduction/locale-issues.html.
inputrc
文件的作用是告知系统应该以怎样的键盘布局处理键盘。此文件对于 readline —— 输入相关库,或者是一些 shell
(例如 bash 等)来说十分重要。
对于大部分用户来说,都不使用那些奇奇怪怪的键盘映射,所以,可以通过以下的命令建立一个全局的 /etc/inputrc
以供所有用户使用。如果需要更改某一个用户的键盘映射,仅需要在那个用户的 HOME 目录下建立一个 .inputrc
文件,然后修改对应的键盘映射就可以了。
如果需要了解更多有关如何编辑 inputrc
文件的信息,可以查看
info bash 中
Readline Init File
一节。其实查看 info
readline 也可以获得不少有用的东西。
下面显示的就是通用的 inputrc
文件,其中包含有 “#”
的都是注释行(需要注意的是,此文件不支持在设置后跟随注释)。使用以下命令创建此文件:
cat > /etc/inputrc << "EOF"
# Begin /etc/inputrc
# Modified by Chris Lynn <roryo@roryo.dynup.net>
# Allow the command prompt to wrap to the next line
set horizontal-scroll-mode Off
# Enable 8bit input
set meta-flag On
set input-meta On
# Turns off 8th bit stripping
set convert-meta Off
# Keep the 8th bit for display
set output-meta On
# none, visible or audible
set bell-style none
# All of the following map the escape sequence of the value
# contained in the 1st argument to the readline specific functions
"\eOd": backward-word
"\eOc": forward-word
# for linux console
"\e[1~": beginning-of-line
"\e[4~": end-of-line
"\e[5~": beginning-of-history
"\e[6~": end-of-history
"\e[3~": delete-char
"\e[2~": quoted-insert
# for xterm
"\eOH": beginning-of-line
"\eOF": end-of-line
# for Konsole
"\e[H": beginning-of-line
"\e[F": end-of-line
# End /etc/inputrc
EOF
shells
文件是当前系统所有可用 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
文件的作用是让其它程序确定存储设备的默认挂载点、挂载参数和检查信息(例如完整性检测)。仿照以下格式新建一个文件系统列表(file
system table,简称 fstab)文件:
cat > /etc/fstab << "EOF"
# Begin /etc/fstab
# file system mount-point type options dump fsck
# order
/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(译者注:fsck
列的数值来决定需要检查的文件系统的检查顺序。允许的数字是0, 1, 和2。根目录应当获得最高的优先权 1,
其它所有需要被检查的设备设置为 2。0 表示设备不会被 fsck 所检查)。
基于 MS-DOS 或者是来源于 Windows
的文件系统(例如:vfat,ntfs,smbfs,cifs,iso9660,udf)需要在挂载选项中添加「iocharset」,才能让非
ASCII
字符的文件名正确解析。此选项的值应该与语言区域设置的值相同,以便让内核能正确处理。此选项在相关字符集定义已为内核内建或是编译为模块时生效(在文件系统
-> 本地语言支持中查看)。此外,vfat 和 smbfs 还需启用「codepage」支持。例如,想要挂载 USB
闪存设备,zh-CN.GBK 用户需要在 /etc/fstab
中添加以下的挂载选项:
noauto,user,quiet,showexec,iocharset=gbk,codepage=936
对于 zh_CN.UTF-8 用户的对应选项是:
noauto,user,quiet,showexec,iocharset=utf8,codepage=936
需要注意的是,iocharset
默认值是 iso8859-1
(其保证文件系统大小写敏感),而 utf8
这个参数告知内核使用 UTF-8 转换文件名,以便可以在 UTF-8
语言环境中解释它们。
此外,还有可能在内核的配置过程中就指定一些文件系统的默认代码页和 iocharset 值。相关参数有「默认 NLS 选项
」(CONFIG_NLS_DEFAULT
),「默认远程 NLS
选项」(CONFIG_SMB_NLS_DEFAULT
),「FAT
默认代码页」(CONFIG_FAT_DEFAULT_CODEPAGE
),和「FAT 默认 IO
字符集」(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]
根据对系统的不同需求,可能需要这些选项。BLFS 软件包需要的选项列表,参考 BLFS 的内核设定索引(http://www.linuxfromscratch.org/blfs/view/9.0/longindex.html#kernel-config-index).
如果你主机的硬件用的是 UEFI,那么上面的‘make defconfig’应该会自动添加一些 EFI 相关的内核选项。
为了让你的 LFS 内核,在你的主机是 UEFI 引导环境的情况下,能够被引导,你的内核必须要有这项:
Processor type and features ---> [*] EFI stub support [CONFIG_EFI_STUB]
文件 lfs-uefi.txt 中包含了管理 UEFI 环境的完整描述,参见 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=
建立与宿主系统相同的地域设定。在 UTF-8 linux 文本命令行上逐行绘制适宜的 menuconfig ncurses 接口时可能需要这项配置。
要使用的话,请我务必使用宿主系统中的变量 $LANG
去代替 <host_LANG_value>
。你也可以用宿主系统中的
$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 绑定到宿主的 /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 环境里做的),解压出来的文件会拥有生成这个包的电脑里用户和组。在安装其他包的时候这并不是问题,因为它们的源代码在安装完后就删除了。不过,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.conf
。如果将
USB 设备(ehci_hcd、ohci_hcd 和 uhci_hcd)
编译为模块,则需要此文件,这样它们就会以正确的顺序加载。ehci_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 进行错误的配置可能会导致在没有外置启动设备(某些USB设备,某些 CD-ROM 等)的情况下无法正常的启动。你可能仅仅需要修改当前正在使用的引导器(比如:Grub-Legacy,GRUB2 或 LILO 等)的配置。
一定要确保你有一个可以引导的光盘或者是 U
盘以备不时之需,否则万一电脑真的不能启动谁都救不了你。如果你需要建立可引导的设备,可以按照以下方法操作(跳转到 BLFS
从
libisoburn 软件包安装 xorriso
)。
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)功能。详细参见文件 lfs-uefi.txt。
GRUB 对于硬盘和分区自有一套命名规则 (hdn,m),其中 n 是硬盘数,m 是分区号。硬盘数 N 从 0
开始计数,分区数需要区别对待——主分区从 1 开始计数而扩展分区从 5
开始计数。需要注意的是,和早期版本相比,计数方式都有所变化。例如,分区 sda1
是 (hd0,1),sdb3
是 (hd1,3)。Linux 下,并不将 CD-ROM
设备假想为硬盘。例如,就算已有 CD 设备挂载为 hdb
,第二块硬盘挂载为 hdc
,GRUB 依旧将第二块硬盘称为 (hd1)。
GRUB 会将一些数据写入硬盘的第一个物理扇区。这一部分不属于任何一个操作系统,在启动时,该部分数据激活,然后寻找 Grub 的模块,Grub 模块的默认位置为 /boot/grub/。
一种建议是使用一个独立的小分区(建议大小 100MB)专用于引导信息。那样的话,每一个发行版,不论是 LFS
还是其他的商业发行版,都能访问相同的引导文件而且任何已经启动的系统都能访问它。如果你选择这么做,你需要挂载这个独立分区,移动所有的文件从当前的
/boot
目录(比如说你上一节刚编译的 Linux
内核)到新的分区。你然后要卸载这个新分区,重新挂载它为 /boot
。如果你这么做,一定要更新 /etc/fstab
。
使用当前的 lfs 分区也没有什么问题,但是在配置多系统启动的时候有些不同。
从以上信息可知,需要确定根分区的磁盘位置(如果使用单独的分区,则需要知道引导分区的磁盘位置),以下假定根分区(或者是磁盘分区)是
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/ 下的一组脚本且将会忽略用户的设置。这些命令主要用于那些非源码编译的发行版,所以不建议 LFS 用户使用此命令。如果你使用商业发行版,你可以试着运行这个命令(运行之前记得备份原来的 grub.cfg 文件)。
干的很棒嘛!至此,全新的 LFS 系统就已经安装完成啦!我们衷心地祝愿,你亲手定制的崭新 Linux 系统能陪着你乘风破浪。
创建 /etc/lfs-release
是个好主意。该文件能帮助你(和我们,如果你需要我们帮助的话)确定你当前使用的 LFS 版本。运行以下命令以创建该文件:
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 书中的额外软件包的好时机。以下给出了一些建议:
文本模式的浏览器,例如 Lynx,可以在虚拟终端中访问这本 BLFS 书,以进行后续的编译打包工作。
GPM(GPM:一个支持控制台和 xterm 的鼠标服务) 软件包可以让你在虚拟终端中更方便的执行复制/粘贴工作。
如果静态 IP 配置不能很好的适用于你当前环境的网络配置,可以安装 dhcpcd 或者是 dhcp 的客户端部分来解决。
安装 sudo,以便在非 root 用户环境下编译软件包,且可以很轻松的在新系统中安装编译出来的软件。
为了更加便利的从网络中下载文件,请安装 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/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 书,我们十分期待本书能够为你构建系统带来一点点的帮助。
我猜,你现在一定很开心——LFS系统已经安装完成。「但是,下面该作些什么呢?」不用担心,我们早已经帮你准备好以下资源!
维护
定期检查软件的 bug 和安全公告。因为在从源码构建出 LFS 之后,你便应该养成经常去查看这些报告的好习惯。有关查询的去处,网上倒是有一些不错的资源,这里列举几个:
CERT(计算机应急响应小组)
CERT 有一个邮件列表,专门公示各种操作系统和应用程序的安全警报。订阅信息请点击此链接查看:http://www.us-cert.gov/cas/signup.html.
Bugtraq
Bugtraq 是一个专门公示计算机安全的邮件列表。它公示新发现的安全问题,偶尔还会尽可能的提出修补方案。订阅信息请点击此链接查看:http://www.securityfocus.com/archive.
Beyond Linux From Scratch
Beyond Linux From Scratch(BLFS)涵盖了比 LFS 书多得多的应用程序。BLFS 项目主页是: http://www.linuxfromscratch.org/blfs/.
LFS Hints
LFS Hints 是由 LFS 社区的志愿者提交的教育文集。有关信息访问以下网址取得: http://www.linuxfromscratch.org/hints/list.html.
邮件列表
有几个 LFS 相关的邮件列表,在你需要的时候可以订阅,也可通过它获得最新的发展动态,对项目作出力所能及的贡献等等。查看 第 1 章 - 邮件列表 可以获得更多的信息。
The Linux Documentation Project(TLDP,Linux 文档项目)
Linux 文档项目(TLDP)的目标是通过协作来完善 Linux 文档中的所有不足。TLDP 已经完成了大量的 HOWTO、指南和 man 帮助页。它的网站是:http://www.tldp.org/.
ABI |
Application Binary Interface(应用程序二进制接口) |
ALFS |
Automated Linux From Scratch (自动化 LFS) |
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 |
equation (相等) |
ext2 |
second extended file system (第二代可扩展文件系统) |
ext3 |
third extended file system (第三代可扩展文件系统) |
ext4 |
fourth extended file system (第四代可扩展文件系统) |
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 (安全哈希算法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 (虚拟终端) |
我们十分感谢下列为 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 的邮件列表里还有无数同志为本书的发布帮过忙,他们提供建议,帮忙测试,提交问题报告和指令,以及分享他们安装各种软件包的经验。
Manuel Canales Esparcia <macana AT macana-es D0T com> – 西班牙语翻译项目
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 and Patches 项目维护者
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> – 网站 Backend-Scripts 维护者
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 Gateway 维护者
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 and Patches 项目负责人
Jeremy Utley <jeremy AT linuxfromscratch D0T org> – LFS 技术作家,Bugzilla 维护者,LFS-Bootscripts 维护者
Zack Winkles <zwinkles AT gmail D0T com> – LFS 技术作家
LFS 中构建或安装的每一个包或多或少都会依赖于其它的包,甚至有些包甚至是循环依赖关系:第一个包依赖于第二个包,反过来,第二个包的生成或者是安装又需要第 一个包。因为存在这些依赖关系,所以在构建 LFS 系统的时候,软件包的编译或安装的顺序是十分重要的。本章的目的就是告知用户在构建 LFS 时每一个包的依赖项。
对于所有需要编译的软件包,我们列举出三种或者四种类型的依赖。第一种类型是「安装必选依赖」,否则在编译和安装的时候会出现问题。第二类是「测试套件依赖」,假如运行一些测试套件,除了第一类的必须安装外,第二类也需要安装。第三类是「必须之前安装」,此类需要在编译和安装之前就安装。在大多数情况下,是因为它们软件包中的脚本包含有到二进制文件的硬编码。如果不按照顺序编译它们,将会导致 /tools/bin/"binary" 路径变为真实路径写入脚本,然后安装到最终系统中去,这显然是不合适的。(译者注:这句话写的不是太清楚,大概解释一下:在真实系统中,可能会在某些脚本 中出现这样的路径信息:/usr/bin/vi,但是假如因为安装顺序的问题,这个路径就有可能变成 /tools/usr/bin/vi,最终导致脚本不能正常的运行,这显然是错误的。)
最后一类提及的「可供选择依赖」,安装方法在此文档类并没有提及,但是这些程序往往对用户来说又都是极其有用。这些软件包可能又有属于自己的「安装必选依赖」或者是「可用选择依赖」。对于这些依赖关系,推荐的做法是在学习完本书之后回到重建 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
本附录仅为方便,在此处列出了配置规则。实则,经过 第 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"
This book is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 2.0 License.
Computer instructions may be extracted from the book under the 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.