unshare 是 Linux 系统中一个底层且强大的命令行工具。它的作用是让一个程序在脱离父进程某些共享环境的情况下运行,是现代容器技术(Docker、Podman)的核心基石。
核心概念:Linux 命名空间(Namespaces)
unshare 本质上是对 Linux 内核 unshare() 系统调用的封装,操作的核心概念是 Linux 命名空间。
在传统 Linux 系统中,所有进程共享同一套全局资源(网络栈、进程号列表、文件系统挂载树等)。Namespaces 机制允许将这些全局资源进行分割和隔离,使不同进程组看到不同的资源视图。
Linux 内核支持以下七种主要命名空间:
| 命名空间 | 隔离内容 |
|---|---|
| Mount (mnt) | 文件系统挂载点。新空间内的挂载/卸载不影响宿主机 |
| PID (pid) | 进程 ID。新空间内第一个进程 PID 为 1,看不到宿主机进程 |
| Network (net) | 网络设备、IP 地址、路由表、防火墙规则等 |
| IPC (ipc) | System V IPC 和 POSIX 消息队列 |
| UTS (uts) | 主机名和域名 |
| User (user) | 用户和用户组 ID。普通用户可在新空间内映射为 root |
| Cgroup (cgroup) | Cgroup 根目录视图 |
unshare 的主要参数
命名空间参数
| 参数 | 对应命名空间 | 作用 |
|---|---|---|
-m, --mount | Mount | 隔离挂载点视图 |
-u, --uts | UTS | 隔离主机名 |
-i, --ipc | IPC | 隔离进程间通信 |
-n, --net | Network | 隔离网络,新环境默认只有 lo 且处于 down 状态 |
-p, --pid | PID | 隔离进程号 |
-U, --user | User | 隔离用户 ID |
-C, --cgroup | Cgroup | 隔离 cgroup 视图 |
关键辅助参数
仅指定命名空间参数还不够,以下三个辅助参数让隔离环境真正可用:
--fork(-f):配合 PID 隔离使用。Linux 内核规定创建新 PID 命名空间的进程本身 PID 不变,只有其子进程才会成为新空间的 PID 1。因此使用-p时必须带上-f。--mount-proc:配合 PID 隔离使用。ps、top等命令通过读取/proc获取进程信息。如果不重新挂载/proc,这些命令仍会显示宿主机进程。此参数会在新的 Mount 命名空间中自动挂载全新的/proc。--map-root-user(-r):配合 User 隔离使用。将当前宿主机用户在新的用户命名空间中映射为 root,获得该空间内的管理员权限。
常见应用场景
1. 隔离主机名(UTS 隔离)
临时修改主机名做测试,不影响宿主机:
sudo unshare -u /bin/bash
hostname test-container
hostname # 显示 test-container2. 干净的网络测试环境(Network 隔离)
sudo unshare -n /bin/bash
ip a # 只有 lo 网卡,完全断网3. 模拟容器环境(PID + Mount 综合隔离)
sudo unshare -p -f --mount-proc /bin/bash
ps aux # 只有 bash 和 ps,bash 的 PID 为 14. 完全隔离的命名空间
一次性隔离全部七种命名空间:
sudo unshare --mount --uts --ipc --net --pid --user --cgroup \
--fork --map-root-user --mount-proc /bin/bash缩写形式:
sudo unshare -muinpUC -f -r --mount-proc /bin/bash验证隔离效果:
ps aux # 只有 bash 和 ps,PID 从 1 开始
ip a # 只有 lo,处于 DOWN 状态
hostname box # 修改主机名不影响宿主机
ls -l /proc/$/ns # 对比宿主机,inode 编号完全不同深入理解:Mount 隔离 ≠ 文件系统隔离
这是一个容易混淆的关键点:unshare -m 隔离的是挂载点列表的视图,而不是文件系统本身。
执行 unshare -m 时,内核将宿主机的整棵挂载树复制一份给新空间。新空间内的挂载/卸载操作不会影响宿主机,但根目录 / 仍然指向宿主机的磁盘分区。
这意味着:
- 修改文件会影响宿主机:
echo "hello" > /root/test.txt会直接写入宿主机磁盘 - 挂载操作不影响宿主机:
mount -t tmpfs none /mnt只在新空间内生效
/proc 为什么是隔离的?
--mount-proc 参数会在新空间内重新挂载 /proc 伪文件系统。/proc 不是硬盘上的真实文件,而是内核在内存中动态生成的接口。当在隔离的 PID 命名空间内挂载 proc 时,内核只暴露属于该命名空间的进程。
结合 chroot 构建真正的隔离容器
要实现像 Docker 一样的文件系统隔离,需要配合 chroot 或 pivot_root 切换根目录。
准备独立的根文件系统
使用轻量级的 Alpine Linux(约 5MB):
mkdir -p /tmp/my-container && cd /tmp/my-container
curl -O https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/x86_64/alpine-minirootfs-3.19.1-x86_64.tar.gz
tar -xvf alpine-minirootfs-3.19.1-x86_64.tar.gz
rm alpine-minirootfs-3.19.1-x86_64.tar.gz启动隔离容器
sudo unshare --mount --uts --ipc --net --pid --user --cgroup \
--fork --map-root-user \
chroot /tmp/my-container /bin/sh进入后手动挂载 /proc:
mount -t proc proc /proc
ps aux # 只有 sh 进程
ls / # Alpine 的文件系统,不再是宿主机的此时在容器内创建的文件只存在于 /tmp/my-container 目录中,不会污染宿主机的根文件系统。
User Namespace 的权限陷阱
使用 --user --map-root-user 时可能遇到 Permission denied,即使文件属于当前用户。
原因:--map-root-user 将你映射为容器内的 UID 0(root),但文件系统检查时,UID 0 与宿主机上文件属主的 UID(如 1000)不匹配,导致权限被拒。
解决方案:
sudo chown -R $(whoami) /tmp/my-container
sudo unshare --mount --uts --ipc --net --pid --fork --mount-proc \
chroot /tmp/my-container /bin/sh总结
unshare 是操纵 Linux 内核 Namespaces 机制的核心工具,也是理解容器技术的钥匙。一个完整的容器可以概括为:
unshare解决"你看得到谁"和"你能用哪个网卡"chroot/pivot_root解决"你能改哪个文件"cgroups解决"你能用多少 CPU 和内存"