Linux文件系统映像:Initranfs 和 Initrd

initramfs 和 initrd 介绍

  在/boot目录下,一般都有一个/boot/initrd.img文件或一个/boot/initramfs.img文件。

  现在有两个问题。第一个问题就是为什么要有 initrd 或者 initramfs?答案是,为了减小 Linux 内核(Kernel)的大小。Linux内核在初始化之后会执行init进程,而init进程会挂载根文件系统,但由于init程序也是在根文件系统上的,所以这就有了悖论(有关Linux开机过程可以看本系列第一篇)。Linux采用两步走的方法来解决这个问题。Linux2.6版以前的方法是:除了内核vmlinuz之外还有一个独立的initrd.img映像文件,其实它就是一个文件系统映像,linux内核在初始化后会mount initrd.img作为一个临时的根文件系统,而init进程就是在initrd.img里的,然后init进程会挂载真正的根文件系统,然后umount initrd.img。但Linux2.6内核的实现方式却不太一样,虽然完成的功能是一样的。Linux2.6采用initramfs。initramfs:init ram filesystem,它是一个cpio格式的内存文件系统。

  第二个问题就是 initrd 文件和 initramfs 文件是什么格式的,怎么创建和打开。其实在 Linux 的历史中,内存中的文件系统使用的技术还不一样。在很老的系统中,是将某一块内存模拟成磁盘,称之为 RamDisk,这个技术效率不高,因为将内存模拟成磁盘后,依然需要像对待磁盘一样对待它,需要给它创建特定格式的文件系统,读取或写入文件的时候还是要用到内核的缓存系统,这样同一份数据就在内存中存在了两份(套娃),所以很浪费。老系统中使用的 initrd 文件其实就相当于是一个磁盘的镜像,所以要读取或写入它的内容,就需要将它挂在到系统中,然后进行读写。就如上一条提到的,在Linux2.6上,initrd 已经被 initramfs 取代了,虽然很多系统中仍然用initrd.img作为文件名,其实使用的是 initramfs 技术。initramfs 技术不使用 RamDisk,而是使用 tempfs,也就是复用了内核中的缓存系统,所以 tempfs 中的内容在内存中只存在一份,那就是在内核的缓存中,不仅节约了内存开销,也提高了效率。

  在GRUB把控制权交给内核后,通常的步骤是先启动内核,再内核挂载initrd.img,执行initrd.img中的init进程挂在真正的根文件系统,然后执行其中的/sbin/init。如果没有initrd.img,计算机的启动时第一个进程(/sbin/init)都启动不起来。

打开观察 initramfs 文件

  initramfs文件在系统中以经过压缩的CPIO格式保存,使用 cat initramfs.img | cpio -imd 打开后文件夹结构如下:whichrpm

image-20210919183239893

image-20210919183437850

  发现只有少许的几个文件,但是解压后的文件大小比原文件的要小得多,所以猜测并没有完全解压出来。

  网上的文章全都是先用 cp 把.img拷贝并加一个 .gz 后缀,然后用 gunzip 解压这个文件,然后用 cpio -imd 来解压 .img 文件,但是这样解压结果和我上面的一样,都不是完全解压!!!

  我尝试使用which mkinitramfs 发现ubuntu有这个 mkinitramfs 指令,所以它的内核文件应该就是用这个指令创建的,所以尝试用 unmkinitramfs 来解压这个.img文件,但是发现ubuntu没有这个指令。。。。

  试图在网上找办法,在一篇文章中看见了类似的问题,网友的 Fedora 21系统采用了最新的 Early User Space 技术导致cpio无法完全解压出文件,所以他使用dracut 软件包中提供的lsinitrd工具可以查看 initramfs 中的内容,发现它需要用到一个skipcpio程序,跳过 initramfs 文件的头部后,再将剩下的部分当成压缩的 CPIO 文件进行解包。最后使用 sudo /usr/lib/dracut/skipcpio initramfs.img | zcat | cpio -imd 指令完成解压。但是我是ubuntu16.04,也没有这个指令。。。。

  最后无奈发现是ubuntu版本的锅,我从16.04升级到18.04后就有 unmkinitramfs 指令了!

  解压后的完整文件夹结构如下:

initrdramfs/
├── bin
├── conf
│   └── conf.d
├── etc
│   ├── console-setup
│   ├── default
│   ├── dhcp
│   │   └── dhclient-enter-hooks.d
│   ├── fonts
│   │   └── conf.d
│   ├── ld.so.conf.d
│   ├── lvm
│   ├── modprobe.d
│   ├── plymouth
│   └── udev
├── lib
│   ├── brltty
│   ├── modprobe.d
│   ├── modules
│   │   └── initramfs.img-0.1
│   ├── systemd
│   │   └── network
│   ├── udev
│   │   └── rules.d
│   └── x86_64-linux-gnu
├── lib64
├── run
├── sbin
├── scripts
│   ├── init-bottom
│   ├── init-premount
│   ├── init-top
│   ├── local-block
│   ├── local-bottom
│   ├── local-premount
│   ├── local-top
│   └── panic
├── usr
│   ├── lib
│   │   └── x86_64-linux-gnu
│   │       └── plymouth
│   │           └── renderers
│   └── share
│       ├── fonts
│       │   └── truetype
│       │       └── dejavu
│       └── plymouth
│           └── themes
│               ├── details
│               ├── ubuntu-logo
│               └── ubuntu-text
└── var
    ├── cache
    │   └── fontconfig
    └── lib
        └── dhcp

  可以看出这几乎就是一个小型文件系统的根目录。

  通过 systemctl list-dependencies initrd.target 指令来观察系统内默认的initrd.target相依的配置数据如下:

