0%

LFS-从零开始的Linux生活(上)

前言

域名买了一亿年了,结果还是只有Hello World,实在看不下去了,总之写点东西吧。

既然是博客的第一篇文章,当然是开天辟地的(X),所以就来写一下LFS吧。 本文参考Linux From Scratch 简体中文版写成,在此特别感谢作者Gerard Beekmans以及Linux中国 LFS 翻译小组成员。

本文采用知识共享署名-非商业性使用-相同方式共享 2.0 通用许可协议授权。

Linux® 为 Linus Torvalds 注册商标。

LFS简介

LFS 就是 Linux From Scratch(Linux 从头开始) 从零开始的Linux生活 ,这个From Scratch 就是和Docker的 FROM scratch 是一样的。

简单说就是自己从源码开始完整的构建一个Linux。

准备

准备宿主

自己构建一个Linux,首先你需要一个Linux环境(……),这里我们选择ArchLinux的LiveCD作为我们的宿主环境。

在VMware里新建一个虚拟机,分配上足够的CPU,因为后面会有无尽的编译。

在选择引导的时候,按tab键修改启动参数,在最后加上cow_spacesize=1G来确保在Live环境中有足够的空间来更新包与安装其他必备包。

Arch-linux-boot-menu.png
Arch-linux-boot-menu.png

启动到ArchLinux的LiveCD后,先启动sshd服务,这样我们就可以通过ssh安装,而不用手动去敲后面那些冗长的命令了。再修改一下root密码,方便登陆。

1
2
systemctl start sshd
passwd

之后我们就可以通过ssh连上宿主了。

检查宿主环境

