老熟女激烈的高潮_日韩一级黄色录像_亚洲1区2区3区视频_精品少妇一区二区三区在线播放_国产欧美日产久久_午夜福利精品导航凹凸

重慶分公司,新征程啟航

為企業提供網站建設、域名注冊、服務器等服務

LinuxNamespace怎么使用

本篇內容介紹了“Linux Namespace怎么使用”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

十年的洮南網站建設經驗,針對設計、前端、開發、售后、文案、推廣等六對一服務,響應快,48小時及時工作處理。成都全網營銷推廣的優勢是能夠根據用戶設備顯示端的尺寸不同,自動調整洮南建站的顯示方式,使網站能夠適用不同顯示終端,在瀏覽器中調整網站的寬度,無論在任何一種瀏覽器上瀏覽網站,都能展現優雅布局與設計,從而大程度地提升瀏覽體驗。創新互聯建站從事“洮南網站設計”,“洮南網站推廣”以來,每個客戶項目都認真落實執行。

Linux Namespace 是 Linux 提供的一種內核級別環境隔離的方法。用官方的話來說,Linux Namespace 將全局系統資源封裝在一個抽象中,從而使 namespace 內的進程認為自己具有獨立的資源實例。這項技術本來沒有掀起多大的波瀾,是容器技術的崛起讓他重新引起了大家的注意。

Linux Namespace 有如下 6 個種類:

