当前位置: 首页 > news >正文

pivot_root:原理、用途及最简单 Demo

什么是 pivot_root

pivot_root 是 Linux 系统中的一个系统调用(和对应的命令行工具),用于更改进程的根文件系统。与 chroot 类似,pivot_root 将一个指定目录设置为进程的新根目录(/),但它比 chroot 更灵活,特别适合容器环境。它的核心功能是将当前根文件系统(旧根)移动到另一个位置,并将新目录设置为根文件系统。

主要特点

  1. 交换根文件系统

    • pivot_root 将一个新目录设置为进程的根(/),并将旧根移动到指定位置(通常是一个子目录)。
    • 例如,pivot_root(new_root, put_old)new_root 设置为新根,旧根挂载到 put_old
  2. 挂载点隔离

    • chroot 不同,pivot_root 要求新根和旧根是独立的挂载点,通常需要 Mount Namespace 支持。
    • 它可以完全隔离旧根的挂载点,防止进程访问主机文件系统。
  3. 容器初始化

    • pivot_root 是容器运行时(如 Docker、containerd)的标准工具,用于将容器镜像的根文件系统设置为容器进程的根。

主要用途

  1. 容器根文件系统切换:在容器启动时,将镜像的根文件系统(例如 /var/lib/docker/...)设置为容器的新根,隔离主机文件系统。
  2. 强隔离环境:比 chroot 提供更彻底的隔离,防止进程通过挂载点逃逸。
  3. 系统初始化:在 Linux 启动过程中(initramfs),将临时根切换到真实根文件系统。
  4. 测试和开发:为进程提供独立的根文件系统,测试特定环境。

与 chroot 的区别

  • 隔离程度
    • chroot 仅更改根目录,进程仍可能看到主机挂载点(如 /proc/dev)。
    • pivot_root 要求新根是独立挂载点,旧根被移动到新位置,提供更强的隔离。
  • 使用场景
    • chroot 适合简单隔离(如测试或修复)。
    • pivot_root 适合容器,需配合 Mount Namespace。
  • 要求
    • chroot 直接作用于目录。
    • pivot_root 需要新根和旧根是挂载点,且进程在 Mount Namespace 中。

与 Mount Namespace 的关系

你的前文提到 Mount Namespace 和 nsenter 的问题(在 Mount Namespace 中挂载 tmpfs 到 /tmp/mnt,但看到主机目录的其他文件)。pivot_root 常与 Mount Namespace 结合使用:

  • Mount Namespace 提供独立的挂载点视图,确保新根挂载(如 tmpfs 或容器镜像)仅在命名空间内可见。
  • pivot_root 将新根设置为进程的根目录,隔离主机文件系统。
  • 结合 nsenter,可以进入 Mount Namespace 验证 pivot_root 的隔离效果。

pivot_root 最简单 Demo

以下是一个最简单的 pivot_root 示例,展示如何在 Mount Namespace 中使用 pivot_root 切换根文件系统,确保隔离主机文件系统。我们将:

  • 创建一个新目录作为新根。
  • 使用 Mount Namespace 和 tmpfs 模拟容器根文件系统。
  • 通过 pivot_root 切换根,并验证隔离。
  • 使用 nsenter 进入 Mount Namespace 检查结果。

环境要求

  • Linux 系统(支持 pivot_root 和 Mount Namespace,例如 Ubuntu、CentOS)
  • unsharensenter 命令(util-linux 包)
  • root 权限(pivot_root 和挂载需要管理员权限)
  • bash 和必要库(用于新根环境)

步骤

  1. 清理旧环境
    确保 /tmp/mnt 干净,清理旧进程(基于你的 ps aux 输出,PID 1537515376):
sudo umount /tmp/mnt 2>/dev/null
sudo rm -rf /tmp/mnt
sudo mkdir -p /tmp/mnt

验证进程已清理:

ps aux | grep '[u]nshare --mount'

预期输出为空。

  1. 创建新根目录
    创建一个新根文件系统,包含 bash 和必要库:
sudo mkdir -p /tmp/mnt/bin /tmp/mnt/lib /tmp/mnt/lib64 /tmp/mnt/old_root
sudo cp /bin/bash /tmp/mnt/bin/

复制 bash 依赖库(检查 ldd /bin/bash 输出):

ldd /bin/bash

输出示例:

linux-vdso.so.1 (0x00007fffc8bff000)
libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007f0c6c5c9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0c6c3c7000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0c6c628000)

复制库:

sudo cp /lib/x86_64-linux-gnu/libtinfo.so.6 /tmp/mnt/lib/
sudo cp /lib/x86_64-linux-gnu/libc.so.6 /tmp/mnt/lib/
sudo cp /lib64/ld-linux-x86-64.so.2 /tmp/mnt/lib64/
  1. 启动 Mount Namespace 并挂载新根
sudo unshare --mount /bin/bash

在新的 bash shell 中,设置挂载隔离并挂载新根:

mount --make-rprivate -o rec /
mount --bind /tmp/mnt /tmp/mnt
echo "Hello from Mount Namespace!" > /tmp/mnt/testfile
ls /tmp/mnt

输出:

bin  lib  lib64  old_root  testfile

说明

  • mount --bind /tmp/mnt /tmp/mnt 使 /tmp/mnt 成为独立挂载点,满足 pivot_root 的要求。
  • old_root 是旧根的存放目录。
  1. 执行 pivot_root
    切换根文件系统,将 /tmp/mnt 设为新根,旧根移到 /tmp/mnt/old_root
