[英]'unshare' does not work as expected in C api
這一系列命令有效:
unshare --fork --pid --mount
umount /proc
mount -t proc proc /proc
umount /dev/pts
mount -t devpts devpts /dev/pts
但是,相應的C程序沒有按預期工作(似乎它沒有卸載以前的/ proc,並且它還提供了嘗試卸載devpts的EBUSY):
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")
之后,它會打印已掛載的文件系統)。
盡管你的評論
“有時”
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)子shell中umount
proc,則會因“設備忙”而失敗 - 這與C程序嘗試執行的操作類似。
當我退出子shell時,腳本的其余部分會運行, umount
和兩個mount
失敗。 這是可以預料的,因為主腳本共享其mount命名空間。
完全似乎/proc
真的很忙,因此即使對於具有mount命名空間的私有副本的進程也無法卸載。 很可能這樣的進程本身正在使用它的/proc
掛載的私有副本。 相比之下,我發現我可以在具有非共享mount命名空間的進程中成功卸載/dev/pts
,但不能在共享該命名空間的系統副本的進程中。
我發現問題檢查unshare命令的源代碼 。 必須使用MS_PRIVATE | MS_REC
卸載/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”),它產生一個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));
unshare bash!= unshare c
unshare - 運行程序,其中一些名稱空間與父項不共享
所以基本上使用--fork你是用/ bin / sh / / bin / sh(無論你執行你的腳本)用--pid和--mount選項分配的。 “fork”后跟“unshare”
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看到。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.