经过 chroot 解决 Linux 系统没法启动的问题

这篇博客讲述了博主为了安装 sqlite3,不当心删了 boot 目录下的内核,还重启系统。结果重启失败,经过 Ubuntu 安装 U 盘和 chroot 修复的故事。php

想在学校的服务器上装一个 sqlite3,被告知以前一次更新内核因为 boot 目录满了,没有更新成功,须要完成内核的更新才能进行其它软件的安装。我一看 boot 分区里已经有了不少版本的内核,想都没想就把旧版本的内核 mv 到了 home 目录下,只在 boot 中留了最新版本的内核,而后执行 update-grub 命令再重启,BOOM,系统开不起来了...刚刚才提示的上一次内核更新没有彻底结束,怎么会脑子一抽只留不完整的最新内核呢!linux

 

1、修复过程

没办法,只好带了一个 Ubuntu 的安装 U 盘跑去机房。用 U 盘启动后,选择“试用 Ubuntu”进入 liveCD(或者应该叫 liveUSB?)。幸亏以前把旧版本的内核在 home 目录下备份了,把完整的旧内核复制回 boot 目录,执行 update-grub 后,获得了错误提示:sql

/usr/sbin/grub-probe: error: failed to get canonical path of `aufs'.

上网搜索了一下,aufs 是一种特殊的文件系统,liveCD 里有用。原来咱们执行 update-grub 的环境是在 liveCD 中,而不是咱们要修复的硬盘上的系统,固然会报错。接下来咱们就要经过 chroot 把环境转换到硬盘上的系统中,再从新安装 grub 并 update-grub 就能完成修复。shell

1. 挂载分区

为了 chroot 到硬盘上的系统中,首先须要挂载硬盘分区。经过  sudo fdisk -l  命令能够查看硬盘的分区状况,寻找 root 目录所在的分区。下面用个人虚拟机进行演示。个人虚拟机并无给 boot 目录或者 usr 目录或者 var 目录一类的单独分区,而是所有划在一块硬盘分区里。执行 fdisk 后,能够看到如下输出:ubuntu

fdisk 执行结果

能够看出,个人根目录在硬盘的第一个分区里,咱们执行  sudo mount /dev/sda1 /mnt  把它挂载到 mnt 目录下。安全

有的系统在分区时,会把根目录放在一个逻辑分区里,这时候根目录所在的分区显示的 Type 会是 LVM(我学校的服务器就是这样的)。这时候,根目录所在分区的设备名通常是 /dev/mapper/xxx,xxx 这个字符串通常会指示这个逻辑分区底下有什么目录,好比 xxx 里面包含 root 通常就是这个逻辑分区底下有根目录。那么咱们就要执行  sudo mount /dev/mapper/xxx /mnt 完成根目录的挂载。服务器

还有的系统在分区时,可能会把 boot 目录或者 usr 目录或者 var 目录等等单独分区下(我学校的服务器就是把 boot 目录单独分区)。在完成根目录的挂载后,还须要把这些目录挂载到根目录底下。好比 boot 目录单独分区,设备名是 /dev/sdXY,那么就要执行  sudo mount /dev/sdXY /mnt/boot  完成 boot 目录的挂载。app

2. 挂载虚拟文件系统

Linux 系统运行过程当中,还须要 /proc、/dev、/run 等虚拟文件系统。参考资料 1 中使用下面这个命令统一完成虚拟文件系统的挂载:url

 for i in /dev /dev/pts /proc /sys /run; do sudo mount -B $i /mnt$i; done spa

我在虚拟机中执行这个指令成功了,可是在修复学校服务器时不知道为何不能成功(可是如今也没法重现这个失败...)。因此我使用了参考资料 2 中的命令完成虚拟文件系统的挂载:

sudo mount -t proc /proc /mnt/proc sudo mount --rbind /sys /mnt/sys sudo mount --rbind /dev /mnt/dev sudo mount --rbind /run /mnt/run

我后来特意搜索了一下 mount 指令的几个选项,-B(或者 --bind)能够挂载一个文件系统中已有的子目录,--rbind 则是递归版的 --bind(能够看一下 这篇博客 了解二者的区别),-t 则是指定挂载的文件系统类型。网上也说  sudo mount -B /proc /mnt/proc  和  sudo mount -t proc /proc /mnt/proc  通常没什么区别,因此也不太知道发生了什么...若是有知道区别的朋友还请留言赐教。

3. chroot + 从新安装 grub

完成以上 chroot 的准备后,咱们就能够执行  chroot /mnt  将环境切换到硬盘上的系统了。如今执行  grub-install /dev/sdX (sdX 是你硬盘上的系统所在的设备名)就能在系统所在的设备上从新安装 grub,再执行  update-grub  完成 boot 目录下可用内核的扫描并更新 grub 便可(记得在 update 前先把 boot 目录下损坏的内核移走,只留下好的内核)。最后从新启动,硬盘上的系统又能启动啦!

 

2、如何安全删除内核

手动删除 boot 下的内核有必定风险,咱们应该如何安全地删除内核呢?各类丰富的软件包安装器或管理器给了咱们很大的便利。以 Debian 系的系统 Ubuntu 为例,咱们能够经过 dpkg 查看如今已经安装了哪些内核。执行  dpkg --get-selections | grep linux ,个人虚拟机中结果以下:

dpkg 命令执行结果

其中 linux-image-xxx,linux-headers-xxx 就是内核相关的了。

咱们能够利用 grep 匹配不须要的内核,用软件包管理器删除便可。假如咱们只想留下 4.13.0-37 的内核,能够执行

sudo apt purge `dpkg --get-selections | grep linux | grep 4.13.0 | grep -v 37 | cut -f1`

这一段 shell 命令的意思是:先经过 dpkg --get-selections 查看如今已经安装了哪些软件包,再经过 grep 把全部名字含 linux 的软件包选出来,再把其中全部名字含 4.13.0 的软件包选出来,再把其中全部名字里不含 37 的软件包选出来(-v 选项表示选出不匹配的字符串),再把每一行的第一列都选出来。这样就得到了全部不须要的内核的名字,再用 apt purge 删除便可。固然,grep 命令还须要根据本身机器上已有内核的名称进行调整;并且在删除以前,必定要确认一下是否是真的删除这些内核,防止不想删除的内核被匹配上了,结果误删除。毕竟删除内核仍是一个比较有风险的操做...

删除完毕后,通常会自动更新 grub。不过为了保险起见,仍是手动执行一下 update-grub 比较好。这样就安全地删除了不须要的内核。

 

参考资料:

1. https://help.ubuntu.com/community/Grub2/Installing#via_ChRoot:经过 chroot 安装 grub;

2. https://wiki.archlinux.org/index.php/change_root:chroot 环境的准备;

3. http://www.wutianqi.com/?p=3699:mount --bind 和 mount --rbind 的区别。

相关文章
相关标签/搜索