cd /tmp/mnt
pivot_root . old_root

修复环境
pivot_root 后,/bin/bash 仍在旧根中,需更新路径:

export PATH=/bin

验证新根:

ls /

输出:

bin  lib  lib64  old_root  testfile

尝试访问旧根:

ls /old_root

输出示例:

bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

隔离验证
尝试访问主机 /tmp

ls /old_root/tmp

输出显示主机 /tmp 的内容,但新根(/)只包含 /tmp/mnt 的内容,表明隔离成功。

  1. 验证全局命名空间隔离
  • 保持 unshare shell 运行,打开另一个终端:
ls /tmp/mnt

输出:

bin  lib  lib64  old_root

全局命名空间看不到 testfile,因为它仅在 Mount Namespace 中创建。

  1. 使用 nsenter 进入 Mount Namespace
    在另一个终端,查找 PID:
ps aux | grep '[u]nshare --mount'

输出示例:

root     16000  0.0  0.1  16732  7040 pts/0    S+   09:00   0:00 sudo unshare --mount /bin/bash

进入 Mount Namespace:

sudo nsenter --mount=/proc/16000/ns/mnt /bin/bash

验证新根:

ls /tmp/mnt

输出:

bin  lib  lib64  old_root  testfile

注意:如果 pivot_root 已执行,/tmp/mnt 是旧路径,可能不可见。直接检查新根:

ls /

输出:

bin  lib  lib64  old_root  testfile
  1. 退出并清理
  • nsenter shell:
exit
  • unshare shell:
umount old_root
umount /tmp/mnt
exit
  • 清理目录:
sudo rm -rf /tmp/mnt

解决主机文件可见问题

在 Mount Namespace 中挂载 tmpfs 到 /tmp/mnt 后,通过 nsenter 进入仍看到主机目录的其他文件。pivot_root 可以解决此问题,因为它完全替换根文件系统,隔离主机文件系统。关键点:

  • Mount Namespace 隔离mount --make-rprivate 确保挂载不传播。
  • pivot_root 切换:将 /tmp/mnt 设为新根,旧根移到 old_root,防止主机文件(如 /tmp/mnt 的其他文件)可见。
  • nsenter 验证:进入 Mount Namespace 检查新根只包含预期内容(如 testfile)。

如果仍看到主机文件,检查:

  • 是否正确执行 pivot_root(需要 cd /tmp/mnt 和挂载点)。
  • /tmp/mnt 是否干净(步骤 1)。
  • PID 是否正确(步骤 6)。

注意事项

  1. 权限pivot_rootunsharensenter 需要 root 权限。
  2. 挂载点要求pivot_root 要求新根和旧根是独立挂载点,使用 mount --bind 创建。
  3. 依赖完整性:新根需包含必要文件(如 bash 和库),否则报错。
  4. 清理:实验完成后,卸载挂载点并删除临时目录。
  5. 与 chroot 对比pivot_root 提供更强隔离,适合容器场景。

总结

pivot_root 是一种强大的根文件系统切换工具,通过将新目录设为根并移动旧根,实现比 chroot 更彻底的隔离,常用于容器初始化。结合 Mount Namespace 和 nsenter,可以确保隔离主机文件系统,解决你在 Mount Namespace 中看到主机目录其他文件的问题。本文通过一个简单 Demo 展示了 pivot_root 的使用,确保新根只包含预期内容(如 testfile)。如果你在执行 pivot_root 时遇到问题(如报错或仍看到主机文件),请提供以下信息,我可以进一步协助:

  • pivot_root . old_root 的具体错误信息。
  • unshare shell 中 mount | grep /tmp/mntls /tmp/mnt 的输出。
  • 全局命名空间中 ls /tmp/mnt 的输出(显示哪些“其他文件”)。
  • /proc/16000/ns/mntls -l 输出。

相关文章:

  • Doris + Iceberg 构建冷热分层数据湖架构:架构设计与实战指南
  • 计算机视觉cv2入门之视频处理
  • 编码器---正交编码器
  • 算法—合并排序—js(场景:大数据且需稳定性)
  • 80 7816协议与串口uart协议时序图
  • 【CPU】中断即时性
  • 自然语言处理(9)—— 共现词矩阵及Python实现
  • C#进阶学习(八)常见的泛型数据结构类(3)SortedDictionary<TKey, TValue>与SortedList<TKey, TValue>
  • OJ - 设计循环队列
  • 交换机端口安全
  • C++学习:六个月从基础到就业——内存管理:内存泄漏与避免
  • chili3d调试6 添加左侧面板
  • 【第四十一周】文献阅读:HippoRAG:受神经生物学启发的大型语言模型长期记忆机制
  • OSPF特殊区域
  • 金融图QCPFinancial
  • mac监控linux上mysql性能(Grafana+Prometheus+mysqld_exporter)
  • VSCode PIO使用Jlink SWD烧录Stm32
  • 【C++初阶】第15课—模版进阶
  • 进程与线程:01 CPU管理的直观想法
  • 股票分析技术指标【RSV、KDJ】
  • 北理工再通报:开除宫某党籍,免去行政职务,解除聘用关系
  • 推动中阿合作“向新而行”,这场论坛在上海松江举行
  • 直播电商监管新规将公开征求意见,出重拳净化行业生态
  • 本周看啥|孤独的美食家,蛮好的人生
  • 加力扩围支持消费品以旧换新,江苏要送出1800万元彩票
  • 海外需求增长推动价格飙升,正丹股份去年净利润暴增119倍