这一系列命令有效:
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")
之后,它会打印已挂载的文件系统)
答案 0 :(得分:2)
尽管您的评论
"有时"
umount
返回0"有时" -1,但最后它并没有完全卸载/proc
,在您对pastebin代码的10000次试用中,umount()
总是对我失败,返回-1
而不是卸载/proc
。我不愿意相信尽管umount()
未能执行请求的卸载,0
仍然会返回umount()
,但如果它确实如此,那将构成bash
中的错误。如果你事实上可以证实这样的错误,那么社区意识的回应就是提交针对glibc的错误报告。
然后问题就变成了unshare(1)
脚本行为不同的原因和方式。事实上,它似乎没有。
首先,您对unshare(2)
命令的期望是错误的。与unshare
函数不同,unshare
命令不会影响执行它的shell。相反,它启动一个单独的进程,它有自己的指定名称空间的私有副本。通常,您将在unshare
命令行上指定启动该进程的命令,实际上程序的手册页指示这样做是强制性的。
根据经验,我发现如果我没有像你那样指定这样的命令,那么unshare
会启动一个新的shell作为目标进程。特别是,当我运行你的脚本(有足够的权限使用umount
)时,我立即得到一个新的提示,但它是在前台运行的新shell的提示。这对我来说很明显,因为提示不同(但在这种情况下,你的提示可能没有任何不同)。此时umount
没有错误消息等,因为尚未运行。如果我在(unshare
d)子shell中手动尝试umount
proc,则会失败并且"设备正忙碌" - 这是你的C程序尝试做的类比。
当我退出子shell时,脚本的其余部分会运行,mount
和/proc
都失败。这是可以预料的,因为主脚本共享其mount命名空间。
/proc
确实很忙,因此即使对于具有mount命名空间的私有副本的进程也无法卸载,这是完全合理的。这样的进程本身可能正在使用/dev/pts
的挂载的私有副本。相反,我发现我可以在具有非共享安装命名空间的进程中成功卸载{{1}},但不能在共享该命名空间的系统副本的进程中。< / p>
答案 1 :(得分:1)
我发现问题正在检查source code of unshare command。必须使用/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);
}
答案 2 :(得分: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));
答案 3 :(得分:-1)
unshare bash!= unshare c
unshare - 运行程序,其中一些名称空间与父项取消共享
所以基本上--fork你用/ bin / sh / / bin / sh(无论你执行你的脚本)都用--pid和--mount选项。 &#34;叉&#34;然后是&#34; unshare&#34;
取消共享 - 取消关联流程执行上下文的部分(当前进程) 你从初始化中取消共享,然后分叉。
CLONE_NEWPID是&#34;克隆&#34;不是#34; unshare&#34;
因此,取决于你想要实现的目标 - 我假设你正在尝试制作&#34; / proc&#34;和&#34; / dev / pts&#34;专属于儿童过程。
这是mount -bind local folders的一个小例子:
# 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看到。