initrd.target
● ├─initrd-parse-etc.service
● ├─basic.target
● │ ├─-.mount
● │ ├─tmp.mount
● │ ├─paths.target
● │ │ ├─acpid.path
● │ │ └─apport-autoreport.path
● │ ├─slices.target
● │ │ ├─-.slice
● │ │ └─system.slice
● │ ├─sockets.target
● │ │ ├─acpid.socket
● │ │ ├─apport-forward.socket
● │ │ ├─avahi-daemon.socket
● │ │ ├─cups.socket
● │ │ ├─dbus.socket
● │ │ ├─dm-event.socket
● │ │ ├─snapd.socket
● │ │ ├─systemd-initctl.socket
● │ │ ├─systemd-journald-audit.socket
● │ │ ├─systemd-journald-dev-log.socket
● │ │ ├─systemd-journald.socket
● │ │ ├─systemd-udevd-control.socket
● │ │ ├─systemd-udevd-kernel.socket
● │ │ └─uuidd.socket
● │ ├─sysinit.target
● │ │ ├─apparmor.service
● │ │ ├─blk-availability.service
● │ │ ├─dev-hugepages.mount
● │ │ ├─dev-mqueue.mount
● │ │ ├─finalrd.service
● │ │ ├─keyboard-setup.service
● │ │ ├─kmod-static-nodes.service
● │ │ ├─lvm2-lvmpolld.socket
● │ │ ├─lvm2-monitor.service
● │ │ ├─plymouth-read-write.service
● │ │ ├─plymouth-start.service
● │ │ ├─proc-sys-fs-binfmt_misc.automount
● │ │ ├─resolvconf.service
● │ │ ├─setvtrgb.service
● │ │ ├─sys-fs-fuse-connections.mount
● │ │ ├─sys-kernel-config.mount
● │ │ ├─sys-kernel-debug.mount
● │ │ ├─sys-kernel-tracing.mount
● │ │ ├─systemd-ask-password-console.path
● │ │ ├─systemd-binfmt.service
● │ │ ├─systemd-boot-system-token.service
● │ │ ├─systemd-hwdb-update.service
● │ │ ├─systemd-journal-flush.service
● │ │ ├─systemd-journald.service
● │ │ ├─systemd-machine-id-commit.service
● │ │ ├─systemd-modules-load.service
● │ │ ├─systemd-pstore.service
● │ │ ├─systemd-random-seed.service
● │ │ ├─systemd-sysctl.service
● │ │ ├─systemd-sysusers.service
● │ │ ├─systemd-timesyncd.service
● │ │ ├─systemd-tmpfiles-setup-dev.service
● │ │ ├─systemd-tmpfiles-setup.service
● │ │ ├─systemd-udev-trigger.service
● │ │ ├─systemd-udevd.service
● │ │ ├─systemd-update-utmp.service
● │ │ ├─cryptsetup.target
● │ │ ├─local-fs.target
● │ │ │ ├─-.mount
● │ │ │ ├─systemd-fsck-root.service
● │ │ │ └─systemd-remount-fs.service
● │ │ └─swap.target
● │ │   └─dev-disk-by\x2duuid-6b5d98c0\x2dcd6e\x2d4a35\x2db8ef\x2dd7743df35ed4.sw…
● │ └─timers.target
● │   ├─anacron.timer
● │   ├─apt-daily-upgrade.timer
● │   ├─apt-daily.timer
● │   ├─e2scrub_all.timer
● │   ├─fstrim.timer
● │   ├─fwupd-refresh.timer
● │   ├─logrotate.timer
● │   ├─man-db.timer
● │   ├─motd-news.timer
● │   ├─snapd.snap-repair.timer
● │   ├─systemd-tmpfiles-clean.timer
● │   └─ua-messaging.timer
● ├─initrd-fs.target
● ├─initrd-root-device.target
● └─initrd-root-fs.target

  可以看出这个小型根文件系统也是通过systemd来进行管理的,同时观察 default.target 的链接,会发现其实这个小型系统就是通过initrd.target 来开机,而initrd.target 也是需要读入一堆例如 basic.target、sysinit.target 等等的硬件侦测、核心功能启用的流程, 然后开始让系统顺利运行。最终才又卸载 initramfs 的小型文件系统,实际挂载系统的根目录。此外initramfs并没有包含各种庞多的驱动文件,而是仅带入开机过程会用到的核心模块而已,例如SCSI、cirto、RAID等和磁盘相关性高的模块。

猜你喜欢

转载自blog.csdn.net/qq_45753394/article/details/120386677
今日推荐