在 Linux 系统编程和容器技术面试中,根文件系统切换是一个高频考点。很多候选人能说出 chroot 命令的基本用法,但当面试官追问 pivot_root 的区别、容器运行时到底用哪个、以及为什么 chroot 不够安全时,往往就答不完整了。这篇文章从面试题的角度,把这三个概念串起来讲清楚。
为什么需要切换根文件系统
正常情况下,Linux 进程看到的根目录 / 来自内核启动时挂载的根文件系统。但在某些场景下,我们需要让一个进程或一组进程“以为”自己看到了一个全新的根目录:
- 系统救援:原系统无法正常启动时,从 Live CD 启动后
chroot到硬盘上的系统进行修复。 - 服务隔离:让某个服务只能访问特定目录树,限制其可见范围。
- 容器运行:容器需要拥有独立的根文件系统,与宿主机隔离。
- 系统构建:在构建新系统时,在目标目录中执行安装和配置命令。
这些场景的共同需求是:改变进程看到的文件系统根。Linux 提供了两种主要机制:chroot 和 pivot_root。
chroot:最经典的根切换方式
基本用法
chroot 是一个系统调用(chroot(2)),也有对应的命令行工具:
# 准备一个最小根文件系统
mkdir -p /tmp/newroot/{bin,lib,lib64}
cp /bin/bash /tmp/newroot/bin/
# 复制 bash 依赖的库(略)
chroot /tmp/newroot /bin/bash
执行后,当前 shell 的根目录就变成了 /tmp/newroot,它无法通过 .. 或绝对路径访问原来的根。
面试常问:chroot 安全吗?
不安全。 这是面试官最想听到的回答。chroot 只改变了进程的根目录视图,但存在多个逃逸途径:
- root 用户可以直接逃逸:如果进程在 chroot 环境内拥有
CAP_SYS_CHROOT能力或本身就是 root,它可以创建一个新目录,然后再次调用chroot并配合chdir("..")跳出。经典逃逸代码:
mkdir("escape", 0755);
chroot("escape");
chdir("../../../../../../../../");
chroot(".");
-
未关闭的文件描述符:如果进程在 chroot 之前打开了指向外部目录的文件描述符,可以通过
fchdir回到外部。 -
挂载点泄漏:chroot 不隔离挂载命名空间,进程仍可能看到或访问某些挂载点。
因此,chroot 只适合做“文件系统视图切换”,不能作为安全隔离手段。真正安全的隔离需要配合 mount namespace、PID namespace、capability 限制等。
pivot_root:真正替换根文件系统
它解决什么问题
pivot_root 是 pivot_root(2) 系统调用,它的设计目标是原子地替换当前进程的根文件系统,并把旧根挂载到新根下的某个目录,之后可以卸载掉。
与 chroot 的关键区别:
pivot_root要求新根必须是一个挂载点(mount point),不能是普通目录。- 它同时改变根目录和当前工作目录,且操作是原子的。
- 旧根不会被丢弃,而是被挂载到新根下的一个目录,方便后续清理。
- 通常配合
CLONE_NEWNS(mount namespace)使用,实现真正的隔离。
典型调用流程
// 1. 创建新根目录并挂载
mkdir("/newroot", 0755);
mount("/dev/sda1", "/newroot", "ext4", 0, NULL);
// 2. 创建旧根的挂载点
mkdir("/newroot/oldroot", 0755);
// 3. 切换根
pivot_root("/newroot", "/newroot/oldroot");
// 4. 切换工作目录
chdir("/");
// 5. 卸载旧根
umount2("/oldroot", MNT_DETACH);
rmdir("/oldroot");
面试常问:pivot_root 能完全替代 chroot 吗?
不能完全替代,但它是容器场景下的正确选择。原因:
pivot_root要求新根是挂载点,某些场景下不方便满足(比如只是一个普通目录)。- 它通常需要
CAP_SYS_ADMIN能力。 - 在 mount namespace 中使用时,它只影响当前命名空间,这正是容器需要的。
容器运行时到底用哪个
这是面试中的“送分题”也是“区分题”。以 Docker、containerd、runc 为代表的容器运行时,实际流程大致是:
- 创建新的 mount namespace、PID namespace、UTS namespace 等。
- 准备容器的 rootfs(从镜像层叠加而来)。
- 优先使用
pivot_root切换根文件系统,而不是chroot。 - 如果
pivot_root失败(某些特殊环境),才回退到chroot。
runc 的源码中可以看到这个逻辑:在 prepareRootfs 阶段,它会先尝试 pivot_root,失败时再调用 chroot。这样做是因为 pivot_root 能更干净地卸载旧根,避免旧根文件系统被容器进程意外访问。
为什么容器不用 chroot 就够了
因为容器的安全边界不只靠根切换。它依赖的是:
- namespace:mount、PID、network、UTS、IPC、user、cgroup。
- cgroup:资源限制。
- capabilities:能力裁剪。
- seccomp:系统调用过滤。
- LSM(SELinux/AppArmor):强制访问控制。
chroot 或 pivot_root 只是其中“文件系统视图”这一环。单独用 chroot 做容器隔离,是极不安全的。
对比总结
| 特性 | chroot | pivot_root |
|---|---|---|
| 系统调用 | chroot(2) |
pivot_root(2) |
| 新根要求 | 任意目录 | 必须是挂载点 |
| 旧根处理 | 不可见,但可能逃逸 | 挂载到新根下可卸载 |
| 原子性 | 否 | 是 |
| 安全隔离 | 弱,可逃逸 | 强,配合 namespace |
| 容器中使用 | 回退方案 | 首选方案 |
| 所需权限 | CAP_SYS_CHROOT | CAP_SYS_ADMIN |
面试回答建议
如果面试官问“chroot 和 pivot_root 有什么区别”,可以按这个结构回答:
- 先说两者都是切换根文件系统的机制,但设计目标不同。
- 说清
pivot_root要求新根是挂载点、原子切换、旧根可卸载。 - 指出
chroot的安全缺陷和逃逸方式。 - 最后落到容器场景:runc 优先用
pivot_root,失败回退chroot,且根切换只是容器隔离的一环。
这样回答既展示了系统调用层面的理解,也体现了对容器运行时实现的熟悉,基本能覆盖面试官想考察的所有点。
延伸思考
- 在 user namespace 中,非特权用户能否使用
pivot_root?答案是可以,因为 user namespace 内拥有CAP_SYS_ADMIN,但受限于该命名空间。 chroot后如何安全地降权?通常先chroot,再setuid到非特权用户,并丢弃 capabilities。- 现代容器中
pivot_root与mount --move、open_tree/move_mount等新 API 的关系,也是加分项。
掌握这些内容,无论是面试还是实际排查容器根文件系统问题,都能做到心中有数。
未经允许不得转载:任鹏个人博客 » Linux 面试题:chroot、pivot_root 与容器根文件系统切换

