问题描述

进入到Ubuntu16.04后发现无法复制、粘贴,但是用ls -l查看权限都是正常的。重启发现一直卡在关机的界面,于是硬重启(按下机箱的reset按钮),重启后进入到initramfs的shell提示符。

原因分析

回顾Linux系统启动过程,进入initramfs就意味着kernel已经load了,就差挂载rootfs了。说明是rootfs所在的磁盘出现了问题。

解决方法

  1. 用cat /proc/cmdline会找到rootfs所在磁盘的UUID。

  2. 用sudo blkid会找到所有磁盘的UUID和它对应的dev/下的设备号,比如说/dev/sdc1。

  3. 用fsck -l /dev/sdc1来修复磁盘文件系统(innodes、blocks),一路按y就可以修复磁盘了。

最终成功。

相关知识

1.initramfs是什么?

The initramfs is a gzipped cpio archive. At boot time, the kernel unpacks that archive into RAM disk, mounts and uses it as initial root file system. All finding of the root device happens in this early user space.
在initramfs的提示符下,也可以看到和rootfs差不多的文件夹:bin、lib等。

2.initramfs的作用是?

It is used for mounting the real rootfs which has all your data. The initramfs carries the modules needed for mounting your rootfs.

3. 一定要有initramfs吗?

initramfs中包含的module都是为了mount rootfs,但是这些module其实都可以编译到kernel中,那么还需要initramfs吗?
答案是:“depends on your system”。
一个必须需要initramfs的例子就是对加密设备的访问。现在考虑一下没有initramfs、但是在挂载rootfs前需要向用户询问密码的场景。这个询问密码是需要在user space执行的,但是只有rootfs挂载后,才可以在user space中执行这个功能。于是这显然是一个chicken and egg problem,无解。
因此对于这种情况,除了rootfs之外,必须有一个临时的、可以实现在user space实现相关功能的fs,即initramfs。
那么这些user space的utility都来自与哪里?实际上都来自于rootfs。因此,可以把initramfs理解为是rootfs的精简版。

4. initramfs在哪里?

进入ubuntu后,在/boot/grub目录下,有一个grub.ctg文件,有这样一段:

menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-95e0c7e4-bca0-4431-b225-c5a27347c8e7' {
	recordfail
	load_video
	gfxmode $linux_gfx_mode
	insmod gzio
	if [ x$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi
	insmod part_msdos
	insmod ext2
	set root='hd2,msdos1'
	if [ x$feature_platform_search_hint = xy ]; then
	  search --no-floppy --fs-uuid --set=root --hint-bios=hd2,msdos1 --hint-efi=hd2,msdos1 --hint-baremetal=ahci2,msdos1  95e0c7e4-bca0-4431-b225-c5a27347c8e7
	else
	  search --no-floppy --fs-uuid --set=root 95e0c7e4-bca0-4431-b225-c5a27347c8e7
	fi
    linux	/boot/vmlinuz-4.15.0-88-generic root=UUID=95e0c7e4-bca0-4431-b225-c5a27347c8e7 ro  quiet splash $vt_handoff
	initrd	/boot/initrd.img-4.15.0-88-generic
}

最后一行的initrd就是initramfs对应的文件:/boot/initrd.img-4.15.0-88-generic。
用file指令查看这个文件:

$ file initrd.img-4.15.0-45-generic 
initrd.img-4.15.0-45-generic: ASCII cpio archive (SVR4 with no CRC)

可以看出它是一个cpio的档案文件,大小只有60多MB。而Ubuntu一个完整的rootfs大概是GB的级别。

5. initramfs中都有哪些文件?

如何解压缩initrd.img-4.15.0-45-generic?
虽然用file指令可以看出该文件是一个ASCII cpio文件,但是用cpio -id < initrd.img-4.15.0-45-generic却只能得到如下的文件:

└── kernel
    └── x86
        └── microcode
            └── AuthenticAMD.bin

这显然不对,因为initrd.img-4.15.0-45-generic的大小是68.5MB,而这个AuthenticAMD.bin只有27.4KB。顺便说一句,这个AuthenticAMD.bin文件是对AMD CPU的microcode打补丁的文件。更过关于microcode的知识,可以看这篇

正确提取initrd.img-4.15.0-45-generic的方法如下,首先用binwalk来查看initrd.img-4.15.0-45-generic的组成:

$ binwalk initrd.img-4.15.0-45-generic 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             ASCII cpio archive (SVR4 with no CRC), file name: ".", file name length: "0x00000002", file size: "0x00000000"
112           0x70            ASCII cpio archive (SVR4 with no CRC), file name: "kernel", file name length: "0x00000007", file size: "0x00000000"
232           0xE8            ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86", file name length: "0x0000000B", file size: "0x00000000"
356           0x164           ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode", file name length: "0x00000015", file size: "0x00000000"
488           0x1E8           ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode/AuthenticAMD.bin", file name length: "0x00000026", file size: "0x00006B2A"
28072         0x6DA8          ASCII cpio archive (SVR4 with no CRC), file name: "TRAILER!!!", file name length: "0x0000000B", file size: "0x00000000"
28672         0x7000          ASCII cpio archive (SVR4 with no CRC), file name: "kernel", file name length: "0x00000007", file size: "0x00000000"
28792         0x7078          ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86", file name length: "0x0000000B", file size: "0x00000000"
28916         0x70F4          ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode", file name length: "0x00000015", file size: "0x00000000"
29048         0x7178          ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode/GenuineIntel.bin", file name length: "0x0000002A", file size: "0x002D2C00"
2989584       0x2D9E10        ASCII cpio archive (SVR4 with no CRC), file name: "TRAILER!!!", file name length: "0x0000000B", file size: "0x00000000"
2990080       0x2DA000        gzip compressed data, from Unix, last modified: 2020-02-23 06:04:02
24602446      0x177674E       xz compressed data
40511347      0x26A2773       MySQL ISAM compressed data file Version 9
60684299      0x39DF80B       MySQL ISAM compressed data file Version 3

可以看出,该文件后面还有gzip、xz、MYSQL ISAM压缩文件。
提取出gzip文件、并依次通过gzip和cpio对其解压:

dd if=initrd.img-4.15.0-45-generic of=image.gz bs=2990080 skip=1
gunzip image.gz 
cpio -i < image

得到以下文件夹,这个就和我们在initramfs提示符下看到的差不多了:

.
├── bin
├── conf
├── etc
├── image
├── init
├── lib
├── lib64
├── run
├── sbin
├── scripts
├── usr
└── var

上述方法参考了Ubuntu18.04的解决方法
我没有对initrd.img-4.15.0-45-generic剩下的两个文件xz compressed data和MYSQL ISAM继续研究。

6. initramfs中的文件如何执行?

ubuntu的wiki详细描述了这个过程。
总的来说,initramfs的执行过程是:

  1. 执行init进程
  2. init调用scripts文件夹其他脚本
    1)init-top
    2)init-premount
    3)boot-top (your crypt scripts execute here - to ask the user password for eg)
    4)boot-premount
    5)boot:mount rootfs到/initramfs/root/
    6)boot-bottom
    7)init-bottom
  3. init进程重新得到控制权,然后执行:
    1)moves the /sys from initramfs to /initramfs/root/sys (in your real rootfs)
    2)moves /proc from initramfs to /initramfs/root/proc
    3)calls run-init to run the real init in your real rootfs kept in /root. run-init does something like
    chroot to the real rootfs and then executes the init kept in /sbin/ or /bin or whatever user requested as a boot parameter.
Logo

更多推荐