簡體   English   中英

'unshare'在C api中無法正常工作

[英]'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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM