DIY:从源代码构建自定义最小 Linux 发行版
按照这份逐步指南,从源代码构建您自己的发行版,并学习它是如何安装、加载和运行的。
当使用 Linux 时,您可以轻松下载任何最常见的发行版进行安装和配置——无论是 Ubuntu、Debian、Fedora、OpenSUSE 还是完全不同的发行版。虽然您应该试用几个发行版,但构建您自己的自定义最小 Linux 发行版也是一个有益且极好的学习实践。
当我说“构建一个自定义和最小 Linux 发行版”时,我的意思是从源代码包开始——也就是说,从交叉编译工具链开始,然后构建一个目标镜像,以便安装在物理或虚拟硬盘驱动器 (HDD) 上。
所以,当我想象与 Linux 相关的终极 DIY(自己动手)指南时,那一定是这个:从源代码构建 Linux 发行版。整个过程至少需要几个小时,在一台配置不错的宿主机上。
如果您按照这个练习进行操作,您将学习构建自定义发行版需要什么,并且您还将学习该发行版是如何安装、加载和运行的。您可以在物理机或虚拟机上运行此练习。
如果我说这个过程没有部分受到出色的 Linux From Scratch (LFS) 项目的启发,那我就在撒谎了。LFS 项目被证明是我理解标准 Linux 操作系统是如何构建和运行的重要工具。使用类似的理念,我希望如果您愿意继续学习,也能将同样的智慧传授给您,读者。
术语- 宿主机: 宿主机 指的是您将在其上完成绝大部分工作的机器,包括交叉编译和目标镜像的安装。
- 目标机: 目标机 是您将从源代码包构建的最终交叉编译操作系统。它将使用宿主机上的交叉编译器构建。
- 交叉编译器: 您将构建并使用交叉编译器在宿主机上创建目标镜像。交叉编译器构建为在宿主机上运行,但它用于为与宿主机不兼容的目标架构或微处理器进行编译。
要继续本教程,您需要在宿主机上安装 GCC、make、ncurses、Perl 和 grub 工具(特别是 grub-install)。
为了构建任何东西,您还需要下载并构建交叉编译器和目标镜像的所有软件包。在本教程中,我使用以下开源软件包和版本
- binutils-2.30.tar.xz
- busybox-1.28.3.tar.bz2
- clfs-embedded-bootscripts-1.0-pre5.tar.bz2
- gcc-7.3.0.tar.xz
- glibc-2.27.tar.xz
- gmp-6.1.2.tar.bz2
- linux-4.16.3.tar.xz
- mpc-1.1.0.tar.gz
- mpfr-4.0.1.tar.xz
- zlib-1.2.11.tar.gz
在开始此过程之前,您需要配置构建环境。首先,启用 Bash 哈希函数
$ set +h
确保新创建的文件/目录仅对所有者(例如,当前登录的用户帐户)可写
$ umask 022
您将使用您的主目录作为主构建目录。(这不是必需的)。这是交叉编译工具链和目标镜像将被安装并放入 lj-os 子目录的位置。如果您希望将其安装在其他位置,请对下面的代码部分进行调整
$ export LJOS=~/lj-os
$ mkdir -pv ${LJOS}
最后,导出一些剩余的变量
$ export LC_ALL=POSIX
$ export PATH=${LJOS}/cross-tools/bin:/bin:/usr/bin
设置上述环境变量后,创建目标镜像的文件系统层次结构
$ mkdir -pv ${LJOS}/{bin,boot{,grub},dev,{etc/,}opt,home,
↪lib/{firmware,modules},lib64,mnt}
$ mkdir -pv ${LJOS}/{proc,media/{floppy,cdrom},sbin,srv,sys}
$ mkdir -pv ${LJOS}/var/{lock,log,mail,run,spool}
$ mkdir -pv ${LJOS}/var/{opt,cache,lib/{misc,locate},local}
$ install -dv -m 0750 ${LJOS}/root
$ install -dv -m 1777 ${LJOS}{/var,}/tmp
$ install -dv ${LJOS}/etc/init.d
$ mkdir -pv ${LJOS}/usr/{,local/}{bin,include,lib{,64},sbin,src}
$ mkdir -pv ${LJOS}/usr/{,local/}share/{doc,info,locale,man}
$ mkdir -pv ${LJOS}/usr/{,local/}share/{misc,terminfo,zoneinfo}
$ mkdir -pv ${LJOS}/usr/{,local/}share/man/man{1,2,3,4,5,6,7,8}
$ for dir in ${LJOS}/usr{,/local}; do
ln -sv share/{man,doc,info} ${dir}
done
此目录树基于文件系统层次结构标准 (FHS),该标准由 Linux 基金会定义和托管
为交叉编译工具链创建目录
$ install -dv ${LJOS}/cross-tools{,/bin}
使用指向 /proc/mounts 的符号链接,以便在 /etc/mtab 文件中正确维护已挂载文件系统的列表
$ ln -svf ../proc/mounts ${LJOS}/etc/mtab
然后创建 /etc/passwd 文件,列出 root 用户帐户(注意:目前,您不会设置帐户密码;您将在首次启动到目标镜像后执行此操作)
$ cat > ${LJOS}/etc/passwd << "EOF"
root::0:0:root:/root:/bin/ash
EOF
使用以下命令创建 /etc/group 文件
$ cat > ${LJOS}/etc/group << "EOF"
root:x:0:
bin:x:1:
sys:x:2:
kmem:x:3:
tty:x:4:
daemon:x:6:
disk:x:8:
dialout:x:10:
video:x:12:
utmp:x:13:
usb:x:14:
EOF
目标系统的 /etc/fstab
$ cat > ${LJOS}/etc/fstab << "EOF"
# file system mount-point type options dump fsck
# order
rootfs / auto defaults 1 1
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
devpts /dev/pts devpts gid=4,mode=620 0 0
tmpfs /dev/shm tmpfs defaults 0 0
EOF
目标系统的 /etc/profile,供 Almquist shell (ash) 在用户登录到目标机器后使用
$ cat > ${LJOS}/etc/profile << "EOF"
export PATH=/bin:/usr/bin
if [ `id -u` -eq 0 ] ; then
PATH=/bin:/sbin:/usr/bin:/usr/sbin
unset HISTFILE
fi
# Set up some environment variables.
export USER=`id -un`
export LOGNAME=$USER
export HOSTNAME=`/bin/hostname`
export HISTSIZE=1000
export HISTFILESIZE=1000
export PAGER='/bin/more '
export EDITOR='/bin/vi'
EOF
目标机器的主机名(您可以随时更改)
$ echo "ljos-test" > ${LJOS}/etc/HOSTNAME
以及 /etc/issue,它将在登录提示符处突出显示
$ cat > ${LJOS}/etc/issue<< "EOF"
Linux Journal OS 0.1a
Kernel \r on an \m
EOF
您不会在此处使用 systemd(这不是一个政治决定;这是出于方便和简单起见)。相反,您将使用 BusyBox 提供的基本 init 进程。这要求您定义一个 /etc/inittab 文件
$ cat > ${LJOS}/etc/inittab<< "EOF"
::sysinit:/etc/rc.d/startup
tty1::respawn:/sbin/getty 38400 tty1
tty2::respawn:/sbin/getty 38400 tty2
tty3::respawn:/sbin/getty 38400 tty3
tty4::respawn:/sbin/getty 38400 tty4
tty5::respawn:/sbin/getty 38400 tty5
tty6::respawn:/sbin/getty 38400 tty6
::shutdown:/etc/rc.d/shutdown
::ctrlaltdel:/sbin/reboot
EOF
同样,由于利用 BusyBox 来简化一些最常见的 Linux 系统功能,您将使用 mdev 而不是 udev,这需要您定义以下 /etc/mdev.conf 文件
$ cat > ${LJOS}/etc/mdev.conf<< "EOF"
# Devices:
# Syntax: %s %d:%d %s
# devices user:group mode
# null does already exist; therefore ownership has to
# be changed with command
null root:root 0666 @chmod 666 $MDEV
zero root:root 0666
grsec root:root 0660
full root:root 0666
random root:root 0666
urandom root:root 0444
hwrandom root:root 0660
# console does already exist; therefore ownership has to
# be changed with command
console root:tty 0600 @mkdir -pm 755 fd && cd fd && for x
↪in 0 1 2 3 ; do ln -sf /proc/self/fd/$x $x; done
kmem root:root 0640
mem root:root 0640
port root:root 0640
ptmx root:tty 0666
# ram.*
ram([0-9]*) root:disk 0660 >rd/%1
loop([0-9]+) root:disk 0660 >loop/%1
sd[a-z].* root:disk 0660 */lib/mdev/usbdisk_link
hd[a-z][0-9]* root:disk 0660 */lib/mdev/ide_links
tty root:tty 0666
tty[0-9] root:root 0600
tty[0-9][0-9] root:tty 0660
ttyO[0-9]* root:tty 0660
pty.* root:tty 0660
vcs[0-9]* root:tty 0660
vcsa[0-9]* root:tty 0660
ttyLTM[0-9] root:dialout 0660 @ln -sf $MDEV modem
ttySHSF[0-9] root:dialout 0660 @ln -sf $MDEV modem
slamr root:dialout 0660 @ln -sf $MDEV slamr0
slusb root:dialout 0660 @ln -sf $MDEV slusb0
fuse root:root 0666
# misc stuff
agpgart root:root 0660 >misc/
psaux root:root 0660 >misc/
rtc root:root 0664 >misc/
# input stuff
event[0-9]+ root:root 0640 =input/
ts[0-9] root:root 0600 =input/
# v4l stuff
vbi[0-9] root:video 0660 >v4l/
video[0-9] root:video 0660 >v4l/
# load drivers for usb devices
usbdev[0-9].[0-9] root:root 0660 */lib/mdev/usbdev
usbdev[0-9].[0-9]_.* root:root 0660
EOF
您需要为将安装在目标机器的物理或虚拟 HDD 上的 GRUB 引导加载程序创建一个 /boot/grub/grub.cfg(注意:此文件中定义的内核镜像需要反映在目标机器上构建和安装的镜像)
$ cat > ${LJOS}/boot/grub/grub.cfg<< "EOF"
set default=0
set timeout=5
set root=(hd0,1)
menuentry "Linux Journal OS 0.1a" {
linux /boot/vmlinuz-4.16.3 root=/dev/sda1 ro quiet
}
EOF
最后,初始化日志文件并赋予它们适当的权限
$ touch ${LJOS}/var/run/utmp ${LJOS}/var/log/{btmp,lastlog,wtmp}
$ chmod -v 664 ${LJOS}/var/run/utmp ${LJOS}/var/log/lastlog
构建交叉编译器
如果您还记得,交叉编译器是一个工具链,由为运行它的系统构建的各种编译工具组成,但设计用于为与您使用的系统不一定兼容的架构或微处理器进行编译。在我的环境中,我运行的是 64 位 x86 架构 (x86-64),并将交叉编译到通用的 x86-64 目标架构。当然,考虑到我正在运行的环境,本节有点多余,但本教程旨在确保您能够为 x86-64 目标构建,无论您使用的是哪种机器类型(例如,PowerPC、ARM、x86 等)。
您永远无法确定当前运行环境中设置了什么,这就是为什么您要取消设置以下 C 和 C++ 标志
$ unset CFLAGS
$ unset CXXFLAGS
接下来,定义创建交叉编译器工具链和目标镜像所需的宿主机/目标机变量的最重要部分
$ export LJOS_HOST=$(echo ${MACHTYPE} | sed "s/-[^-]*/-cross/")
$ export LJOS_TARGET=x86_64-unknown-linux-gnu
$ export LJOS_CPU=k8
$ export LJOS_ARCH=$(echo ${LJOS_TARGET} | sed -e
↪'s/-.*//' -e 's/i.86/i386/')
$ export LJOS_ENDIAN=little
内核头文件
内核的标准头文件需要为交叉编译器安装。解压缩内核 tarball 并更改到其目录。然后运行
$ make mrproper
$ make ARCH=${LJOS_ARCH} headers_check && \
make ARCH=${LJOS_ARCH} INSTALL_HDR_PATH=dest headers_install
$ cp -rv dest/include/* ${LJOS}/usr/include
Binutils
Binutils 包含链接器、汇编器和其他处理编译对象文件所需的工具。解压缩 tarball。然后创建 binutils-build 目录并更改到其中
$ mkdir binutils-build
$ cd binutils-build/
然后运行
$ ../binutils-2.30/configure --prefix=${LJOS}/cross-tools \
--target=${LJOS_TARGET} --with-sysroot=${LJOS} \
--disable-nls --enable-shared --disable-multilib
$ make configure-host && make
$ ln -sv lib ${LJOS}/cross-tools/lib64
$ make install
将以下头文件复制到目标的文件系统中
$ cp -v ../binutils-2.30/include/libiberty.h ${LJOS}/usr/include
GCC(静态)
在构建最终的交叉编译器工具链之前,您首先必须构建一个静态编译的工具链,以构建最终 GCC 交叉编译器将链接到的 C 库 (glibc)。
解压缩 GCC tarball,然后解压缩以下软件包并将它们移动到 GCC 根目录中
$ tar xjf gmp-6.1.2.tar.bz2
$ mv gmp-6.1.2 gcc-7.3.0/gmp
$ tar xJf mpfr-4.0.1.tar.xz
$ mv mpfr-4.0.1 gcc-7.3.0/mpfr
$ tar xzf mpc-1.1.0.tar.gz
$ mv mpc-1.1.0 gcc-7.3.0/mpc
现在创建一个 gcc-static 目录并更改到其中
$ mkdir gcc-static
$ cd gcc-static/
运行以下命令
$ AR=ar LDFLAGS="-Wl,-rpath,${LJOS}/cross-tools/lib" \
../gcc-7.3.0/configure --prefix=${LJOS}/cross-tools \
--build=${LJOS_HOST} --host=${LJOS_HOST} \
--target=${LJOS_TARGET} \
--with-sysroot=${LJOS}/target --disable-nls \
--disable-shared \
--with-mpfr-include=$(pwd)/../gcc-7.3.0/mpfr/src \
--with-mpfr-lib=$(pwd)/mpfr/src/.libs \
--without-headers --with-newlib --disable-decimal-float \
--disable-libgomp --disable-libmudflap --disable-libssp \
--disable-threads --enable-languages=c,c++ \
--disable-multilib --with-arch=${LJOS_CPU}
$ make all-gcc all-target-libgcc && \
make install-gcc install-target-libgcc
$ ln -vs libgcc.a `${LJOS_TARGET}-gcc -print-libgcc-file-name |
↪sed 's/libgcc/&_eh/'`
不要删除这些目录;您需要从最终版本的 GCC 返回到它们。
Glibc
解压缩 glibc tarball。然后创建 glibc-build 目录并更改到其中
$ mkdir glibc-build
$ cd glibc-build/
配置以下构建标志
$ echo "libc_cv_forced_unwind=yes" > config.cache
$ echo "libc_cv_c_cleanup=yes" >> config.cache
$ echo "libc_cv_ssp=no" >> config.cache
$ echo "libc_cv_ssp_strong=no" >> config.cache
然后运行
$ BUILD_CC="gcc" CC="${LJOS_TARGET}-gcc" \
AR="${LJOS_TARGET}-ar" \
RANLIB="${LJOS_TARGET}-ranlib" CFLAGS="-O2" \
../glibc-2.27/configure --prefix=/usr \
--host=${LJOS_TARGET} --build=${LJOS_HOST} \
--disable-profile --enable-add-ons --with-tls \
--enable-kernel=2.6.32 --with-__thread \
--with-binutils=${LJOS}/cross-tools/bin \
--with-headers=${LJOS}/usr/include \
--cache-file=config.cache
$ make && make install_root=${LJOS}/ install
GCC(最终)
正如我之前提到的,您现在将构建最终的 GCC 交叉编译器,它将链接到在上一步中构建和安装的 C 库。创建 gcc-build 目录并更改到其中
$ mkdir gcc-build
$ cd gcc-build/
然后运行
$ AR=ar LDFLAGS="-Wl,-rpath,${LJOS}/cross-tools/lib" \
../gcc-7.3.0/configure --prefix=${LJOS}/cross-tools \
--build=${LJOS_HOST} --target=${LJOS_TARGET} \
--host=${LJOS_HOST} --with-sysroot=${LJOS} \
--disable-nls --enable-shared \
--enable-languages=c,c++ --enable-c99 \
--enable-long-long \
--with-mpfr-include=$(pwd)/../gcc-7.3.0/mpfr/src \
--with-mpfr-lib=$(pwd)/mpfr/src/.libs \
--disable-multilib --with-arch=${LJOS_CPU}
$ make && make install
$ cp -v ${LJOS}/cross-tools/${LJOS_TARGET}/lib64/
↪libgcc_s.so.1 ${LJOS}/lib64
既然您已经构建了交叉编译器,您需要调整和导出以下变量
$ export CC="${LJOS_TARGET}-gcc"
$ export CXX="${LJOS_TARGET}-g++"
$ export CPP="${LJOS_TARGET}-gcc -E"
$ export AR="${LJOS_TARGET}-ar"
$ export AS="${LJOS_TARGET}-as"
$ export LD="${LJOS_TARGET}-ld"
$ export RANLIB="${LJOS_TARGET}-ranlib"
$ export READELF="${LJOS_TARGET}-readelf"
$ export STRIP="${LJOS_TARGET}-strip"
构建目标镜像
最困难的部分现在已经完成——您有了交叉编译器。现在,让我们专注于构建将安装在目标镜像上的组件。这包括各种库和实用程序,当然还有 Linux 内核本身。
BusyBox
BusyBox 是我一直以来最喜欢的开源项目之一。该项目自称为开源实用程序的瑞士军刀,这可能是对该项目最好的描述。BusyBox 将大量常用 Linux 实用程序的微型版本组合成一个单独的分布式软件包。这些工具的范围从常见的二进制文件、文本编辑器和命令行 shell 到文件系统和网络实用程序、进程管理工具等等。
解压缩 tarball 并更改到其目录。然后加载默认编译配置模板
$ make CROSS_COMPILE="${LJOS_TARGET}-" defconfig
默认配置模板将启用编译默认定义的一组实用程序和库。您可以通过运行 menuconfig
启用/禁用您认为合适的任何内容
$ make CROSS_COMPILE="${LJOS_TARGET}-" menuconfig
编译并安装软件包
$ make CROSS_COMPILE="${LJOS_TARGET}-"
$ make CROSS_COMPILE="${LJOS_TARGET}-" \
CONFIG_PREFIX="${LJOS}" install
安装以下 Perl 脚本,因为您将在下面的内核构建中需要它
$ cp -v examples/depmod.pl ${LJOS}/cross-tools/bin
$ chmod 755 ${LJOS}/cross-tools/bin/depmod.pl
Linux 内核
更改到内核软件包目录并运行以下命令以设置默认的 x86-64 配置模板
$ make ARCH=${LJOS_ARCH} \
CROSS_COMPILE=${LJOS_TARGET}- x86_64_defconfig
这将为编译过程定义一组最少的模块和设置。您很可能需要为目标机器的环境进行适当的调整。这包括启用存储和网络控制器等的模块。您可以使用 menuconfig
选项来做到这一点
$ make ARCH=${LJOS_ARCH} \
CROSS_COMPILE=${LJOS_TARGET}- menuconfig
例如,我将在 VirtualBox 虚拟机中运行此目标镜像,它将依赖于 Intel e1000 网络模块(在 defconfig 中默认)和 LSI mpt2sas 存储控制器作为操作系统驱动器。为了简单起见,这些模块配置为静态编译到内核镜像中——即设置为 *
而不是 m
。请务必检查需要什么并启用它,否则您的目标环境在启动时将无法正常运行。
编译并安装内核
$ make ARCH=${LJOS_ARCH} \
CROSS_COMPILE=${LJOS_TARGET}-
$ make ARCH=${LJOS_ARCH} \
CROSS_COMPILE=${LJOS_TARGET}- \
INSTALL_MOD_PATH=${LJOS} modules_install
您需要将一些文件复制到 /boot 目录以供 GRUB 使用
$ cp -v arch/x86/boot/bzImage ${LJOS}/boot/vmlinuz-4.16.3
$ cp -v System.map ${LJOS}/boot/System.map-4.16.3
$ cp -v .config ${LJOS}/boot/config-4.16.3
然后运行之前安装的 BusyBox 软件包提供的 Perl 脚本
$ ${LJOS}/cross-tools/bin/depmod.pl \
-F ${LJOS}/boot/System.map-4.16.3 \
-b ${LJOS}/lib/modules/4.16.3
启动脚本
Cross Linux From Scratch (CLFS) 项目(原始 LFS 项目的一个分支)提供了一组很棒的启动脚本,我在这里使用它们是为了简单起见。解压缩软件包并更改到其目录。开箱即用,软件包的 makefile 之一包含一行可能与您当前的工作 shell 不兼容。将以下更改应用于软件包的根 Makefile,以确保您在软件包安装过程中不会遇到任何问题
@@ -19,7 +19,9 @@ dist:
rm -rf "dist/clfs-embedded-bootscripts-$(VERSION)"
create-dirs:
- install -d -m ${DIRMODE}
↪${EXTDIR}/rc.d/{init.d,start,stop}
+ install -d -m ${DIRMODE} ${EXTDIR}/rc.d/init.d
+ install -d -m ${DIRMODE} ${EXTDIR}/rc.d/start
+ install -d -m ${DIRMODE} ${EXTDIR}/rc.d/stop
install-bootscripts: create-dirs
install -m ${CONFMODE} clfs/rc.d/init.d/functions
↪${EXTDIR}/rc.d/init.d/
然后运行以下命令来安装和配置目标环境
$ make DESTDIR=${LJOS}/ install-bootscripts
$ ln -sv ../rc.d/startup ${LJOS}/etc/init.d/rcS
Zlib
现在您已到达本教程的最后一个软件包。Zlib 不是必需的,但它可以作为您可能想要为您的环境安装的其他软件包的绝佳指南。如果您更愿意格式化和配置物理或虚拟 HDD,请随意跳过此步骤。
解压缩 Zlib tarball 并更改到其目录。然后配置、构建和安装软件包
$ sed -i 's/-O3/-Os/g' configure
$ ./configure --prefix=/usr --shared
$ make && make DESTDIR=${LJOS}/ install
现在,由于某些软件包可能会在 /lib 目录而不是 /lib64 目录中查找 Zlib 库,请应用以下更改
$ mv -v ${LJOS}/usr/lib/libz.so.* ${LJOS}/lib
$ ln -svf ../../lib/libz.so.1 ${LJOS}/usr/lib/libz.so
$ ln -svf ../../lib/libz.so.1 ${LJOS}/usr/lib/libz.so.1
$ ln -svf ../lib/libz.so.1 ${LJOS}/lib64/libz.so.1
安装目标镜像
所有交叉编译都已完成。现在您拥有安装整个交叉编译操作系统到物理或虚拟驱动器所需的一切,但在执行此操作之前,我们先复制一份原始目标构建目录,以免篡改它
$ cp -rf ${LJOS}/ ${LJOS}-copy
在本教程的剩余部分中使用此副本。删除一些现在不需要的目录
$ rm -rfv ${LJOS}-copy/cross-tools
$ rm -rfv ${LJOS}-copy/usr/src/*
然后删除现在不需要的静态编译库文件(如果有)
$ FILES="$(ls ${LJOS}-copy/usr/lib64/*.a)"
$ for file in $FILES; do
> rm -f $file
> done
现在从已安装的二进制文件中剥离所有调试符号。这将减小整体文件大小,并将目标镜像的整体 footprint 保持在最低限度
$ find ${LJOS}-copy/{,usr/}{bin,lib,sbin} -type f
↪-exec sudo strip --strip-debug '{}' ';'
$ find ${LJOS}-copy/{,usr/}lib64 -type f -exec sudo
↪strip --strip-debug '{}' ';'
最后,更改文件所有权并创建以下节点
$ sudo chown -R root:root ${LJOS}-copy
$ sudo chgrp 13 ${LJOS}-copy/var/run/utmp
↪${LJOS}-copy/var/log/lastlog
$ sudo mknod -m 0666 ${LJOS}-copy/dev/null c 1 3
$ sudo mknod -m 0600 ${LJOS}-copy/dev/console c 5 1
$ sudo chmod 4755 ${LJOS}-copy/bin/busybox
更改到目标副本目录以创建整个操作系统镜像的 tarball
$ cd {LJOS}-copy/
$ sudo tar cfJ ../ljos-build-21April2018.tar.xz *
请注意目标镜像如何小于 60MB。您构建了它——一个占用磁盘空间小于 60MB 的最小 Linux 操作系统
$ sudo du -h|tail -n1
58M .
而且,同一个操作系统压缩后小于 20MB
$ ls -lh ljos-build-21April2018.tar.xz
-rw-r--r-- 1 root root 18M Apr 21 15:31
↪ljos-build-21April2018.tar.xz
在本教程的剩余部分中,您将需要一个磁盘驱动器。它需要枚举为传统的块设备(在我的情况下,它是 /dev/sdd)
$ cat /proc/partitions |grep sdd
8 48 256000 sdd
该块设备需要进行分区。单个分区应该足够,您可以使用许多分区实用程序中的任何一个,包括 fdisk 或 parted。一旦创建分区并被宿主机系统检测到,请使用 ext4 文件系统格式化分区,将该分区挂载到暂存区并更改到该目录
$ sudo mkfs.ext4 /dev/sdd1
$ sudo mkdir tmp
$ sudo mount /dev/sdd1 tmp/
$ cd tmp/
将整个目标操作系统的操作系统 tarball 解压缩到暂存区的根目录
$ sudo tar xJf ../ljos-build-21April2018.tar.xz
现在运行 grub-install
以将所有必要的模块和引导记录安装到卷
$ sudo grub-install --root-directory=/home/petros/tmp/ /dev/sdd
--root-directory
参数定义了暂存目录的绝对路径,而最后一个参数是没有分区标签的块设备。
现在您正式完成了。将 HDD 安装到物理机或虚拟机(作为主磁盘驱动器)并启动电源。您将立即看到 GRUB 引导加载程序(图 1)。

图 1. GRUB 引导加载程序
在一秒钟内(是的,您没看错,一秒钟),您将到达操作系统的登录提示符(图 2)。

图 2. 用户登录提示符
您会注意到一些启动“错误”和警告消息。这是因为您缺少几个文件。随着您继续学习环境并在操作系统中构建更多软件包,您可以纠正这一点。
如果您还记得,您从未设置 root 密码。这是故意的。以 root 身份登录,您将立即进入 shell,而无需输入密码。您可以使用 BusyBox 的 passwd
命令来更改此行为,该命令应该已内置到此镜像中。

图 3. 执行一些简单任务
尽情享用吧!
后续步骤那么,这让您现在处于什么位置呢?您已经能够从开源软件包为通用的 x86-64 架构构建自定义 Linux 发行版并成功加载到其中。使用相同的交叉编译工具链,您可以使用类似的过程在操作系统中构建更多实用程序和库,例如网络实用程序、存储卷管理框架等等。
对于未来的构建,请务必保留交叉编译构建目录和您的头文件,并务必继续导出以下变量(您可能可以将它们放入脚本文件中)
set +h
umask 022
export LJOS=~/lj-os
export LC_ALL=POSIX
export PATH=${LJOS}/cross-tools/bin:/bin:/usr/bin
unset CFLAGS
unset CXXFLAGS
export LJOS_HOST=$(echo ${MACHTYPE} | sed "s/-[^-]*/-cross/")
export LJOS_TARGET=x86_64-unknown-linux-gnu
export LJOS_CPU=k8
export LJOS_ARCH=$(echo ${LJOS_TARGET} | sed -e 's/-.*//'
↪-e 's/i.86/i386/')
export LJOS_ENDIAN=little
export CC="${LJOS_TARGET}-gcc"
export CXX="${LJOS_TARGET}-g++"
export CPP="${LJOS_TARGET}-gcc -E"
export AR="${LJOS_TARGET}-ar"
export AS="${LJOS_TARGET}-as"
export LD="${LJOS_TARGET}-ld"
export RANLIB="${LJOS_TARGET}-ranlib"
export READELF="${LJOS_TARGET}-readelf"
export STRIP="${LJOS_TARGET}-strip"