![为什么需要在 `pivot_root` 之后执行 `exec` 来卸载旧的根文件系统?](https://linux22.com/image/175886/%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%E5%9C%A8%20%60pivot_root%60%20%E4%B9%8B%E5%90%8E%E6%89%A7%E8%A1%8C%20%60exec%60%20%E6%9D%A5%E5%8D%B8%E8%BD%BD%E6%97%A7%E7%9A%84%E6%A0%B9%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%EF%BC%9F.png)
概要:
pivot_root new_root put_old
问题:
为什么需要执行exec
更改正在运行的可执行文件才能卸载旧的根文件系统(put_old
)?
假设put_old
移动到不在新根文件系统内的目录 ( new_root
)。那还可以卸载吗?
为什么chroot
执行后还需要pivot_root
?new_root
此时已经成为新的根文件系统。
pivot_root
最后,如果父进程(例如bash
)已从 shell 分叉,如何更改其根目录?更改不应该只影响pivot_root
流程本身吗?页面中的“当前进程”是man
指父进程还是pivot_root
其本身?
例子:
mount /dev/hda1 /new-root
cd /new-root
pivot_root . old-root
exec chroot . sh <dev/console >dev/console 2>&1
umount /old-root
man pivot_root
:
请注意,
exec chroot
更改正在运行的可执行文件,如果之后应卸载旧根目录,则这是必要的。
答案1
问:为什么需要执行
exec
更改正在运行的可执行文件才能卸载旧的根文件系统(put_old
)?
因为文件系统在繁忙时无法卸载,并且如果正在运行的可执行文件(例如bash
)或其共享库位于旧的根文件系统上,它们会使它保持繁忙。
问:假设
put_old
移动到不在新根文件系统内的目录 (new_root
)。那还可以卸载吗?
这是不允许的put_old在外面新根。来自系统调用的手册页:
put_old必须位于或下方新根
问:为什么
chroot
执行后还需要pivot_root
?new_root
此时已经成为新的根文件系统。
不再需要它了。原始手册页因为系统调用是2019年大幅重写[替代链接],以及更新的手册页[存档链接][替代链接]解释了行为和历史不确定性:
pivot_root()
将同一挂载命名空间中每个进程或线程的根目录和当前工作目录更改为新根如果它们指向旧的根目录。...
[此行为]是必要的,以防止内核线程使旧的根挂载忙于其根目录和当前工作目录[...]
...
历史笔记
多年来,该手册页包含以下文本:
pivot_root()
可能会也可能不会更改使用旧根目录的任何进程或线程的当前根目录和当前工作目录。的调用者pivot_root()
必须确保根目录或当前工作目录位于旧根目录的进程在任何一种情况下都能正确运行。确保这一点的一个简单方法是将其根目录和当前工作目录更改为 新根在调用之前pivot_root()
。这篇文章是在系统调用实现在内核中最终确定之前编写的,可能是为了警告当时的用户,在最终发布之前实现可能会发生变化。然而,自该系统调用首次实现以来,描述中所述的行为一直保持一致,现在不会改变。
问:
pivot_root
最后,如果父进程(例如bash
)已从 shell 分叉,如何更改其根目录?更改不应该只影响pivot_root
流程本身吗?页面中的“当前进程”是man
指父进程还是pivot_root
其本身?
这是因为pivot_root()
操作于挂载命名空间调用进程的信息,与父进程共享。该实用程序的手册页(枢轴_根(8))没有明确说明这一点,但系统调用的更新手册页(枢轴_根(2)) 确实(强调):
pivot_root()
更改根挂载在挂载命名空间中的调用过程。 [...]
pivot_root()
更改根目录和当前工作目录每个进程或线程在同一个挂载命名空间中新根如果它们指向旧的根目录。