lfs

0x01: Linux From Scratch

本篇blog是基于Linux From Scratch - Version 13.0-systemd的学习过程。

每次重新启动后要检查的事情:

disk是否挂载到/mnt/lfs(LFS)
运行

1
findmnt $LFS

要有相应的输出。

每次关机前要做的事情:

1
2
3
4
5
6
7
exit
mountpoint -q $LFS/dev/shm && umount $LFS/dev/shm
umount $LFS/dev/pts
umount $LFS/{sys,proc,run,dev} #卸载虚拟文件系统
sudo umount -R /mnt/lfs
sudo swapoff /dev/loop?p2
sudo losetup -d /dev/loop?

?是loop后面的编号

part1:准备工作

分区

在这一章节中我们的主要目的是给磁盘进行分区并在分区中刷入对应的文件系统。由于笔者的kubuntu没有空闲的磁盘空间,重新分区可能会出现问题,于是就在/home下搞了块60G的虚拟磁盘进行实验。

1
fallocate -l 60G ./lfs_disk.img

分配好后是进行分区,使用cfdisk命令,这个命令提供了一个简单的图形化的页面对磁盘进行分区操作。

1
cfdisk ./lfs_disk.img

当然也可以使用fdisk进行纯命令行的操作。分区后我们可以使用

1
fdisk -l ./lfs_disk.img

查看分区情况,笔者分了0.5G的EFI,4G的swap以及55.5G的/,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
myslqyr@myslqyr-ubuntu ~/c/lfs> fdisk -l ./lfs_disk.img
Disk ./lfs_disk.img: 60 GiB, 64424509440 bytes, 125829120 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: BF82A038-B3EE-4AA2-82D7-0889E36ED794

Device Start End Sectors Size Type
./lfs_disk.img1 2048 1050623 1048576 512M EFI System
./lfs_disk.img2 1050624 9439231 8388608 4G Linux swap
./lfs_disk.img3 9439232 125827071 116387840 55.5G Linux root (x86-64)

由于我们没有在物理硬盘上建立分区,所以之后是让宿主机识别到这部分。

1
sudo losetup -fP ./lfs_disk.img

这个命令将lfs_disk映射到了我电脑上的loop19。命令解释参考菜鸟教程:Linux losetup 命令用于设置循环设备。
循环设备可把文件虚拟成区块设备,籍以模拟整个文件系统,让用户得以将其视为硬盘驱动器,光驱或软驱等设备,并挂入当作目录来使用。

此操作在电脑重启后需要重新执行。之后可以使用lsblk查看磁盘情况。

1
2
3
4
5
6
> lsblk
......
loop19 7:19 0 60G 0 loop
├─loop19p1 259:6 0 512M 0 part
├─loop19p2 259:7 0 4G 0 part
└─loop19p3 259:8 0 55.5G 0 part

刷入文件系统

然后是给这些分区刷入文件系统,EFI使用FAT32,swap使用swap格式,根分区使用ext4。刷入的文件系统和分区信息在重启电脑后不会消失。

1
2
3
sudo mkfs.vfat -F 32 /dev/loop19p1
sudo mkfs.vfat -F 32 /dev/loop19p1
sudo mkfs.vfat -F 32 /dev/loop19p1

设置环境变量和umusk

我们需要执行下面两条指令。

1
2
export LFS=/mnt/lfs
umask 022

umask是设置了权限掩码,保证755或644的权限,避免出现问题。设置好后可以测试一下,如果输出结果不对就要重新输入。需要注意的是上述操作在电脑重启时会刷新,需要重新搞一下。

1
2
3
4
myslqyr@myslqyr-ubuntu ~/c/lfs> echo $LFS
/mnt/lfs
myslqyr@myslqyr-ubuntu ~/c/lfs> umask
0022

挂载

我们需要将虚拟硬盘挂在到/mnt/lfs中。

1
2
sudo mkdir -pv $LFS
sudo mount -v -t ext4 /dev/loop19p3 $LFS

挂载后,统一设置根目录权限为755防止不同发行版为mkfs使用了不同的配置。

1
2
sudo chown root:root $LFS
sudo chmod 755 $LFS

我们还要把交换空间临时增加到宿主机中,命令如下:

1
sudo /sbin/swapon -v /dev/loop19p2

在挂载这一小节中的所有操作重启电脑后都会失效,所以可以编写一个脚本统一保存这些操作,关于环境变量和umask最好是要保存到bashrc中,因为简单来说在终端中进行环境变量的配置操作不是全局的。笔者用的shell是fish,为了避免可能发生的问题换回bash了。重启电脑,在bash中运行好脚本后,可以看到正确的挂载信息。

1
2
myslqyr@myslqyr-ubuntu:~$ mount | grep $LFS
/dev/loop19p3 on /mnt/lfs type ext4 (rw,relatime)

由于我们使用的是虚拟硬盘,每次都要重新映射,所以不方便采用书中修改/etc/fstab文件在重启时自动挂载的操作。到此我们书中的第二章的内容就此完结。

part2:软件包与补丁

下载软件包并解压

在lfs官网中我们可以找到与lfs book版本对应的配套软件包,下载好后进行解压。首先在挂载点创建sources目录,并赋予只有文件所有者才能删除或重命名文件的功能,随后将解压好的文件全部复制到sources中。这一步如果是一直在root环境下做的话(sudo su)可能会由于root环境不会加载用户的bashrc而导致环境变量出现一些问题,所以每次都手打sudo。或者是在/root/.bashrc中添加指向/mnt/lfs的环境变量。

1
2
3
sudo mkdir -v $LFS/sources
sudo chmod -v a+wt $LFS/sources #a+w赋予所有人写入权限,+t只有文件所有者可以删除
sudo cp -r ./13.0/. $LFS/source

复制好后我们可以用md5来检查软件包是否可用,如果全部为OK即代表可用。

1
md5sum -c md5sums

最后是执行下面的命令。

