溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Linux Namespace怎么使用

發(fā)布時(shí)間:2021-12-13 15:11:28 來(lái)源:億速云 閱讀:257 作者:iii 欄目:云計(jì)算

本篇內(nèi)容介紹了“Linux Namespace怎么使用”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

Linux Namespace 是 Linux 提供的一種內(nèi)核級(jí)別環(huán)境隔離的方法。用官方的話來(lái)說(shuō),Linux Namespace 將全局系統(tǒng)資源封裝在一個(gè)抽象中,從而使 namespace 內(nèi)的進(jìn)程認(rèn)為自己具有獨(dú)立的資源實(shí)例。這項(xiàng)技術(shù)本來(lái)沒(méi)有掀起多大的波瀾,是容器技術(shù)的崛起讓他重新引起了大家的注意。

Linux Namespace 有如下 6 個(gè)種類:

分類系統(tǒng)調(diào)用參數(shù)相關(guān)內(nèi)核版本
Mount namespacesCLONE_NEWNSLinux 2.4.19
UTS namespacesCLONE_NEWUTSLinux 2.6.19
IPC namespacesCLONE_NEWIPCLinux 2.6.19
PID namespacesCLONE_NEWPIDLinux 2.6.24
Network namespacesCLONE_NEWNET始于Linux 2.6.24 完成于 Linux 2.6.29
User namespacesCLONE_NEWUSER始于 Linux 2.6.23 完成于 Linux 3.8

