本文目录
1.下载Linux内核源码
首先需要下载Linux
内核源码。
wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.19.3.tar.gz
下载Busybox
源码,BusyBox 是一个集成多种常用 Linux 工具的软件集合,它将许多标准的 Unix 工具
(如 ls、cp、mv、sh 等)集成到一个小型的可执行文件中。通常用于嵌入式系统或需要轻量级环境的场景,例如在构建最小化的 Linux 根文件系统时。
在嵌入式设备或轻量级环境中,BusyBox 可以替代完整的 GNU 工具链
,节省空间。
wget https://busybox.net/downloads/busybox-1.37.0.tar.bz2
2.编译内核
首先创建一个目录,用来放编译的相关文件。
mkdir kernel
cd kernel
然后进行解压。
tar -zxvf linux-4.19.3.tar.gz
cd linux-4.19.3.tar.gz
配置硬件架构
配置硬件架构,最终是使用QEMU
来运行,因此直接将ARCH设置为x86,减少交叉编译所需要的时间。(如果目标架构与编译机器的架构相同(例如,都是 x86 或 x86_64),则不需要进行交叉编译。交叉编译是指在一个架构上编译出适用于另一个架构的代码,通常需要额外的工具链(如交叉编译器)。)
export ARCH=x86
配置config
配置board config
,同样是配置为x86架构的。make x86_64_defconfig
是一个非常重要的命令,它的作用是为特定的硬件架构生成默认的内核配置文件(.config)
。
生成的 .config 文件
是一个文本文件,包含了内核编译
所需的配置选项。它定义了哪些内核功能、模块和驱动程序将被编译。例如:是否启用特定的文件系统支持(如 ext4、NTFS)
、是否编译特定的硬件驱动程序(如网络驱动、GPU 驱动)
。
make x86_64_defconfig
配置Linux内核
提供了一个基于文本菜单的交互式界面,允许用户自定义内核的编译选项。
make menuconfig
的核心作用是让用户能够自定义 Linux 内核的配置。它通过一个图形化的菜单界面(基于 ncurses 库),让用户可以轻松地启用、禁用或修改内核的功能、模块和驱动程序的编译选项。
make menuconfig
其中具体需要配置的选项如下所示。
支持ramdisk驱动
,选中下面的配置,启用内核对初始 RAM 磁盘(Initial RAM Disk,简称 initrd 或 ramdisk)的支持。ramdisk 是一种基于内存的虚拟块设备
,它将内存的一部分分配出来,模拟成一个磁盘设备
,用于存储临时文件系统或其他数据。
ramdisk在系统启动时,ramdisk 可以被用作初始根文件系统
(initramfs
)。它允许系统在启动过程中加载必要的驱动程序和工具
,以便完成后续的启动过程。
这里有几个参数需要设置一下:
Kernel log buffer size
:设置内核日志缓冲区的大小,影响内核可以存储的日志信息量。选项中的数字(16 => 64KB, 17 => 128KB)表示不同的日志缓冲区大小配置。
CPU kernel log buffer size contribution
:为每个 CPU 设置内核日志缓冲区的贡献大小,影响每个 CPU 可以存储的日志信息量。
Temporary per-CPU NMI log buffer size
:设置每个 CPU 的临时不可屏蔽中断(NMI)日志缓冲区大小。
Control Group support
:启用控制组(cgroups)支持,允许对系统资源进行细粒度控制和管理。
Namespaces support
:启用命名空间支持,提供进程隔离的机制,如 PID、网络、IPC 等命名空间。
Kernel->user space relay support (formerly relayfs)
:提供内核到用户空间的中继支持,允许用户空间程序与内核空间进行更高效的通信。
Initial RAM filesystem and RAM disk (initramfs/initrd) support
:启用初始 RAM 文件系统和 RAM 磁盘(initramfs/initrd)的支持,允许系统在启动时从内存中的文件系统加载必要的驱动和工具。
配置设备驱动
,特别是块设备(Block devices)的驱动。块设备通常指的是存储设备,如硬盘、固态硬盘、USB 存储设备等,它们能够以块为单位存储和检索数据。
开始编译内核
通过命令指定核心数,一般根据虚拟机的大小进行设置。N代表具体核心数。
make -j4
然后等待编译即可。
此时内核镜像的位置为:linux-4.9.299/arch/x86/boot/bzImage
3.编译busybox
cd kernel
tar -jxvf busybox-1.37.0.tar.bz2
cd busybox-1.37.0/
busybox比较简单,只用选一个,支持静态编译就行。BusyBox 可以编译为静态二进制文件,不依赖外部库,适合在没有动态库支持的环境中运行。
make menuconfig
make -j4
make install
编译并安装完成busybox后,会在busybox目录下多一个_install
的目录,busybox就已经编译完成。
4.创建rootfs根文件系统
补充目录结构
接下来通过busybox启动一个Linux系统,也就是需要一个根文件系统,也就是我们需要对busybox生成的目录进行补充。
进入到busybox的目录
cd _install
mkdir -p etc/init.d mnt tmp sys dev proc
这个命令使用 mkdir 创建一系列目录,-p 选项确保即使上级目录不存在时,命令也能正确执行,自动创建所有必要的上级目录。
etc/init.d
:这个目录通常用于存放系统启动时需要执行的初始化脚本。
mnt
:挂载点目录,用于挂载文件系统,如 USB 设备、网络文件系统等。
tmp
:临时文件目录,用于存放系统运行过程中产生的临时文件。
sys
:系统文件目录,用于存放与内核交互的文件,如设备文件、驱动信息等。
dev
:设备文件目录,用于存放设备节点,如 /dev/ttyS0(串口设备)。
proc
:进程文件系统目录,用于存放虚拟文件系统 proc 的挂载点,proc 文件系统提供了一种机制来获取内核和进程信息。
创建文件系统表fstab
/etc/fstab
文件是 Linux 系统中的一个配置文件,用于存储文件系统的静态信息,包括挂载点、文件系统类型、挂载选项等。
vim etc/fstab
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
创建启动脚本rcS
vim etc/init.d/rcS
echo -e "Welcome to tinyLinux"
/bin/mount -a
mount -o remount,rw /
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
/bin/mount -a
命令会挂载 /etc/fstab 文件中定义的所有文件系统,-a 选项表示挂载所有文件系统。
然后,脚本使用 mount -o remount,rw /
命令重新挂载根文件系统(/)为读写模式
。这通常是在系统启动时将只读的根文件系统切换为读写模式,以便系统可以对文件系统进行修改。
mkdir -p /dev/pts
创建 /dev/pts
目录,该目录用于存放伪终端设备文件。
紧接着,脚本通过 mount -t devpts devpts /dev/pts
命令挂载 devpts 文件系统
到 /dev/pts 目录
。devpts 是一个特殊的文件系统,用于支持伪终端(pseudo-terminals)
,它允许用户通过终端模拟器与系统进行交互。
然后,echo /sbin/mdev > /proc/sys/kernel/hotplug
命令将 /sbin/mdev
写入 /proc/sys/kernel/hotplug
文件,这通常是用来指定 udev 或 mdev 这类设备管理器的启动脚本。
mdev -s
命令启动 mdev 设备管理器,它会扫描 /dev
目录并根据需要创建设备文件。mdev 是一个轻量级的设备管理器,常用于嵌入式系统或资源受限的环境。
通过这些步骤,rcS 脚本确保了系统启动时文件系统被正确挂载
,设备节点被创建,并且设备管理器正常运行,从而为系统的后续操作提供了基础。
chmod 755 rcS
添加权限。
创建初始化程序配置文件inittab
该文件用于定义系统启动时的初始化进程和行为。inittab
文件是 System V init 系统的配置文件,用于告诉 init 进程(系统的第一个进程,PID 为 1)在系统启动、运行和关闭时应该执行哪些任务。
vim etc/inittab
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
::sysinit:
是一个运行级别(runlevel)
,sysinit 表示系统初始化阶段。/etc/init.d/rcS
是在系统初始化阶段需要执行的脚本文件。这个脚本就是上面那个,通常包含启动系统所需的各种服务和任务。
::respawn:
表示如果进程退出,init 将自动重启该进程。/bin/sh
是需要重启的 shell 进程。这确保了 shell 始终运行,即使它意外退出。
::askfirst:
表示在执行指定的命令前,init 会提示用户确认
。/bin/sh 是需要用户确认的命令。这可以用于在执行某些关键操作前确保用户同意。
::ctrlaltdel
: 表示当用户按下 Ctrl+Alt+Del 组合键时触发的动作。/bin/umount -a -r
是要执行的命令,用于卸载所有文件系统并重新启动系统。-a 选项表示卸载所有文件系统,-r 选项表示重新启动。
chmod 755 inittab
给文件添加可执行权限。
创建设备文件节点
tty
设备通常用于表示终端设备,tty1 表示第一个伪终端。
null
设备通常用于丢弃所有写入其中的数据,而从该设备读取则会返回 EOF(文件结束标志)。
在 dev 目录下创建了三个设备文件:console、null 和 tty1
。这些设备文件分别用于访问系统控制台、提供一个数据黑洞以及访问第一个伪终端。这些设备文件是用户空间程序与系统内核交互的重要接口,使得用户可以通过这些文件与系统进行通信。
cd dev
mknod console c 5 1
mknod null c 1 3
mknod tty1 c 4 1
5.镜像制作
在kernel目录下执行操作:
# 先创建一个空的镜像并格式化为ext4
dd if=/dev/zero of=./rootfs.ext4 bs=1M count=32
mkfs.ext4 rootfs.ext4
# 创建一个空目录fs
mkdir fs
# 挂载到fs,并复制启动文件
mount -o loop rootfs.ext4 ./fs
cp -rf ./_install/* ./fs
#卸载镜像文件
umount ./fs
# 使用gzip压缩
gzip --best -c rootfs.ext4 > rootfs.img.gz
dd if=/dev/zero of=./rootfs.ext4 bs=1M count=32
mkfs.ext4 rootfs.ext4
dd if=/dev/zero of=./rootfs.ext4 bs=1M count=32
:这个命令创建一个大小为 32MB 的空文件(镜像),名为 rootfs.ext4
。dd 命令从 /dev/zero
读取数据,以 1MB 的块大小写入 rootfs.ext4
文件,总共写入 32 次。
mkfs.ext4 rootfs.ext4
:这个命令将 rootfs.ext4
文件格式化为 ext4 文件系统
,使其可以被 Linux 系统识别和使用。
mkdir fs
创建一个名为 fs 的空目录,用于挂载镜像文件。
mount -o loop rootfs.ext4 ./fs
:这个命令将 rootfs.ext4
镜像文件挂载到 fs 目录。
cp -rf ./_install/* ./fs
:这个命令将 _install
目录下的所有文件递归地复制到挂载的 fs 目录中。这些文件通常包括 BusyBox 工具、系统启动脚本
等。
umount ./fs
卸载 fs 目录,即从文件系统中卸载 rootfs.ext4 镜像文件
。这是在对镜像文件进行修改后必须执行的步骤,以确保所有更改都已保存。
gzip --best -c rootfs.ext4 > rootfs.img.gz
使用 gzip
对 rootfs.ext4 镜像文件
进行压缩,并将压缩后的数据输出到 rootfs.img.gz 文件中
。–best 选项指定使用最高的压缩级别,-c 选项指定输出到标准输出,然后通过重定向 > 将其写入文件。
这里我们来说明一下挂载和镜像文件的关系。
挂载的定义:挂载是一种将存储设备(包括镜像文件)连接到正在运行的操作系统的方法,使其可以像本地存储设备一样被访问和使用。挂载过程涉及将文件系统的根目录指定为系统中的一个目录(称为挂载点),这样文件系统中的文件和目录就可以通过这个挂载点访问了。
挂载的过程可以这样理解:
创建挂载点:首先,在操作系统中创建一个空目录,这个目录将作为文件系统的访问入口。
执行挂载命令:使用挂载命令(如 mount)将镜像文件或存储设备挂载到创建的挂载点上。
访问文件系统:一旦挂载成功,就可以通过挂载点访问文件系统中的文件和目录,就像访问本地文件系统一样。
镜像文件包含了一个文件系统,当镜像文件被挂载时,它所包含的文件系统就变得可访问。挂载过程使得镜像文件中的数据可以被操作系统读取和写入,就像它是一个本地存储设备一样。
可以这么理解在卸载之后,您不再能通过该挂载点访问镜像文件中的文件系统,但镜像文件本身仍然存在,并且包含了您在挂载期间所做的所有更改。
6.利用qemu运行内核和文件系统
在kernel目录下创建一个目录,然后拷贝bzImage。
mkdir -p img/4.9
cp linux-4.9.299/arch/x86/boot/bzImage img/4.9/
运行命令就可以开始运行内核了。
qemu-system-x86_64 -kernel img/4.9/bzImage -initrd rootfs.img.gz -append "root=/dev/ram init=/linuxrc" -serial file:output.txt