[Share Experiences] 【有点长的】chroot超级新手教程
Tofloor
poster avatar
enforcee
deepin
2021-10-25 19:08
Author

尽量通俗一点,嫌长可以直接看后面。这个帖子只讲chroot一个命令,要修电脑只用这一个是不可能的。
欢迎补充、挑错。
使用root权限一定要谨慎!

Reply Favorite View the author
All Replies
enforcee
deepin
2021-10-25 19:09
#1

“借尸还魂”,“移花接木”

无论是什么系统的用户,最让人恐惧的事不过是系统无法启动、彻底瘫痪。俗话说得好,重装系统解决90%的问题(重买电脑解决100%的问题)。但是并不是每个用户都能心平气和地接受重装,毕竟系统里有重要的文件资料、花了很长时间才调顺手的配置,或者是长期使用产生的感情等等。很多时候,我们希望系统还能抢救一下,然而他已经丧失了给你提供字符或图形界面的能力。

设想一个场景,汽车熄火又忘记关空调,结果电瓶没电了...当然如果发动机在运行,燃料就会源源不断地转化成电力,尴尬的是,发动机的启动也是需要用电的(如果是手摇拖拉机就好了)。这时候有位好心的司机打开发动机盖,用电线把他车上的电池和你的连接起来,借助这位司机的电能成功启动了发动机,你的车又能跑了,电器也能用了,这时候不用他的电池供电也没有关系了。

Reply View the author
enforcee
deepin
2021-10-25 19:09
#2

切换根目录(change root)

在GNU/Linux系统上,你能在一个已经启动的系统“搬家”般地进入另一个系统。这和虚拟机是不一样的。GNU/Linux的程序并不在乎他的资源是从谁那得到的,只关注资源是不是他想要的、资源的位置(文件路径)是不是他能找到的。只要能满足这些需求,程序就能正确执行。我们想在一个系统上执行其他系统的命令,而他所需要的资源都在他本来的系统上,那只需要“瞒住”程序实际的路径,告诉他一个“虚假”的路径,程序就会按照用户指定的位置去寻找他所需的资源。实际上各种沙箱技术也是按照这个原理设计的。

chroot命令就提供了这么一个功能,让一个其他的路径成为程序运行的根目录。这听上去可能有点不可思议,如果让一个别的目录变成根目录,那系统岂不是有两个根目录?根目录也并不是“最底层的目录”,在“根目录”之上还有一个“根目录”?我们要时刻记得,在Linux中,文件目录都是“虚构”的,他并不是和硬盘上的文件系统完全一致,Linux是把一个文件夹取了一个“根目录”的名字,让整个系统围绕着这个文件夹运行。而chroot只是“虚构”了另一个世界,在这个世界中“根目录”的名字属于其他文件夹。chroot虚构的范围只是他的子进程,他没办法欺骗其他运行的程序,其他程序不会理睬chroot,在chroot中运行的程序也不会感知到chroot外发生的事情。

实际上在现代GNU/Linux系统的启动过程中,并不会直接就启动硬盘中的系统,而是先启动一个名为“初始化内存盘(inital ramdisk)”的简化版文件系统(大家可以在/boot目录找到以initramfs开头的文件,就是他。顺便一提,旁边的vmlinuz就是linux内核的本体)。在经过一些操作后,再将控制转移到真实的文件系统上并启动第一个程序“init”程序(deepin的init是systemd)。这个过程就如chroot。

Reply View the author
enforcee
deepin
2021-10-25 19:11
#3

chroot的初次尝试

其实非常简单,只需要打开任何一个终端或者tty(按Ctrl+Alt+功能键F1,F2,F3..等等,在不同的系统中这几个键代表的控制台不同,有的是图形界面,有的是tty文本界面,多试几个)

然后输入
sudo chroot <路径>
就可以了。(输入命令后别忘了按回车——写给特别超级新手)

很简单吧。不过这个命令要想执行成功有一些要求。比较重要的是:
1:你必须要有root权限。要么你正在使用root用户,要么你是其他用户,但是你有执行sudo命令的能力。
2:输入的路径必须成为根目录的能力。即:他应该包含/usr,/dev等等这些系统目录。如果他本身就是一个GNU/Linux系统的根分区,这个条件自然满足。

现在我们来实际操作一下。假如我安装了一个deepin的系统,然后我们想用另一个系统chroot进这个deepin系统,那么,
我们先要挂载deepin的分区。鉴于现在很多用户都在用图形界面,我们直接用图形界面的文件管理器或者磁盘管理器去挂载就可以了。命令挂载的自行探索。
挂载好了以后我们去找挂载的位置。一般系统会默认挂载在/media/用户名/磁盘名或UUID,假设他叫/media/abc/deepin。
打开终端,输入命令
sudo chroot /media/abc/deepin/
如果你觉得路径名太长输入太累,可以只输入“sudo chroot ”然后从文件管理器里把deepin这个文件夹用鼠标拖到终端窗口里,这样就自动输入了(路径加引号没关系的,其实加了引号更标准)。

有经验的玩家可以选择更改当前工作目录(这样我们只要输入.点号就代表那一长串路径,输入非'/'为首的文件名就是这个路径下的文件)
cd /media/abc/deepin/
sudo chroot .

执行后,我们会发现,我们从其他系统来到了deepin系统内,打开了deepin的默认shell,并且我们的身份是root!
运行一下
cat /etc/os-version
能看到系统版本。看看chroot内外的差别。

同时我们输入
uname -a
我们会发现,正在使用的内核不是chroot进入的deepin系统的内核,而仍然是真实系统的内核。

想结束命令shell,按Ctrl+D组合键或者输入exit命令。

在上面我们提到chroot进入的是默认shell,但是万一这个默认shell也坏掉不能用了呢(毕竟谁都有犯傻的时候)?chroot提供了一个直接执行命令的选项。只要在上面的命令后面跟着输入命令的路径和选项就可以了(注意命令是chroot内系统的命令,要按照新根目录的路径)
sudo chroot <要切根的路径> <命令>

Reply View the author
enforcee
deepin
2021-10-25 19:13
#4

更完善的chroot环境

我们知道在Linux中有些文件并不是实际在硬盘上,而是为了一些特殊操作而“虚构”出来的。我们挂载上其他系统的根分区,打开proc,sys等目录,会发现他空无一物。这个目录中的文件只有系统启动时才有,并存在内存中,有一些应用是需要使用这些目录的。我们上面做的简单的chroot环境,在执行一些复杂的命令时可能会出现问题,因为这些程序需要这些位置的资源。chroot并不会像真实系统一样建立这些“虚拟”文件。幸好,在Linux中我们可以将一些目录“映射”到其他目录,这可以让chroot内这些目录的访问就会用实际系统的相同目录替代。比如,在我们chroot环境中没有的proc,sys,dev等文件夹替换成当前系统中的/proc,/sys,/dev,这样需要这些资源的命令在chroot环境中也能使用。

代码如下:

cd <要切根的路径>
sudo mount -t proc /proc proc/
sudo mount -t sysfs /sys sys/
sudo mount --rbind /dev dev/
sudo chroot .

 

不过这样亦非终止(尽管上面的命令已经能适应大多数情况了),在具体实施上,我们要熟知程序的需求和执行目标,针对性地提供所需资源。一般不应该把实际系统上有硬盘文件的目录暴露给chroot环境,以免在chroot内执行命令时损坏真实系统。

说了这么多,系统损坏无法启动到底要怎么维修呢?chroot只是维修系统的第一步,显然之后还是要进行其他操作的。给个小提示,apt是deepin的包管理器,无论是系统内核还是应用、动态库都可以通过apt重装。

Reply View the author
enforcee
deepin
2021-10-25 19:15
#5

扩展阅读

要知道某个命令的用法,可以在终端中输入
<某个命令> --help

<某个命令> -h

要查看某个命令的手册,可以用man命令(虽然都是英文)
man <某个命令>

善用搜索引擎可以查到很多有用信息。一定要谨慎甄别,不要输入作用不明的命令。

arch wiki永远是GNU/Linux玩家的好朋友!
https://wiki.archlinux.org/title/Chroot

虽然我们在上边介绍chroot主要是以维修系统的角度去讲解,但是chroot的玩法不只这些。还请各位多多探索。

Reply View the author
神末shenmo
deepin
Spark-App
Q&A Team
2021-10-25 19:35
#6

可以举一个例子吗?这样比较好理解

比如我不小心卸载了grub,想要恢复

Reply View the author
sgb76
deepin
2021-10-25 19:49
#7

学习了,这种学习贴可以多来点

Reply View the author
enforcee
deepin
2021-10-25 22:57
#8
神末shenmo

可以举一个例子吗?这样比较好理解

比如我不小心卸载了grub,想要恢复

好问题

刚才测试了一下,只要按照格式来就可以了,首先的挂载步骤要多几个(可能有几个是没用的,我没试那么多,这些命令arch wiki里都有)

cd <要切根的路径>

sudo mount -t proc /proc proc/

sudo mount -t sysfs /sys sys/

sudo mount --rbind /dev dev/

sudo mount --rbind  /sys/firmware/efi/efivars sys/firmware/efi/efivars/

sudo mount --rbind /run run/

然后要挂载efi分区,如果用LiveCD的话就要挂载硬盘里的efi分区再rbind,如果是同一硬盘的其他位置就直接用/boot/efi

sudo mount --rbind <路径> boot/efi

然后就chroot即可

sudo chroot .

如果grub组件有损坏就重装,第一个包是一些命令,第二个包是二进制文件

apt reinstall grub-common grub-efi-amd64-bin grub-efi-amd64-signed

如果系统内的组件是没有损坏的,就不需要apt再下载,用自带工具就可以了

grub-install

然后要重新生成配置文件(其实一般问题是出在这),这里有个小插曲,我当前的系统没有/sbin,而deepin的grub的命令在/sbin里,因此要先添加 $PATH

PATH=$PATH":/sbin"

生成配置文件可以用debian系的脚本,非debian的要用grub-mkconfig

update-grub

Reply View the author
神末shenmo
deepin
Spark-App
Q&A Team
2021-10-27 19:01
#9
enforcee

好问题

刚才测试了一下,只要按照格式来就可以了,首先的挂载步骤要多几个(可能有几个是没用的,我没试那么多,这些命令arch wiki里都有)

cd <要切根的路径>

sudo mount -t proc /proc proc/

sudo mount -t sysfs /sys sys/

sudo mount --rbind /dev dev/

sudo mount --rbind  /sys/firmware/efi/efivars sys/firmware/efi/efivars/

sudo mount --rbind /run run/

然后要挂载efi分区,如果用LiveCD的话就要挂载硬盘里的efi分区再rbind,如果是同一硬盘的其他位置就直接用/boot/efi

sudo mount --rbind <路径> boot/efi

然后就chroot即可

sudo chroot .

如果grub组件有损坏就重装,第一个包是一些命令,第二个包是二进制文件

apt reinstall grub-common grub-efi-amd64-bin grub-efi-amd64-signed

如果系统内的组件是没有损坏的,就不需要apt再下载,用自带工具就可以了

grub-install

然后要重新生成配置文件(其实一般问题是出在这),这里有个小插曲,我当前的系统没有/sbin,而deepin的grub的命令在/sbin里,因此要先添加 $PATH

PATH=$PATH":/sbin"

生成配置文件可以用debian系的脚本,非debian的要用grub-mkconfig

update-grub

加精

Reply View the author
神末shenmo
deepin
Spark-App
Q&A Team
2023-07-23 07:16
#10
enforcee

好问题

刚才测试了一下,只要按照格式来就可以了,首先的挂载步骤要多几个(可能有几个是没用的,我没试那么多,这些命令arch wiki里都有)

cd <要切根的路径>

sudo mount -t proc /proc proc/

sudo mount -t sysfs /sys sys/

sudo mount --rbind /dev dev/

sudo mount --rbind  /sys/firmware/efi/efivars sys/firmware/efi/efivars/

sudo mount --rbind /run run/

然后要挂载efi分区,如果用LiveCD的话就要挂载硬盘里的efi分区再rbind,如果是同一硬盘的其他位置就直接用/boot/efi

sudo mount --rbind <路径> boot/efi

然后就chroot即可

sudo chroot .

如果grub组件有损坏就重装,第一个包是一些命令,第二个包是二进制文件

apt reinstall grub-common grub-efi-amd64-bin grub-efi-amd64-signed

如果系统内的组件是没有损坏的,就不需要apt再下载,用自带工具就可以了

grub-install

然后要重新生成配置文件(其实一般问题是出在这),这里有个小插曲,我当前的系统没有/sbin,而deepin的grub的命令在/sbin里,因此要先添加 $PATH

PATH=$PATH":/sbin"

生成配置文件可以用debian系的脚本,非debian的要用grub-mkconfig

update-grub

我跑一些xapps应用,提示/run下有文件缺失,好像dbus也有问题,是通过-rbind /run 可以解决吗

Reply View the author
enforcee
deepin
2023-07-23 18:37
#11
神末shenmo

我跑一些xapps应用,提示/run下有文件缺失,好像dbus也有问题,是通过-rbind /run 可以解决吗

缺什么就补什么
blush
/run也是个临时目录,应该没有危害

Reply View the author
神末shenmo
deepin
Spark-App
Q&A Team
2023-07-23 21:59
#12
enforcee

缺什么就补什么
blush
/run也是个临时目录,应该没有危害

bind和rbind的区别是什么呢

正在用bwrap搓运行环境

Reply View the author
SamLukeYes
deepin
2023-07-23 22:38
#13

我一般都偷懒直接用 arch-chroot,nixos 的话就用 nixos-enterblush

Reply View the author
enforcee
deepin
2023-07-23 22:53
#14
神末shenmo

bind和rbind的区别是什么呢

正在用bwrap搓运行环境

r其实就是递归(recursive),如果经常用cp、rm之类的命令会很熟悉。--bind只能挂载一个树,不包含下属的其他挂载,--rbind也会把子挂载也同时挂载上。

Reply View the author
enforcee
deepin
2023-07-23 23:05
#15
SamLukeYes

我一般都偷懒直接用 arch-chroot,nixos 的话就用 nixos-enterblush

是bash脚本的魔法
yeah

Reply View the author