1
sudo chown root:root $LFS/sources/*

这行命令的作用是将所有的文件的所有者和组均改为root,因为我们在构建lfs系统时,这些软件包的默认用户是我们自己,而不是root,lfs中可能没有我们用户的uid,导致异常情况,但是一定会有root用户,所以将这些在sources中的软件包的所有者和组均改为root,以便这些软件包归属于一个完全确定存在的用户。

在书中列出了所有的软件包及其版本,我们需要仔细核对一下书中的软件包、版本和自己复制到sources里的软件包、版本是否一致,笔者大概核对了一下,发现从官网下载的软件包应该是没有缺东西。至此,第三章结束。

part3:最后的准备

创建目录布局

第一步是在LFS里创建一些目录,以便将编译的程序放在最终的位置。在root下执行下述命令。

1
2
3
4
5
6
7
8
9
10
11
mkdir -pv $LFS/{etc,var} $LFS/usr/{bin,lib,sbin}  

for i in bin lib sbin;
do ln -sv usr/$i $LFS/$i #在LFS下创建usr中文件夹的符号链接
done

case $(uname -m) in
x86_64) mkdir -pv $LFS/lib64 ;; #如果电脑的架构为x86就创建$LFS/lib64
esac

mkdir -pv $LFS/tools

创建lfs用户

为了方便实验,我们可以创建一个lfs用户,在这个用户下进行后续操作。在root下执行下述命令。

1
2
3
4
5
6
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创建家目录
#lfs:新用户名

为lfs设置密码。

1
passwd lfs

授予lfs对$LFS下目录的访问权限,将这些目录的所有者指定为lfs。

1
2
3
4
5
chown -v lfs $LFS/{usr{,/*},var,etc,tools} 

case $(uname -m) in x86_64)
chown -v lfs $LFS/lib64 ;;
esac

使用下述命令切换到lfs用户。

1
su - lfs

su与su - 的区别是 su是非登陆切换,保持当前用户的环境变量、工作目录不变,PATH等环境变量不变。su - 是登陆切换,加载目标用户的完整环境,并切换到目标用户的家目录。重新设PATH、HOME等环境变量,执行目标用户的登录脚本(来自菜鸟教程)。 所以下一步的操作是配置lfs用户的环境。

配置lfs用户环境

我们要配置bash profile和bashrc两个配置文件。.bash_profile文件在用户初次登陆时读取,.bashrc文件在用户每次新建终端时读取。之后按照书中写入这两个文件的内容,禁掉原系统中的/etc/bash.bashrc,设置编译核心数,强制bash shell读取新的用户配置文件。

到这里第四章结束,我们所有的准备工作都完成了。

part4:构建交叉编译链

本章我们有三个目标:构建交叉编译链及其相关库;利用交叉编译链构建多个临时工具;进入chroot环境。

首先要明确的一个点是即使我们构建的lfs的目标架构与主机的架构相同,但是我们仍要使用交叉编译,这是为了防止在编译时依赖了宿主机的环境。我们的最终目标是构建出一个和宿主机毫无关系的系统。

以编译Binutils为例。首先要用tar命令解压。

1
tar -xvf binutils-2.46.0.tar.xz

解压好后cd进去,创建build目录,cd进去,运行configure构建makefile,要注意,例如下面的命令,在反斜杠后面不能有空格,否则会无法构建makefile。

1
2
3
4
5
6
7
8
../configure --prefix=$LFS/tools \
--with-sysroot=$LFS \
--target=$LFS_TGT \
--disable-nls \
--enable-gprofng=no \
--disable-werror \
--enable-new-dtags \
--enable-default-hash-style=gnu

之后是编译和安装。

1
2
make
make install

然后我们要删除解压完的目录。

1
2
#在sources目录下
rm -rf ./binutils-2.46.0

第五章结束后就编译好了交叉工具链。

part05:编译临时工具

按照教程来。第六章结束。

part06:进入chroot环境

在其余部分,都需要在root而非lfs用户下进行。首先收回lfs用户的权限,因为lfs用户在主机上而不是在lfs系统中,防止后来的某一个uid和lfs用户一样的用户可以随意篡改系统内容。

1
2
3
4
chown --from lfs -R root:root $LFS/{usr,var,etc,tools}
case $(uname -m) in
x86_64) chown --from lfs -R root:root $LFS/lib64 ;;
esac

创建虚拟内核文件系统

用户空间创建的程序通过存储在内存而非磁盘中的虚拟内核文件系统与内核进行通信,这一小节要把它们挂在到lfs中。要挂载的是devpts,proc,sysfs和tmpfs。

1
2
3
4
5
mount -v --bind /dev $LFS/dev
mount -vt devpts devpts -o gid=5,mode=0620 $LFS/dev/pts
mount -vt proc proc $LFS/proc
mount -vt sysfs sysfs $LFS/sys
mount -vt tmpfs tmpfs $LFS/run
1
2
3
4
5
if [ -h $LFS/dev/shm ]; then 
install -v -d -m 1777 $LFS$(realpath /dev/shm)
else
mount -vt tmpfs -o nosuid,nodev tmpfs $LFS/dev/shm
fi

进入chroot环境

1
2
3
4
5
6
7
8
chroot "$LFS" /usr/bin/env -i \
HOME=/root \
TERM="$TERM" \
PS1='(lfs chroot) \u:\w\$ ' \
PATH=/usr/bin:/usr/sbin \
MAKEFLAGS="-j$(nproc)" \
TESTSUITEFLAGS="-j$(nproc)" \
/bin/bash --login

在重启电脑后需要重新挂载/dev和虚拟文件系统。构建完成后我们需要进行一些清理工作,并离开chroot环境,对已有的临时系统进行备份。

1
2
3
4
5
6
7
8
9
rm -rf /usr/share/{info,man,doc}/*
find /usr/{lib,libexec} -name \*.la -delete
rm -rf /tools
exit #离开chroot环境
mountpoint -q $LFS/dev/shm && umount $LFS/dev/shm
umount $LFS/dev/pts
umount $LFS/{sys,proc,run,dev} #卸载虚拟文件系统
cd $LFS
tar -cJpf $HOME/lfs-temp-tools-13.0-systemd.tar.xz . #保存当前系统,时间会很长

如果后续出现了一系列问题想要恢复系统,可以执行以下命令。

1
2
3
cd $LFS
rm -rf ./*
tar -xpf $HOME/lfs-temp-tools-13.0-systemd.tar.xz

继续接下来的实验时要回到chroot环境并确保虚拟文件系统已挂载。

part07:构建系统

按照教程编译那几十个软件包。

part08:系统配置

按照lfs book配置一下就行。有一些章节写的挺迷惑的,很多配置项都不需要搞,需要根据实际情况来评估一下。

part09:启动

按照blfs的内容安装grub(为了通过uefi启动)。也可以将安装步骤发给gpt,让gpt帮助写启动脚本之类的工作,AI真强大喵。
可以使用下列启动脚本:

1
2
3
4
5
6
7
8
sudo qemu-system-x86_64 \
-machine pc \
-m 4096 \
-smp 4 \
-drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
-drive if=pflash,format=raw,file=/home/myslqyr/czh/lfs/OVMF_VARS_4M.fd \
-drive file=/home/myslqyr/czh/lfs/lfs_disk.img,format=raw,if=ide \
-boot menu=on

这时候disk不可以挂载。启动之后用户名是root,密码是在安装shadow时自己设定的。至此我们就自己搓出来了一个发行版,不过笔者还有一系列疑问没有仔细研究,后续会持续更新。


lfs
http://example.com/2026/05/01/lfs/
作者
myslqyr
发布于
2026年5月1日
许可协议