这个命令序列工作:
unshare --fork --pid --mount
umount /proc
mount -t proc proc /proc
umount /dev/pts
mount -t devpts devpts /dev/pts
但是,相应的 C 程序没有按预期工作(似乎它没有卸载以前的/proc,并且它还提供了尝试卸载 devpts 的 EBUS):
unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
int status;
waitpid(-1, &status, 0);
return status;
}
printf("My pid: %in", getpid()); // It prints 1 as expected
umount("/proc"); // Returns 0
system("mount"); // Should print error on mtab, but it prints the previous mounted filesystems
mount("proc", "/proc", "proc",
MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV,
NULL)); // Returns 0
umount("/dev/pts"); // Returns -1 errno = 0 (??)
mount("devpts", "/dev/pts", "devpts",
MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV,
NULL) ); // Returns -1 errno = EBUSY
我在这里省略了可读性的错误检查
我认为取消共享或卸载无法按预期工作:即使它返回零,似乎也不会卸载/proc(如果我尝试在那之后执行system("mount")
,它会打印挂载的文件系统)。
尽管你的评论
"有时"
umount
返回 0 "有时" -1,但最终它根本不卸载/proc
,在您的 pastebin 代码的 10000 次试用中,umount()
对我来说总是失败,返回-1
而不是卸载/proc
. 我不愿意相信尽管未能执行请求的卸载,umount()
会返回0
,但如果确实如此,那将构成umount()
中的错误。 如果您实际上可以证实这样的错误,那么社区意识的回应将是针对 glibc 提交错误报告。
那么问题就变成了您的bash
脚本为什么以及如何表现不同。 然而,事实上,它似乎并没有这样做。
首先,您对unshare(1)
命令有错误的期望。 与 unshare(2)
函数不同,unshare
命令不会影响在其中执行它的 shell。 相反,它会启动一个单独的进程,该进程具有指定命名空间的专用副本。 通常,您会在unshare
命令行上指定启动该进程的命令,实际上程序的手册页表明这样做是强制性的。
根据经验,我发现如果我未能像您一样指定这样的命令,那么unshare
启动一个新的 shell 作为目标进程。 特别是,当我运行您的脚本(具有足够的权限使用 unshare
)时,我立即收到一个新提示,但它是新 shell 的提示,在前台运行。 这对我来说是显而易见的,因为提示是不同的(但是,在这种情况下,您的提示可能没有任何不同)。 此时没有来自umount
的错误消息等,因为它尚未运行。 如果我手动尝试在 (unshare
d) 子壳中umount
proc,它会失败并显示"设备繁忙"——这是您的 C 程序尝试执行的操作的模拟。
当我退出子外壳时,脚本的其余部分将运行,umount
s 和两个 mount
都失败。 这是意料之中的,因为主脚本共享其挂载命名空间。
/proc
确实很忙,因此无法卸载,即使对于具有挂载命名空间的私有副本的进程,也是完全合理的。 这样的过程很可能本身正在使用其/proc
挂载的私人副本。 相比之下,我发现我可以在具有未共享的挂载命名空间的进程中成功卸载/dev/pts
,但在共享该命名空间的系统副本的进程中则不能成功卸载
我发现检查取消共享命令的源代码时出现问题。/proc
必须使用 MS_PRIVATE | MS_REC
卸载并在没有它们的情况下挂载,这本质上是为了确保挂载仅在当前(新)命名空间中有效。第二个问题是,如果不对全局命名空间产生影响,就无法卸载/dev/pts
(这是由 devpts 驱动程序的内部例程引起的)。要拥有一个私有的/dev/pts,唯一的方法是使用专用的 -o newinstance
选项挂载它。最后/dev/ptmx
也应绑定重新安装。
因此,这是预期的 C 工作代码:
unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
int status;
waitpid(-1, &status, 0);
return status;
}
printf("New PID after unshare is %i", getpid());
if (mount("none", "/proc", NULL, MS_PRIVATE|MS_REC, NULL)) {
printf("Cannot umount proc! errno=%i", errno);
exit(1);
}
if (mount("proc", "/proc", "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL)) {
printf("Cannot mount proc! errno=%i", errno);
exit(1);
}
if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL | MS_NOSUID | MS_NOEXEC, "newinstance") ) {
printf("Cannot mount pts! errno=%i", errno);
exit(1);
}
if (mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_BIND, NULL) ) {
printf("Cannot mount ptmx! errno=%i", errno);
exit(1);
}
问题出在系统("mount")上,它生成了一个外壳并且不会承载umount。尝试在卸载后打开/proc/中的文件,看看它按预期工作。
看到这里 -
unshare(CLONE_NEWPID | CLONE_NEWNS );
int rc = 0;
int pid = fork();
if (pid != 0) {
int status;
waitpid(-1, &status, 0);
return status;
}
printf(">>> My pid: %dn", getpid()); // It prints 1 as expected
rc = umount2("/proc", MNT_FORCE); // Returns 0
printf(">>> umount returned %d. errno = %d, desc = (%s)n", rc, errno, strerror(errno));
rc = open("/proc/cpuinfo", O_RDONLY);
printf(">>> open returned %d. errno = %d, desc = (%s)n", rc, errno, strerror(errno));
unshare bash != unshare c
取消共享 - 使用某些与父级取消共享的命名空间运行程序
所以基本上使用 --fork 你可以使用 --pid 和 --mount 选项从/bin/bash 的/bin/sh 分叉(无论你用什么执行脚本)。"分叉"后跟"取消共享"
取消共享 - 取消关联部分流程执行上下文(当前进程)您正在从 init 取消共享,然后分叉。
CLONE_NEWPID"克隆"标志不是"取消共享"
因此,根据您要实现的目标 - 我假设您正在尝试使"/proc"和"/dev/pts"专用于儿童过程。
下面是一个挂载 --bind 本地文件夹的小例子:
# mkdir mnt point
# touch point/point.txt
# mount --bind point mnt
# ls mnt
point.txt
# ./unshare
My pid: 28377
Child:
point.txt
Parent:
# ls mnt
法典:
#define _GNU_SOURCE
#include <sched.h>
int main(int argc, char *argv[])
{
/** umount global */
system("umount mnt/");
int pid = fork();
if (pid != 0) {
int status;
waitpid(-1, &status, 0);
printf("Parent:n");
/* and here we don't */
system("ls mnt/");
return status;
}
/* unshare */
unshare(CLONE_FS | CLONE_NEWNS);
printf("My pid: %in", getpid()); // It prints 1 as expected
/* mount exclusively */
system("mount --bind point/ mnt/");
printf("Child:n");
/* here we see it */
system("ls mnt/");
return 0;
}
bash还有一个很好的例子:http://karelzak.blogspot.ru/2009/12/unshare1.html
延续:
mount依赖于/etc/mtab,它并不总是指向/proc/mount 的符号链接
所以用 ls -la 检查/etc/mtab。
还要检查/dev/pts 上的 umount 代码:
int ret = umount("/dev/pts");
int errsv = errno;
if(ret == -1) {
printf("Error on umount: %sn", strerror(errsv));
}
我很确定它被使用了 - 用热熔器/dev/pts/检查它
**编辑**
最后 - 我不确定您只能在命名空间中卸载 procfs(我认为这是不可能的)
但是您可以在命名空间中挂载自己的 procfs 副本:
# mount -t proc proc /proc/
现在只有您的过程可以通过 ps -e 看到。