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
发现只有少许的几个文件,但是解压后的文件大小比原文件的要小得多,所以猜测并没有完全解压出来。
网上的文章全都是先用 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等和磁盘相关性高的模块。