执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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`
sed --version | head -n1
tar --version | head -n1
makeinfo --version | head -n1
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

得到输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bash, version 5.0.0(1)-release
/bin/sh -> /usr/bin/bash
Binutils: version-check.sh: line 10: ld: command not found
version-check.sh: line 11: bison: command not found
yacc not found
bzip2, Version 1.0.6, 6-Sept-2010.
Coreutils: 8.30
diff (GNU diffutils) 3.7
find (GNU findutils) 4.6.0
GNU Awk 4.2.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.1.2)
/usr/bin/awk -> /usr/bin/gawk
version-check.sh: line 35: gcc: command not found
version-check.sh: line 36: g++: command not found
(GNU libc) 2.28
grep (GNU grep) 3.3
gzip 1.10
Linux version 4.20.13-arch1-1-ARCH ([email protected]) (gcc version 8.2.1 20181127 (GCC)) #1 SMP PREEMPT Wed Feb 27 19:10:28 UTC 2019
version-check.sh: line 41: m4: command not found
version-check.sh: line 42: make: command not found
version-check.sh: line 43: patch: command not found
Perl version='5.28.1';
sed (GNU sed) 4.7
tar (GNU tar) 1.32
texi2any (GNU texinfo) 6.5
xz (XZ Utils) 5.2.4
version-check.sh: line 50: g++: command not found
g++ compilation failed

看来环境差的有点多,慢慢配吧。

首先更换pacman源,这里我选择了中科大源

更新之后安装缺少的环境。

pacman -S binutils bison gcc make patch

再次执行version-check.sh可以发现不缺环境了。

分区

由于这只是实验性的构建系统,我们就将磁盘分为一个区,具体操作参见fdisk

注意:新建虚拟机的时候采用了UEFI请再划分一个efi分区(或叫boot)

格式化

毫无疑问,我们采用ext4分区。

mkfs.ext4 /dev/sda1

注意:请根据实际情况将sda1替换为实际的分区。

设置$LFS变量

在整个构建LFS的过程中,我们会多次用到环境变量 LFS ,它应该被设置为你将要构建的 LFS 系统的目录名,这里我们使用 /mnt/lfs

export LFS=/mnt/lfs

小心

不论何时,当你离开又重新进入这个工作环境时都不要忘了检查 LFS 是否设置(比如当你使用 su 切换到 root 或是另一个用户时)。使用如下命令检查 LFS 变量是否正确设置:

echo $LFS

请确保输出的是你构建 LFS 的那个目录的路径,如果你是按照例子设置的,那就是 /mnt/lfs。如果你输出的路径不正确,请使用前一部分提供的命令把$LFS重新设置到正确的目录。

挂载分区

执行以下命令来挂载分区

1
2
mkdir -pv $LFS
mount /dev/sda1 $LFS

准备软件包和补丁

在构建完整的Linux之前,我们需要一个基础环境。

在下载之前,为了方便和整洁,我们新建一个工作用的目录。

root权限执行,创建这个目录,并设置目录的写权限和粘滞模式。「粘滞模式」是指,即便多个用户对某个目录有写权限,但仅有文件的所有者,能在粘滞目录中删除该文件。

1
2
mkdir -v $LFS/sources
chmod -v a+wt $LFS/sources

我们可以用wget-list输入wget来快捷的下载所需的软件包和补丁。

1
2
wget https://lctt.github.io/LFS-BOOK/lfs-systemd/wget-list
wget --input-file=wget-list --continue --directory-prefix=$LFS/sources

最后的准备

添加临时工作目录

现在我们还有最后一点需要做的准备,现在我们构建的工具都是临时的,我们需要将其放在临时的目录里,方便用完后删除。

以root用户执行

1
2
mkdir -v $LFS/tools
ln -sv $LFS/tools /

如果你对第二条命令感到疑惑,请查阅ln(1).

TL;NR:

第二命令是创建了/tools指向$LFS/tools的符号链接

### 添加LFS用户

以root用户的身份构建系统可能会造成毁灭性的后果,所以我们添加一个非特特权用户来进行工作。

1
2
3
4
5
6
groupadd lfs
useradd -s /bin/bash -g lfs -m -k /dev/null lfs
passwd lfs
chown -v lfs $LFS/tools
chown -v lfs $LFS/sources
su - lfs

useradd-k选项防止了复制一些奇怪的东西到用户目录

设置环境

我们现在需要为新用户配置合适的环境来确保构建的顺利。

lfs用户身份运行一下命令创建.bash_profile文件:

1
2
3
cat > ~/.bash_profile << "EOF"
exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash
EOF

这一串命令确保了登陆lfs用户时环境变量的纯净,防止被外部环境变量污染导致错误。

lfs用户身份运行一下命令创建.bashrc文件:

1
2
3
4
5
6
7
8
9
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

这里关闭了bash的哈希功能,确保每次执行的命令都会从PATH里依次寻找,保证能使用最新编译的工具。

设置掩码(umask)为022,以确保新建的文件和目录只有其所有者可写,但任何人都可读可执行。

LFS 变量应设置成选定的挂载点。

LC_ALL 变量控制某些程序的本地化,使它们的消息遵循特定国家的惯例。设置 LC_ALL 为「POSIX」或「C」(两者是等价的),确保在 chroot 环境中一切能如期望的那样进行。

LFS_TGT 变量设置了一个虽非默认,但在构建交叉编译器、连接器和交叉编译临时工作链时,用得上到的兼容的机器说明。参考Section 5.2, “工具链技术说明”获得更多信息。

最后,执行以下命令来完成我们的准备工作吧。

source ~/.bash_profile

构建临时系统

说明

在开始构建之前请务必再进行以下检查来确保环境正确: 0. 执行echo $LFS确保环境变量正确 1. 使用的shell是bash 2. sh是到bash的符号链接 3. /usr/bin/awk 是到 gawk 的符号链接。 4. /usr/bin/yacc 是到 bison 的符号链接,或者是一个执行bison 的小脚本。

构建过程

  1. 把所有源文件和补丁放到 chroot 环境可访问的目录,例如 /mnt/lfs/sources/。但是千万不能把源文件放在 /mnt/lfs/tools/ 中。
  2. 进入到源文件目录。
  3. 对于每个软件包:
    1. tar 程序解压要编译的软件包。构建临时系统时,确保解压软件包时你使用的是 lfs 用户。
    2. 进入到解压后创建的目录中。
    3. 根据指南说明编译软件包。
    4. 回退到源文件目录。
    5. 除非特别说明,删除解压出来的目录。

构建

由于需要构建的包过于多,详细信息请从这里开始阅读,并按照说明执行,若有重要信息我会在本文里说明。

Binutils-2.31.1 - 第 1 遍

构建Binutils的时间将会作为编译用时参考,称为SBU

build文件夹下的测试命令应为

time { ../configure --prefix=/tools --with-sysroot=$LFS --with-lib-path=/tools/lib --target=$LFS_TGT --disable-nls --disable-werror&&make -j12 &&make install;}

其中j12的多核编译参数请根据自己的cpu调整

也可添加环境变量来自动设置

export MAKEFLAGS='-j12'

Tar-1.31

如果硬盘空间不够,请不要执行这个测试,血的教训啊

尾声

现在我们已经把临时系统构建好了,可以开始摸鱼了休息一下,过上若干天后,再继续或者不继续这个漫长的LFS之旅