分類系統調用參數相關內核版本
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 由三個系統調用和一系列 /proc 文件組成,本文將會詳細介紹這些系統調用和 /proc 文件。為了指定要操作的 namespace 類型,需要在系統調用的 flag 中通過常量 CLONE_NEW* 指定(包括 CLONE_NEWIPCCLONE_NEWNSCLONE_NEWNETCLONE_NEWPIDCLONE_NEWUSER 和 `CLONE_NEWUTS),可以指定多個常量,通過 |(位或)操作來實現。

簡單描述一下三個系統調用的功能:

  • clone(): 實現線程的系統調用,用來創建一個新的進程,并可以通過設計上述系統調用參數達到隔離的目的。

  • unshare(): 使某進程脫離某個 namespace。

  • setns(): 把某進程加入到某個 namespace。

具體的實現原理請往下看。

1. clone()


clone() 的原型如下:

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

  • child_stack: 傳入子進程使用的棧空間。

  • flags: 表示使用哪些 CLONE_* 標志位。

  • args: 用于傳入用戶參數。

clone()fork() 類似,都相當于把當前進程復制了一份,但 clone() 可以更細粒度地控制與子進程共享的資源(其實就是通過 flags 來控制),包括虛擬內存、打開的文件描述符和信號量等等。一旦指定了標志位 CLONE_NEW*,相對應類型的 namespace 就會被創建,新創建的進程也會成為該 namespace 中的一員。

clone() 的原型并不是最底層的系統調用,而是封裝過的,真正的系統調用內核實現函數為 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 可以賦值為上面提到的標志。

下面來看一個例子:

/* 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 
#include 
#include 
#include 
#include 
#include 
#include 

/* 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 中修改主機名 */

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

    /* 獲取并顯示主機名 */

    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 */
}

/* 定義一個給 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 \n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* 調用 clone 函數創建一個新的 UTS namespace,其中傳出一個函數,還有一個棧空間(為什么傳尾指針,因為棧是反著的);
       新的進程將在用戶定義的函數 childFunc() 中執行 */

    child_pid = clone(childFunc, 
                    child_stack + STACK_SIZE,   /* 因為棧是反著的, 
                                                   所以傳尾指針 */ 
                    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);           /* 給子進程預留一定的時間來改變主機名 */

    /* 顯示當前 UTS namespace 中的主機名,和 
       子進程所在的 UTS namespace 中的主機名不同 */

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

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

    exit(EXIT_SUCCESS);
}

該程序通過標志位 CLONE_NEWUTS 調用 clone() 函數創建一個 UTS namespace。UTS namespace 隔離了兩個系統標識符 — 主機名NIS 域名—它們分別通過 sethostname()setdomainname() 這兩個系統調用來設置,并通過系統調用 uname() 來獲取。

下面將對程序中的一些關鍵部分進行解讀(為了簡單起見,我們將省略其中的錯誤檢查)。

程序運行時后面需要跟上一個命令行參數,它將會創建一個在新的 UTS namespace 中執行的子進程,該子進程會在新的 UTS namespace 中將主機名改為命令行參數中提供的值。

主程序的第一個關鍵部分是通過系統調用 clone() 來創建子進程:

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);

子進程將會在用戶定義的函數 childFunc() 中開始執行,該函數將會接收 clone() 最后的參數(argv[1])作為自己的參數,并且標志位包含了 CLONE_NEWUTS,所以子進程會在新創建的 UTS namespace 中執行。

接下來主進程睡眠一段時間,讓子進程能夠有時間更改其 UTS namespace 中的主機名。然后調用 uname() 來檢索當前 UTS namespace 中的主機名,并顯示該主機名:

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

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

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

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

子進程退出之前也睡眠了一段時間,這樣可以防止新的 UTS namespace 不會被關閉,讓我們能夠有機會進行后續的實驗。

執行程序,觀察父進程和子進程是否處于不同的 UTS namespace 中:

$ su                   # 需要特權才能創建 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 之外,創建其他的 namespace 都需要特權,更確切地說,是需要相應的 Linux Capabilities,即 CAP_SYS_ADMIN。這樣就可以避免設置了 SUID(Set User ID on execution)的程序因為主機名不同而做出一些愚蠢的行為。如果對 Linux Capabilities 不是很熟悉,可以參考我之前的文章:Linux Capabilities 入門教程:概念篇。

2. proc 文件


每個進程都有一個 /proc/PID/ns 目錄,其下面的文件依次表示每個 namespace, 例如 user 就表示 user namespace。從 3.8 版本的內核開始,該目錄下的每個文件都是一個特殊的符號鏈接,鏈接指向 $namespace:[$namespace-inode-number],前半部份為 namespace 的名稱,后半部份的數字表示這個 namespace 的句柄號。句柄號用來對進程所關聯的 namespace 執行某些操作。

$ ls -l /proc/$$/ns         # $$ 表示當前所在的 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]

這些符號鏈接的用途之一是用來確認兩個不同的進程是否處于同一 namespace 中。如果兩個進程指向的 namespace inode number 相同,就說明他們在同一個 namespace 下,否則就在不同的 namespace 下。這些符號鏈接指向的文件比較特殊,不能直接訪問,事實上指向的文件存放在被稱為 nsfs 的文件系統中,該文件系統用戶不可見,可以使用系統調用 stat() 在返回的結構體的 st_ino 字段中獲取 inode number。在 shell 終端中可以用命令(實際上就是調用了 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: -

除了上述用途之外,這些符號鏈接還有其他的用途,如果我們打開了其中一個文件,那么只要與該文件相關聯的文件描述符處于打開狀態,即使該 namespace 中的所有進程都終止了,該 namespace 依然不會被刪除。通過 bind mount 將符號鏈接掛載到系統的其他位置,也可以獲得相同的效果:

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

3. setns()


加入一個已經存在的 namespace 可以通過系統調用 setns() 來完成。它的原型如下:

int setns(int fd, int nstype);

更確切的說法是:setns() 將調用的進程與特定類型 namespace 的一個實例分離,并將該進程與該類型 namespace 的另一個實例重新關聯。

  • fd 表示要加入的 namespace 的文件描述符,可以通過打開其中一個符號鏈接來獲取,也可以通過打開 bind mount 到其中一個鏈接的文件來獲取。

  • nstype 讓調用者可以去檢查 fd 指向的 namespace 類型,值可以設置為前文提到的常量 CLONE_NEW*,填 0 表示不檢查。如果調用者已經明確知道自己要加入了 namespace 類型,或者不關心 namespace 類型,就可以使用該參數來自動校驗。

結合 setns()execve() 可以實現一個簡單但非常有用的功能:將某個進程加入某個特定的 namespace,然后在該 namespace 中執行命令。直接來看例子:

/* 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 
#include 
#include 
#include 
#include 

/* 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 中執行相應的命令 */
    errExit("execvp");
}

該程序運行需要兩個或兩個以上的命令行參數,第一個參數表示特定的 namespace 符號鏈接的路徑(或者 bind mount 到這些符號鏈接的文件路徑);第二個參數表示要在該符號鏈接相對應的 namespace 中執行的程序名稱,以及執行這個程序所需的命令行參數。關鍵步驟如下:

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

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

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

還記得我們之前已經通過 bind mount 將 demo_uts_namespaces 創建的 UTS namespace 掛載到 ~/uts 中了嗎?可以將本例中的程序與之結合,讓新進程可以在該 UTS namespace 中執行 shell:

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

驗證新的 shell 是否與 demo_uts_namespaces 創建的子進程處于同一個 UTS namespace:

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

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

util-linux 包里提供了nsenter 命令,其提供了一種方式將新創建的進程運行在指定的 namespace 里面,它的實現很簡單,就是通過命令行(-t 參數)指定要進入的 namespace 的符號鏈接,然后利用 setns() 將當前的進程放到指定的 namespace 里面,再調用 clone() 運行指定的執行文件。我們可以用 strace 來看看它的運行情況:

# 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()


最后一個要介紹的系統調用是 unshare(),它的原型如下:

int unshare(int flags);

unshare()clone() 類似,但它運行在原先的進程上,不需要創建一個新進程,即:先通過指定的 flags 參數 CLONE_NEW* 創建一個新的 namespace,然后將調用者加入該 namespace。最后實現的效果其實就是將調用者從當前的 namespace 分離,然后加入一個新的 namespace。

Linux 中自帶的 unshare 命令,就是通過 unshare() 系統調用實現的,使用方法如下:

$ unshare [options] program [arguments]

options 指定要創建的 namespace 類型。

unshare 命令的主要實現如下:

/* 通過提供的命令行參數初始化 '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 命令的完整實現如下:

/* 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 
#include 
#include 
#include 

/* 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");
}

下面我們執行 unshare.c 程序在一個新的 mount namespace 中執行 shell:

$ echo $$                             # 顯示當前 shell 的 PID
8490
$ cat /proc/8490/mounts | grep mq     # 顯示當前 namespace 中的某個掛載點
mqueue /dev/mqueue mqueue rw,seclabel,relatime 0 0
$ readlink /proc/8490/ns/mnt          # 顯示當前 namespace 的 ID 
mnt:[4026531840]
$ ./unshare -m /bin/bash              # 在新創建的 mount namespace 中執行新的 shell
$ readlink /proc/$$/ns/mnt            # 顯示新 namespace 的 ID 
mnt:[4026532325]

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

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

可以看出,新的 namespace 中的掛載點 /dev/mqueue 已經消失了,但在原來的 namespace 中依然存在。

“Linux Namespace怎么使用”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注創新互聯網站,小編將為大家輸出更多高質量的實用文章!


文章名稱:LinuxNamespace怎么使用
本文路徑:http://www.xueling.net.cn/article/jhgjjh.html

其他資訊

在線咨詢
服務熱線
服務熱線:028-86922220
TOP
主站蜘蛛池模板: 国产精品网站在线看 | 久久久久久久久成人网A片 国产成年视频 | 亚洲日韩欧美一区 | 8x在线观看| 国产精品呻吟久久av图片 | 麻豆国产原创 | 国产无遮挡一区二区三区 | 欧美精品日韩一区 | 日韩国产欧美一区二区 | youjizz丰满熟妇中国 | 北条麻妃最猛aⅴ作品 | 欧美成人午夜免费影院 | 国产成人8x视频网站入口 | 99pao成人国产永久免费视频 | 国产情人综合久久777777 | 99蜜久久精品国产首页 | 国产午夜精华无码网站 | 超碰人人草人人干 | 精品国产成人亚洲午夜福利 | 午夜香蕉成视频人网站 | 福利看片盒子永久国产 | 欧美激情精品久久 | 91精品国产?合久久久欧美 | 中字一区 | 噜噜噜视频在线观看 | 午夜免费无码福利视频 | 免费在线激情视频 | 国产综合久久 | 小嫩妇好紧好爽再快视频 | 亚洲中文字幕不卡无码 | 97毛片| 精品国品一二三产品区别在线观看 | 国产不卡av在线播放 | 精品久久精品久久 | 亚洲制服丝无码中文在线 | 国产乱码久久久 | 久久久999精品免费 国产欧美久久久久 | 凤隐天下60集全免费播放在线观看 | 一级免费av| 国产在线亚州精品内射 | 日韩理论片中文字幕 |