独自の仮想化コンテナ#4 PIDネーミングスペースの作成


📖 概要


今日、仮想化コンテナを実装するための最も基本的なPID 네임스페이스テクノロジーを使用します.
前回の投稿で述べたJNIコードの作成は、すべての練習が完了した後に続行されます.また、#2 기초 이론ではこれらの概念が1編で議論されているため、詳細に説明されていない内容に重点を置いて議論する.

📦 PIDネーミングスペースとは?


PIDネーミングスペースの詳細については、こちらを参考にしてください!を参照してください.
PIDネーミングスペースは、特定のプロセスを他のプロセスから分離し、自分がinitプロセスだと思っているようにします.

🔧 体験する


🖥 実験環境:Ubuntu 20.04 LTS、AMD 64アーキテクチャ

1.unshareコマンドの使用

unshare -fp /bin/bash
上記のコマンドを使用すると、新しいbashウィンドウが開きます.
  • unshare:新しいネーミングスペースを作成し、ドライバのコマンド
  • -fp
  • fオプション:フォークリフト特定プログラムはunshareプロセスのサブプロセスで駆動されます.
  • このオプションがない場合、unshareは特定のプログラム
  • を直接駆動します.
  • pオプション:PIDネーミングスペースの作成
  • /bin/bash:bash shellを分離するプログラムを指定
  • 2.差異の比較


    これで、別のネーミングスペースで分離bash shellが実行されます.
    現在の状況では、プログラムは自分をinitプロセスと認識します.pstreeコマンドでチェックできますか?
    pstree
    うん.しかし不思議なことにbash shellではない他のプログラムも一緒に現れます.
    何か問題がありますか.
    プロセス分離に失敗したと思うかもしれませんが、実際には正常です.

    3.PIDネーミングスペースの問題


    まず,왜 이러한 현상이 일어나는가?PIDネーミング空間の役割を理解するには,pstreeの動作原理も理解しなければならない.
    まず,PIDネーミングスペースは,특정 프로세스가 스스로를 init 프로세스라고 인식하도록(他のプロセスにアクセスできない)を分離する機能にすぎない.
    その後、プロセスクエリ命令(pstreeまたはpsコマンドなど)は、/procというディレクトリで識別され、出力される.
    何か気づいたか?
    はい、bash shellのPIDネーミングスペースだけを隔離しました.
    したがって、bash shellから発行されたpstreeコマンドは、ネーミングスペース以外の/procディレクトリをスキャンし、bash shell標準のpstreeではなく外部標準を出力します.
    もっと簡単に言えば...
  • bashのPIDネーミングスペースのみを分離します(つまり、インストールネーミングスペースは既存のネーミングスペースを使用しています!)
  • bash実行pstree
  • マウントネーミングスペースが隔離されていないため、pstreeは外部(既存のファイルシステム)上の/procディレクトリ
  • にアクセスする.
  • 外部条件下の/procディレクトリであるため、外部条件下のpstree
  • が描画される
  • 私たちはこれを見て、隔離に失敗したと勘違いするかもしれません!
  • これはどうやって解決しますか?

    4.mount-procオプション


    実際、Linux開発者もこれらの問題を知っているので、これらの問題を解決するためのオプションを別途作成しました.mount-procという名前のオプションで、このオプションの基礎(?)私たちはまだmountオプション(ネーミングスペースをマウント)を勉強していないので、簡単に紹介します.
    問題のフォルダを別のインストールネーミングスペースに分離するオプション
    そう思えばいい
    もしそうであれば、このオプションを使用してbash shellを再度開きます.
    unshare -fp --mount-proc /bin/bash
    pstreeコマンドを再入力しましょうか?
    pstree
    次の結果が表示される可能性があります.
    bash───pstree
    独立したbash shell条件からpstreeを取得することに成功しました.C言語で/procを記述しましょう.

    プログラムの作成と実行


    コードの作成


    以前、LinuxのコマンドShell環境では、격리를 자동화하는 프로그램コマンドで名前空間を管理できました.では、C言語ではどうなるのでしょうか.
    C言語はタイトルファイルunshareを提供している.コードファイルを作成するには、次の手順に従います.
    注意:次のコードは、Githubの|45915|、|Repositoryのコードを参照して作成されます.
    #define _GNU_SOURCE
    
    #include <stdio.h> // 표준 입출력
    #include <sched.h> // unshare() 메소드 및 flags
    #include <unistd.h> // execl() 메소드
    #include <sys/wait.h> // wait() 메소드
    #include <sys/mount.h> // mount() 메소드
    
    // unshare 함수를 위한 인자
    // PID 네임스페이스(p 옵션), FS 네임스페이스(mount를 위한 옵션)
    const int flags = CLONE_NEWPID | CLONE_NEWNS;
    
    // bash를 실행하는 함수
    void spawnShell() {
        execl("/bin/bash", "bash", (char*)NULL);
    }
    
    // '/proc' 디렉터리를 재마운트하는 함수
    int remountProc() {
        if(mount("none", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL)) return -1;
        return 0;
    }
    
    int main() {
        // 네임스페이스 구성
        if(unshare(flags) == -1) {
            printf("Unshare failed!\n");
            return -1;
        }
        printf("unshared process successfully\n");
        
        // 자식 프로세스 생성(분기점)
        int forkResponse = fork();
        
        // 현 프로세스의 종류에 따라 기능 분리
        switch(forkResponse) {
            case 0: // 자식 프로세스일 때
                printf("현재 자식 프로세스입니다 / PID : %d\n", getpid());
    
                if(remountProc() == -1) {
                    printf("Mounting /proc failed!");
                    return -1;
                }
                printf("Mount successed!\n");
                
                printf("Spawning /bin/bash :\n");
                spawnShell(); // '/bin/bash' 실행
    
                break;
            case -1: // 부모 프로세스에서 오류 발생 시
                printf("fork() 실행 중 오류 발생\n");
                break;
            default: // 부모 프로세스일 때(자식 PID 반환)
                printf("부모(PID %d)로부터 자식 프로세스(PID %d) 생성\n", getpid(), forkResponse);
                wait(NULL); // 중요: 자식 프로세스(/bin/bash)가 종료될 때까지 대기함.
                
                printf("Child process has exited.\n\n");
        }
    
        return 0;
    }

    コアコード


    sched.hの旗

    // unshare 함수를 위한 인자
    // PID 네임스페이스(p 옵션), FS 네임스페이스(mount를 위한 옵션)
    const int flags = CLONE_NEWPID | CLONE_NEWNS;
    ...
    
    
    int main() {
        // 네임스페이스 구성
        if(unshare(flags) == -1) {
        	...
    ご覧のように、util-linux/util-linuxメソッドを呼び出すと、パラメータ値はweary/unshareppを超えます.unshareを実行すると、このパラメータはどのネーミングスペースを作成するかを決定します.
    ただし、unshareの初期化値を表示すると、複数の定数のビット演算が表示されます.どうしてですか.
    JAVAのようにオブジェクト向け言語を使用する人は、通常、unshareをメソッドに渡すときにクラスを作成して渡します.形式は次のとおりです.
    class Config {
    	boolean CLONE_NEWPID;
        boolean CLONE_NEWNS;
    }
    しかし、C言語ではクラスはサポートされておらず、代替材料としてflagsが存在するが、実施の特性上、メモリの浪費が深刻である.したがって,C言語では主にビット演算によるflagが用いられる.
    通常、整数は4バイト、整数は32ビットです.したがって、合計32個の「真/偽」フィールドを表示できます.
    サンプルコードでは、オプションflagsおよびBool만으로 이루어진 여러 설정값flag.どちらのオプションもBit OR演算で、最終的なflag値は次のようになります.
      00100000000000000000000000000000 (CLONE_NEWPID)
    + 00000000000000100000000000000000 (CLONE_NEWNS)
      ―――――――――――――――――――――――――――――――――――――
    = 00100000000000100000000000000000

    構造体呼び出し

    // 자식 프로세스 생성(분기점)
    int forkResponse = fork();
        
    // 현 프로세스의 종류에 따라 기능 분리
    switch(forkResponse) {
    	...
    CLONE_NEWPIDメソッドは、プロセス自体のメモリをコピーすることによってサブプロセスを作成します.その後、親プロセスはCLONE_NEWNSを返し、子プロセスはforkを返します.
    画像として表示:

    実行例


    次のコマンドを使用してコードをコンパイルして実行します.
    sudo su & gcc -o myContainer 코드파일명.c && ./myContainer
    この操作を行うと、次の画面が表示されます.
    unshared process successfully
    부모(PID <부모 PID>)로부터 자식 프로세스(PID <자식 PID>) 생성
    현재 자식 프로세스입니다 / PID : 1
    Mount successed!
    Spawning /bin/bash :
    root@<호스트명>:/<디렉터리명># 
    今からpstreeコマンドを使いましょうか?
    bash───pstree
    以前にコマンドで実装されたfork()bashと同様に、bashが생성된 자식 프로세스의 PIDと認識されていることがわかります.

    👏 の最後の部分


    申し訳ございませんが、この文章には約3週間かかりました.実際、書くこと自体にはあまり時間がかかりませんでした.学校のビデオ編集の仕事と私の怠惰なので、完成が遅いです.これからはあまり時間がかかりません.
    次の投稿では、0について説明します.
    ありがとうございます.