20

这一系列命令有效:

unshare --fork --pid --mount 
umount /proc
mount -t proc proc /proc
umount /dev/pts
mount -t devpts devpts /dev/pts

但是,相应的 C 程序没有按预期工作(似乎它没有卸载以前的 /proc,并且它还提供 EBUSY 试图卸载 devpts):

unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
    int status;
    waitpid(-1, &status, 0);
    return status;
}

printf("My pid: %i\n", 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

我在这里省略了错误检查以提高可读性

我认为 unshare 或 unmount 不能按预期工作:即使它返回零,似乎也不会卸载 /proc (如果我尝试在此system("mount")之后执行 a ,它会打印已安装的文件系统)。

4

4 回答 4

2

尽管你的评论是

"sometimes"返回 0 "sometimes" -1,但最终它根本umount没有卸载/proc

,在您的 pastebin 代码的 10000 次试验中,对我来说umount() 总是失败,返回-1而不是卸载/proc。尽管未能执行请求的卸载,但我不愿意相信它会umount()返回0,但如果它确实如此,那么这将构成umount(). 如果您实际上可以证实这样的错误,那么社区意识的回应将是针对 glibc 提交错误报告。


bash然后问题就变成了您的脚本为何以及如何表现不同。然而,事实上,它似乎并没有。

首先,您对unshare(1)命令有错误的期望。与unshare(2)函数不同的是,该unshare命令不会影响执行它的 shell。相反,它会启动一个单独的进程,该进程拥有自己的指定命名空间的私有副本。通常,您会在命令行上指定启动该进程的unshare命令,实际上程序的手册页表明这样做是强制性的。

根据经验,我发现如果我没有指定这样的命令——就像你做的那样——然后unshare启动一个新的 shell 作为目标进程。特别是当我运行你的脚本时(有足够的权限使用unshare),我立即得到一个新的提示,但它是新shell的提示,在前台运行。这对我来说很明显,因为提示不同(但是,在这些情况下,您的提示可能没有任何不同)。从那时起没有错误消息等umount,因为它还没有运行。如果我手动尝试umount在 ( unshared) 子shell 中执行,它会因“设备忙”而失败——这与您的 C 程序尝试执行的操作类似。

当我退出子外壳时,脚本的其余部分运行,umounts 和mounts 都失败了。这是意料之中的,因为主脚本共享其挂载命名空间。


完全有可能/proc确实很忙,因此无法卸载,即使对于具有挂载命名空间的私有副本的进程也是如此。这样的进程本身很可能正在使用其挂载的私有副本/proc。相反,我发现我可以/dev/pts在具有非共享挂载命名空间的进程中成功卸载,但不能在共享该命名空间的系统副本的进程中卸载。

于 2016-01-27T19:24:06.717 回答
1

我在检查unshare 命令的源代码时发现了问题。/proc必须在没有它们的情况下卸载MS_PRIVATE | MS_REC和安装,这本质上是为了确保安装仅在当前(新)命名空间中有效。第二个问题是无法在/dev/pts不影响全局命名空间的情况下进行卸载(这是由 devpts 驱动程序的内部例程引起的)。要拥有私有的 /dev/pts ,唯一的方法是使用 dedicated-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);
}
于 2016-01-30T21:48:27.543 回答
0

我认为问题出在系统(“mount”)上,它产生了一个 shell 并且没有携带 umount。尝试在 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: %d\n", 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));
于 2016-01-27T16:38:12.257 回答
-1

取消分享 bash != 取消分享 c

unshare - 使用一些与父级未共享的命名空间运行程序

所以基本上使用 --fork 你是从 /bin/sh 的 /bin/bash (无论你用什么执行脚本)与 --pid 和 --mount 选项分叉。“分叉”后跟“取消共享”

unshare - 取消部分流程执行上下文(当前流程) 您从 init 取消共享,然后分叉。

CLONE_NEWPID 是“克隆”标志而不是“取消共享”

因此,取决于您要实现的目标 - 我假设您正在尝试使“/proc”和“/dev/pts”专用于子进程。

这是 mount --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: %i\n", 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/mounts 的符号链接

所以用 ls -la 检查 /etc/mtab。

还要检查 /dev/pts 上的 umount 代码:

int ret = umount("/dev/pts");
int errsv = errno;
if(ret == -1) {
  printf("Error on umount: %s\n", strerror(errsv));
}

我很确定它已被使用 - 使用 fuser /dev/pts/ 检查它

** 已编辑 **

最后-我不确定您是否只能在命名空间中卸载 procfs(我认为这是不可能的)

但您可以在命名空间中挂载自己的 procfs 副本:

# mount -t proc proc /proc/

现在只有你的进程通过 ps -e 可见。

于 2016-01-25T08:16:27.310 回答