namespace 的 API 由三個(gè)系統(tǒng)調(diào)用和一系列 /proc 文件組成,本文將會(huì)詳細(xì)介紹這些系統(tǒng)調(diào)用和 /proc 文件。為了指定要操作的 namespace 類型,需要在系統(tǒng)調(diào)用的 flag 中通過(guò)常量 CLONE_NEW* 指定(包括 CLONE_NEWIPC,CLONE_NEWNS, CLONE_NEWNET,CLONE_NEWPID,CLONE_NEWUSER 和 `CLONE_NEWUTS),可以指定多個(gè)常量,通過(guò) |(位或)操作來(lái)實(shí)現(xiàn)。

簡(jiǎn)單描述一下三個(gè)系統(tǒng)調(diào)用的功能:

  • clone() : 實(shí)現(xiàn)線程的系統(tǒng)調(diào)用,用來(lái)創(chuàng)建一個(gè)新的進(jìn)程,并可以通過(guò)設(shè)計(jì)上述系統(tǒng)調(diào)用參數(shù)達(dá)到隔離的目的。

  • unshare() : 使某進(jìn)程脫離某個(gè) namespace。

  • setns() : 把某進(jìn)程加入到某個(gè) namespace。

具體的實(shí)現(xiàn)原理請(qǐng)往下看。

1. clone()


clone() 的原型如下:

int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);
  • child_func : 傳入子進(jìn)程運(yùn)行的程序主函數(shù)。

  • child_stack : 傳入子進(jìn)程使用的??臻g。

  • flags : 表示使用哪些 CLONE_* 標(biāo)志位。

  • args : 用于傳入用戶參數(shù)。

clone()fork() 類似,都相當(dāng)于把當(dāng)前進(jìn)程復(fù)制了一份,但 clone() 可以更細(xì)粒度地控制與子進(jìn)程共享的資源(其實(shí)就是通過(guò) flags 來(lái)控制),包括虛擬內(nèi)存、打開(kāi)的文件描述符和信號(hào)量等等。一旦指定了標(biāo)志位 CLONE_NEW*,相對(duì)應(yīng)類型的 namespace 就會(huì)被創(chuàng)建,新創(chuàng)建的進(jìn)程也會(huì)成為該 namespace 中的一員。

clone() 的原型并不是最底層的系統(tǒng)調(diào)用,而是封裝過(guò)的,真正的系統(tǒng)調(diào)用內(nèi)核實(shí)現(xiàn)函數(shù)為 do_fork(),形式如下:

long do_fork(unsigned long clone_flags,
	      unsigned long stack_start,
	      unsigned long stack_size,
	      int __user *parent_tidptr,
	      int __user *child_tidptr)

其中 clone_flags 可以賦值為上面提到的標(biāo)志。

下面來(lái)看一個(gè)例子:

/* demo_uts_namespaces.c

   Copyright 2013, Michael Kerrisk
   Licensed under GNU General Public License v2 or later

   Demonstrate the operation of UTS namespaces.
*/
#define _GNU_SOURCE
#include <sys/wait.h>
#include <sys/utsname.h>
#include <sched.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

/* A simple error-handling function: print an error message based
   on the value in 'errno' and terminate the calling process */

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)

static int              /* Start function for cloned child */
childFunc(void *arg)
{
    struct utsname uts;

    /* 在新的 UTS namespace 中修改主機(jī)名 */

    if (sethostname(arg, strlen(arg)) == -1)
        errExit("sethostname");

    /* 獲取并顯示主機(jī)名 */

    if (uname(&uts) == -1)
        errExit("uname");
    printf("uts.nodename in child:  %s\n", uts.nodename);

    /* Keep the namespace open for a while, by sleeping.
       This allows some experimentation--for example, another
       process might join the namespace. */
     
    sleep(100);

    return 0;           /* Terminates child */
}

/* 定義一個(gè)給 clone 用的棧,棧大小1M */
#define STACK_SIZE (1024 * 1024) 

static char child_stack[STACK_SIZE];

int
main(int argc, char *argv[])
{
    pid_t child_pid;
    struct utsname uts;

    if (argc < 2) {
        fprintf(stderr, "Usage: %s <child-hostname>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* 調(diào)用 clone 函數(shù)創(chuàng)建一個(gè)新的 UTS namespace,其中傳出一個(gè)函數(shù),還有一個(gè)??臻g(為什么傳尾指針,因?yàn)闂J欠粗模?
       新的進(jìn)程將在用戶定義的函數(shù) childFunc() 中執(zhí)行 */

    child_pid = clone(childFunc, 
                    child_stack + STACK_SIZE,   /* 因?yàn)闂J欠粗模?nbsp;
                                                   所以傳尾指針 */ 
                    CLONE_NEWUTS | SIGCHLD, argv[1]);
    if (child_pid == -1)
        errExit("clone");
    printf("PID of child created by clone() is %ld\n", (long) child_pid);

    /* Parent falls through to here */

    sleep(1);           /* 給子進(jìn)程預(yù)留一定的時(shí)間來(lái)改變主機(jī)名 */

    /* 顯示當(dāng)前 UTS namespace 中的主機(jī)名,和 
       子進(jìn)程所在的 UTS namespace 中的主機(jī)名不同 */

    if (uname(&uts) == -1)
        errExit("uname");
    printf("uts.nodename in parent: %s\n", uts.nodename);

    if (waitpid(child_pid, NULL, 0) == -1)      /* 等待子進(jìn)程結(jié)束 */
        errExit("waitpid");
    printf("child has terminated\n");

    exit(EXIT_SUCCESS);
}

該程序通過(guò)標(biāo)志位 CLONE_NEWUTS 調(diào)用 clone() 函數(shù)創(chuàng)建一個(gè) UTS namespace。UTS namespace 隔離了兩個(gè)系統(tǒng)標(biāo)識(shí)符 — 主機(jī)名NIS 域名 —它們分別通過(guò) sethostname()setdomainname() 這兩個(gè)系統(tǒng)調(diào)用來(lái)設(shè)置,并通過(guò)系統(tǒng)調(diào)用 uname() 來(lái)獲取。

下面將對(duì)程序中的一些關(guān)鍵部分進(jìn)行解讀(為了簡(jiǎn)單起見(jiàn),我們將省略其中的錯(cuò)誤檢查)。

程序運(yùn)行時(shí)后面需要跟上一個(gè)命令行參數(shù),它將會(huì)創(chuàng)建一個(gè)在新的 UTS namespace 中執(zhí)行的子進(jìn)程,該子進(jìn)程會(huì)在新的 UTS namespace 中將主機(jī)名改為命令行參數(shù)中提供的值。

主程序的第一個(gè)關(guān)鍵部分是通過(guò)系統(tǒng)調(diào)用 clone() 來(lái)創(chuàng)建子進(jìn)程:

child_pid = clone(childFunc, 
                  child_stack + STACK_SIZE,   /* Points to start of 
                                                 downwardly growing stack */ 
                  CLONE_NEWUTS | SIGCHLD, argv[1]);

printf("PID of child created by clone() is %ld\n", (long) child_pid);

子進(jìn)程將會(huì)在用戶定義的函數(shù) childFunc() 中開(kāi)始執(zhí)行,該函數(shù)將會(huì)接收 clone() 最后的參數(shù)(argv[1])作為自己的參數(shù),并且標(biāo)志位包含了 CLONE_NEWUTS,所以子進(jìn)程會(huì)在新創(chuàng)建的 UTS namespace 中執(zhí)行。

接下來(lái)主進(jìn)程睡眠一段時(shí)間,讓子進(jìn)程能夠有時(shí)間更改其 UTS namespace 中的主機(jī)名。然后調(diào)用 uname() 來(lái)檢索當(dāng)前 UTS namespace 中的主機(jī)名,并顯示該主機(jī)名:

sleep(1);           /* Give child time to change its hostname */

uname(&uts);
printf("uts.nodename in parent: %s\n", uts.nodename);

與此同時(shí),由 clone() 創(chuàng)建的子進(jìn)程執(zhí)行的函數(shù) childFunc() 首先將主機(jī)名改為命令行參數(shù)中提供的值,然后檢索并顯示修改后的主機(jī)名:

sethostname(arg, strlen(arg);
    
uname(&uts);
printf("uts.nodename in child:  %s\n", uts.nodename);

子進(jìn)程退出之前也睡眠了一段時(shí)間,這樣可以防止新的 UTS namespace 不會(huì)被關(guān)閉,讓我們能夠有機(jī)會(huì)進(jìn)行后續(xù)的實(shí)驗(yàn)。

執(zhí)行程序,觀察父進(jìn)程和子進(jìn)程是否處于不同的 UTS namespace 中:

$ su                   # 需要特權(quán)才能創(chuàng)建 UTS namespace
Password: 
# uname -n
antero
# ./demo_uts_namespaces bizarro
PID of child created by clone() is 27514
uts.nodename in child:  bizarro
uts.nodename in parent: antero

除了 User namespace 之外,創(chuàng)建其他的 namespace 都需要特權(quán),更確切地說(shuō),是需要相應(yīng)的 Linux Capabilities,即 CAP_SYS_ADMIN。這樣就可以避免設(shè)置了 SUID(Set User ID on execution)的程序因?yàn)橹鳈C(jī)名不同而做出一些愚蠢的行為。如果對(duì) Linux Capabilities 不是很熟悉,可以參考我之前的文章:Linux Capabilities 入門教程:概念篇。

2. proc 文件


每個(gè)進(jìn)程都有一個(gè) /proc/PID/ns 目錄,其下面的文件依次表示每個(gè) namespace, 例如 user 就表示 user namespace。從 3.8 版本的內(nèi)核開(kāi)始,該目錄下的每個(gè)文件都是一個(gè)特殊的符號(hào)鏈接,鏈接指向 $namespace:[$namespace-inode-number],前半部份為 namespace 的名稱,后半部份的數(shù)字表示這個(gè) namespace 的句柄號(hào)。句柄號(hào)用來(lái)對(duì)進(jìn)程所關(guān)聯(lián)的 namespace 執(zhí)行某些操作。

$ ls -l /proc/$$/ns         # $$ 表示當(dāng)前所在的 shell 的 PID
total 0
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 net -> net:[4026531956]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 pid -> pid:[4026531836]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 user -> user:[4026531837]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 uts -> uts:[4026531838]

這些符號(hào)鏈接的用途之一是用來(lái)確認(rèn)兩個(gè)不同的進(jìn)程是否處于同一 namespace 中。如果兩個(gè)進(jìn)程指向的 namespace inode number 相同,就說(shuō)明他們?cè)谕粋€(gè) namespace 下,否則就在不同的 namespace 下。這些符號(hào)鏈接指向的文件比較特殊,不能直接訪問(wèn),事實(shí)上指向的文件存放在被稱為 nsfs 的文件系統(tǒng)中,該文件系統(tǒng)用戶不可見(jiàn),可以使用系統(tǒng)調(diào)用 stat() 在返回的結(jié)構(gòu)體的 st_ino 字段中獲取 inode number。在 shell 終端中可以用命令(實(shí)際上就是調(diào)用了 stat())看到指向文件的 inode 信息:

$ stat -L /proc/$$/ns/net
  File: /proc/3232/ns/net
  Size: 0         	Blocks: 0          IO Block: 4096   regular empty file
Device: 4h/4d	Inode: 4026531956  Links: 1
Access: (0444/-r--r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-01-17 15:45:23.783304900 +0800
Modify: 2020-01-17 15:45:23.783304900 +0800
Change: 2020-01-17 15:45:23.783304900 +0800
 Birth: -

除了上述用途之外,這些符號(hào)鏈接還有其他的用途,如果我們打開(kāi)了其中一個(gè)文件,那么只要與該文件相關(guān)聯(lián)的文件描述符處于打開(kāi)狀態(tài),即使該 namespace 中的所有進(jìn)程都終止了,該 namespace 依然不會(huì)被刪除。通過(guò) bind mount 將符號(hào)鏈接掛載到系統(tǒng)的其他位置,也可以獲得相同的效果:

$ touch ~/uts
$ mount --bind /proc/27514/ns/uts ~/uts

3. setns()


加入一個(gè)已經(jīng)存在的 namespace 可以通過(guò)系統(tǒng)調(diào)用 setns() 來(lái)完成。它的原型如下:

int setns(int fd, int nstype);

更確切的說(shuō)法是:setns() 將調(diào)用的進(jìn)程與特定類型 namespace 的一個(gè)實(shí)例分離,并將該進(jìn)程與該類型 namespace 的另一個(gè)實(shí)例重新關(guān)聯(lián)。

  • fd 表示要加入的 namespace 的文件描述符,可以通過(guò)打開(kāi)其中一個(gè)符號(hào)鏈接來(lái)獲取,也可以通過(guò)打開(kāi) bind mount 到其中一個(gè)鏈接的文件來(lái)獲取。

  • nstype 讓調(diào)用者可以去檢查 fd 指向的 namespace 類型,值可以設(shè)置為前文提到的常量 CLONE_NEW*,填 0 表示不檢查。如果調(diào)用者已經(jīng)明確知道自己要加入了 namespace 類型,或者不關(guān)心 namespace 類型,就可以使用該參數(shù)來(lái)自動(dòng)校驗(yàn)。

結(jié)合 setns()execve() 可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單但非常有用的功能:將某個(gè)進(jìn)程加入某個(gè)特定的 namespace,然后在該 namespace 中執(zhí)行命令。直接來(lái)看例子:

/* ns_exec.c 

   Copyright 2013, Michael Kerrisk
   Licensed under GNU General Public License v2 or later

   Join a namespace and execute a command in the namespace
*/
#define _GNU_SOURCE
#include <fcntl.h>
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

/* A simple error-handling function: print an error message based
   on the value in 'errno' and terminate the calling process */

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)

int
main(int argc, char *argv[])
{
    int fd;

    if (argc < 3) {
        fprintf(stderr, "%s /proc/PID/ns/FILE cmd [arg...]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    fd = open(argv[1], O_RDONLY);   /* 獲取想要加入的 namespace 的文件描述符 */
    if (fd == -1)
        errExit("open");

    if (setns(fd, 0) == -1)         /* 加入該 namespace */
        errExit("setns");

    execvp(argv[2], &argv[2]);      /* 在加入的 namespace 中執(zhí)行相應(yīng)的命令 */
    errExit("execvp");
}

該程序運(yùn)行需要兩個(gè)或兩個(gè)以上的命令行參數(shù),第一個(gè)參數(shù)表示特定的 namespace 符號(hào)鏈接的路徑(或者 bind mount 到這些符號(hào)鏈接的文件路徑);第二個(gè)參數(shù)表示要在該符號(hào)鏈接相對(duì)應(yīng)的 namespace 中執(zhí)行的程序名稱,以及執(zhí)行這個(gè)程序所需的命令行參數(shù)。關(guān)鍵步驟如下:

fd = open(argv[1], O_RDONLY);   /* 獲取想要加入的 namespace 的文件描述符 */

setns(fd, 0);                   /* 加入該 namespace */

execvp(argv[2], &argv[2]);      /* 在加入的 namespace 中執(zhí)行相應(yīng)的命令 */

還記得我們之前已經(jīng)通過(guò) bind mount 將 demo_uts_namespaces 創(chuàng)建的 UTS namespace 掛載到 ~/uts 中了嗎?可以將本例中的程序與之結(jié)合,讓新進(jìn)程可以在該 UTS namespace 中執(zhí)行 shell:

    $ ./ns_exec ~/uts /bin/bash     # ~/uts 被 bind mount 到了 /proc/27514/ns/uts
    My PID is: 28788

驗(yàn)證新的 shell 是否與 demo_uts_namespaces 創(chuàng)建的子進(jìn)程處于同一個(gè) UTS namespace:

$ hostname
bizarro
$ readlink /proc/27514/ns/uts
uts:[4026532338]
$ readlink /proc/$$/ns/uts      # $$ 表示當(dāng)前 shell 的 PID
uts:[4026532338]

在早期的內(nèi)核版本中,不能使用 setns() 來(lái)加入 mount namespace、PID namespace 和 user namespace,從 3.8 版本的內(nèi)核開(kāi)始,setns() 支持加入所有的 namespace。

util-linux 包里提供了nsenter 命令,其提供了一種方式將新創(chuàng)建的進(jìn)程運(yùn)行在指定的 namespace 里面,它的實(shí)現(xiàn)很簡(jiǎn)單,就是通過(guò)命令行(-t 參數(shù))指定要進(jìn)入的 namespace 的符號(hào)鏈接,然后利用 setns() 將當(dāng)前的進(jìn)程放到指定的 namespace 里面,再調(diào)用 clone() 運(yùn)行指定的執(zhí)行文件。我們可以用 strace 來(lái)看看它的運(yùn)行情況:

# strace nsenter -t 27242 -i -m -n -p -u /bin/bash
execve("/usr/bin/nsenter", ["nsenter", "-t", "27242", "-i", "-m", "-n", "-p", "-u", "/bin/bash"], [/* 21 vars */]) = 0
…………
…………
pen("/proc/27242/ns/ipc", O_RDONLY)    = 3
open("/proc/27242/ns/uts", O_RDONLY)    = 4
open("/proc/27242/ns/net", O_RDONLY)    = 5
open("/proc/27242/ns/pid", O_RDONLY)    = 6
open("/proc/27242/ns/mnt", O_RDONLY)    = 7
setns(3, CLONE_NEWIPC)                  = 0
close(3)                                = 0
setns(4, CLONE_NEWUTS)                  = 0
close(4)                                = 0
setns(5, CLONE_NEWNET)                  = 0
close(5)                                = 0
setns(6, CLONE_NEWPID)                  = 0
close(6)                                = 0
setns(7, CLONE_NEWNS)                   = 0
close(7)                                = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f4deb1faad0) = 4968

4. unshare()


最后一個(gè)要介紹的系統(tǒng)調(diào)用是 unshare(),它的原型如下:

int unshare(int flags);

unshare()clone() 類似,但它運(yùn)行在原先的進(jìn)程上,不需要?jiǎng)?chuàng)建一個(gè)新進(jìn)程,即:先通過(guò)指定的 flags 參數(shù) CLONE_NEW* 創(chuàng)建一個(gè)新的 namespace,然后將調(diào)用者加入該 namespace。最后實(shí)現(xiàn)的效果其實(shí)就是將調(diào)用者從當(dāng)前的 namespace 分離,然后加入一個(gè)新的 namespace。

Linux 中自帶的 unshare 命令,就是通過(guò) unshare() 系統(tǒng)調(diào)用實(shí)現(xiàn)的,使用方法如下:

$ unshare [options] program [arguments]

options 指定要?jiǎng)?chuàng)建的 namespace 類型。

unshare 命令的主要實(shí)現(xiàn)如下:

/* 通過(guò)提供的命令行參數(shù)初始化 'flags' */

unshare(flags);

/* Now execute 'program' with 'arguments'; 'optind' is the index
   of the next command-line argument after options */

execvp(argv[optind], &argv[optind]);

unshare 命令的完整實(shí)現(xiàn)如下:

/* unshare.c 

   Copyright 2013, Michael Kerrisk
   Licensed under GNU General Public License v2 or later

   A simple implementation of the unshare(1) command: unshare
   namespaces and execute a command.
*/

#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

/* A simple error-handling function: print an error message based
   on the value in 'errno' and terminate the calling process */

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)

static void
usage(char *pname)
{
    fprintf(stderr, "Usage: %s [options] program [arg...]\n", pname);
    fprintf(stderr, "Options can be:\n");
    fprintf(stderr, "    -i   unshare IPC namespace\n");
    fprintf(stderr, "    -m   unshare mount namespace\n");
    fprintf(stderr, "    -n   unshare network namespace\n");
    fprintf(stderr, "    -p   unshare PID namespace\n");
    fprintf(stderr, "    -u   unshare UTS namespace\n");
    fprintf(stderr, "    -U   unshare user namespace\n");
    exit(EXIT_FAILURE);
}

int
main(int argc, char *argv[])
{
    int flags, opt;

    flags = 0;

    while ((opt = getopt(argc, argv, "imnpuU")) != -1) {
        switch (opt) {
        case 'i': flags |= CLONE_NEWIPC;        break;
        case 'm': flags |= CLONE_NEWNS;         break;
        case 'n': flags |= CLONE_NEWNET;        break;
        case 'p': flags |= CLONE_NEWPID;        break;
        case 'u': flags |= CLONE_NEWUTS;        break;
        case 'U': flags |= CLONE_NEWUSER;       break;
        default:  usage(argv[0]);
        }
    }

    if (optind >= argc)
        usage(argv[0]);

    if (unshare(flags) == -1)
        errExit("unshare");

    execvp(argv[optind], &argv[optind]);  
    errExit("execvp");
}

下面我們執(zhí)行 unshare.c 程序在一個(gè)新的 mount namespace 中執(zhí)行 shell:

$ echo $$                             # 顯示當(dāng)前 shell 的 PID
8490
$ cat /proc/8490/mounts | grep mq     # 顯示當(dāng)前 namespace 中的某個(gè)掛載點(diǎn)
mqueue /dev/mqueue mqueue rw,seclabel,relatime 0 0
$ readlink /proc/8490/ns/mnt          # 顯示當(dāng)前 namespace 的 ID 
mnt:[4026531840]
$ ./unshare -m /bin/bash              # 在新創(chuàng)建的 mount namespace 中執(zhí)行新的 shell
$ readlink /proc/$$/ns/mnt            # 顯示新 namespace 的 ID 
mnt:[4026532325]

對(duì)比兩個(gè) readlink 命令的輸出,可以知道兩個(gè)shell 處于不同的 mount namespace 中。改變新的 namespace 中的某個(gè)掛載點(diǎn),然后觀察兩個(gè) namespace 的掛載點(diǎn)是否有變化:

$ umount /dev/mqueue                  # 移除新 namespace 中的掛載點(diǎn)
$ cat /proc/$$/mounts | grep mq       # 檢查是否生效
$ cat /proc/8490/mounts | grep mq     # 查看原來(lái)的 namespace 中的掛載點(diǎn)是否依然存在?
mqueue /dev/mqueue mqueue rw,seclabel,relatime 0 0

可以看出,新的 namespace 中的掛載點(diǎn) /dev/mqueue 已經(jīng)消失了,但在原來(lái)的 namespace 中依然存在。

“Linux Namespace怎么使用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI