initrd和initramfs实操

RAMFS 和 RAMDISK 都是内存文件系统,他们有着小巧快速的特点。INIT前缀表示其包含有效“init”可执行文件,可以作为启动的root文件系统。INITRAMDISK出现较早,在使用nor flash和2.4kernel盛行的时期很流行。RAMFS出现也很早,但INITRAMFS是2.6版本kernel添加的新功能,INITRAMFS更象是INITRAMDISK的一个简化版本。如今嵌入式设备大量使用nand flash,像INITRAMFS和INITRAMDISK已经不适合用来管理nand这种大容量并且需要校验机制的存储设备。

在 boot loader 配置了 initrd 在这情况下,内核启动被分成了两个阶段,第一阶段先执行 initrd 文件系统中的"某个文件",完成加载驱动模块等任务,第二阶段才会执行真正的文件系统中的任务 /sbin/init 进程。

initrd

linux 发行版必须适应各种不同的硬件架构,将所有的驱动编译进内核是不现实的,根文件系统可能保存到各种存储设备上包括scsi、sata,u-disk等等,但是又必须先加载他们的驱动才能加载根文件系统。

为了解决这一矛盾,于是出现了基于ramdisk的initrd( bootloader initialized RAM disk )。Initrd是一个被压缩过的小型根目录,这个目录中包含了启动阶段中必须的驱动模块,可执行文件和启动脚本。当系统启动的时候,bootloader会把initrd文件读到内存中,然后把initrd文件在内存中的起始地址和大小传递给内核。内核在启动初始化过程中会解压缩initrd文件,然后将解压后的initrd挂载为根目录,然后执行根目录中的/linuxrc脚本。

initrd 的英文含义是 boot loader iniTIalized RAM disk,就是由 boot loader 初始化的内存盘。在 linux内核启动前, boot loader 会将存储介质中的 initrd 文件加载到内存,内核启动时会在访问真正的文件系统前先访问该内存中的 initrd 文件系统。

initramfs

在linux2.5中出现了initramfs,它的作用和initrd类似,只是和内核编译成一个文件(该initramfs是经过gzip压缩后的cpio格式的数据文件),该cpio格式的文件被链接进了内核中特殊的数据段.init.ramfs上,其中全局变量__initramfs_start__initramfs_end分别指向这个数据段的起始地址和结束地址。内核启动时会对.init.ramfs段中的数据进行解压,然后使用它作为临时的根文件系统。

实操

initrd

Linux内核配置:

选中RAM disk支持,注意一定要变成星号,并且将大小调整为64M:

Device Drivers > Block devices
	  <*>   RAM block device support
        (16)    Default number of RAM disks (NEW)
        (65536) Default RAM disk size (kbytes)

initrd制作脚本:

#!/bin/bash

MOUNT_DIR=mnt
CURR_DIR=`pwd`

rm initrd.ext4
dd if=/dev/zero of=initrd.ext4 bs=1M count=64
mkfs.ext4 initrd.ext4

mkdir -p $MOUNT_DIR
mount initrd.ext4 $MOUNT_DIR
cp -arf busybox-1.35.0/_install/* $MOUNT_DIR

cd $MOUNT_DIR
mkdir -p etc dev mnt proc sys tmp mnt etc/init.d/

echo "proc /proc proc defaults 0 0" > etc/fstab
echo "tmpfs /tmp tmpfs defaults 0 0" >> etc/fstab
echo "sysfs /sys sysfs defaults 0 0" >> etc/fstab

echo "#!/bin/sh" > etc/init.d/rcS
echo "mount -a" >> etc/init.d/rcS
echo "mount -o remount,rw /" >> etc/init.d/rcS
echo "echo -e \"Welcome to ARM64 Linux\"" >> etc/init.d/rcS
chmod 755 etc/init.d/rcS

echo "::sysinit:/etc/init.d/rcS" > etc/inittab
echo "::respawn:-/bin/sh" >> etc/inittab
echo "::askfirst:-/bin/sh" >> etc/inittab
chmod 755 etc/inittab

cd dev
mknod console c 5 1
mknod null c 1 3
mknod tty1 c 4 1

cd $CURR_DIR
umount $MOUNT_DIR
echo "make initrd ok!"

制作完成后产生文件

-rw-r--r--  1 root    root     64M  6月 13 19:27 initrd.ext4

QEMU配置:

#!/bin/bash

source/qemu-7.2.0-rc1/build/aarch64-softmmu/qemu-system-aarch64 \
    -nographic \
    -M virt \
    -cpu cortex-a57 \
    -smp 2 \
    -m 1G \
    -kernel source/linux-6.0.9/build/arch/arm64/boot/Image \
    -append "nokaslr root=/dev/ram init=/linuxrc console=ttyAMA0 console=ttyS0" \
    -initrd source/initrd.ext4

启动即可看到kernel加载initrd的过程。

我们再执行一个操作gzip initrd.ext4,然后QEMU配置也改成加载-initrd source/initrd.ext4.gz,也可以支持gzip格式的

initramfs

制作initramfs文件系统

Linux内核只认cpio格式的initramfs文件包(因为unpack_to_rootfs只能解析cpio格式文件),非cpio格式的 initramfs文件包将被系统抛弃,而initrd可以是cpio包也可以是传统的镜像(image)文件,实际使用中initrd都是传统镜像文件。

initramfs可以独立ram disk单独存在,而要支持initrd必须要先支持ram disk

和initrd不同,第一个运行的是init文件,这个文件可以自己创建,也可以直接使用busybox

#!/bin/bash

MOUNT_DIR=initramfs_root
CURR_DIR=`pwd`

rm -rf $MOUNT_DIR initramfs.img.gz

mkdir -p $MOUNT_DIR
cp -arf busybox-1.35.0/_install/* $MOUNT_DIR

cd $MOUNT_DIR
mkdir -p etc dev mnt proc sys tmp mnt etc/init.d/

echo "proc /proc proc defaults 0 0" > etc/fstab
echo "tmpfs /tmp tmpfs defaults 0 0" >> etc/fstab
echo "sysfs /sys sysfs defaults 0 0" >> etc/fstab

echo "#!/bin/sh" > etc/init.d/rcS
echo "mount -a" >> etc/init.d/rcS
echo "mount -o remount,rw /" >> etc/init.d/rcS
echo "echo -e \"Welcome to ARM64 Linux\"" >> etc/init.d/rcS
chmod 755 etc/init.d/rcS

echo "::sysinit:/etc/init.d/rcS" > etc/inittab
echo "::respawn:-/bin/sh" >> etc/inittab
echo "::askfirst:-/bin/sh" >> etc/inittab
chmod 755 etc/inittab

ln -s bin/busybox init

cd dev
mknod console c 5 1
mknod null c 1 3
mknod tty1 c 4 1

cd $CURR_DIR
find $MOUNT_DIR/ | cpio -c -o > initramfs.img
gzip initramfs.img
echo "make initramfs ok!"

由于initramfs使用cpio包格式,所以很容易将一个单一的文件、目录、node编译链接到系统中去,这样很简单的系统中使用起来很方便,不需要另外挂接文件系统。
但是因为cpio包实际是文件、目录、节点的描述语言包,为了描述一个文件、目录、节点,要增加很多额外的描述文字开销,特别是对于目录和节点,本身很小额外添加的描述文字却很多,这样使得cpio包比相应的image文件大很多。

Linux内核配置:

General setup
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support         
  (../source/initramfs_root) Initramfs source file(s)

路径输入上述文件系统根目录,这样内核编译时就会和内核编译成同一个文件。

我们使用cpio工具单独打包出来,可以观察跟文件系统的大小,如果不在内核配置这个选项,也可以使用cpio生成的镜像和内核产物再次打包,有利于路径解耦。

QEMU配置:

#!/bin/bash

source/qemu-7.2.0-rc1/build/aarch64-softmmu/qemu-system-aarch64 \
    -nographic \
    -M virt \
    -cpu cortex-a57 \
    -smp 2 \
    -m 1G \
    -kernel source/linux-6.0.9/build/arch/arm64/boot/Image \
    -append "nokaslr root=/dev/ram init=/linuxrc console=ttyAMA0 console=ttyS0" \

可以看到不需要再制定initrd了,内核和initramfs绑定在一起有利于分区管理

总结

本篇介绍了initrd和initramfs的使用方法和差异点,其实他们的差异点远不止这些,如果写入真实flash还有启动命令等的差异。

后面可以分析下源码是如何区别的,启动流程的差异

posted @ 2024-06-14 16:48  小满的博客  阅读(169)  评论(0编辑  收藏  举报