文書の過去の版を表示しています。
名前空間の操作,その 3: PID 名前空間
先の 2 つの記事に続いて,これからは PID 名前空間を見ていく.PID 名前空間によって隔離されるグローバルリソースはプロセス ID 番号の空間である.これは,異なる PID 名前空間に属するプロセスは同じプロセス ID を持つことが可能になるという事である.PID 名前空間は,コンテナ内のプロセスは,同じプロセス ID を持ったまま,ホスト間を移動可能なコンテナの実装に使われる.
伝統的な Linux (や UNIX) システム上のプロセスと同様に,PID 名前空間内ではプロセス ID はユニークであり,PID 1 から始まり順に割り当てられる.さらに,伝統的な Linux システムと同じく,PID 1 (init プロセス) は特別である.これは名前空間内で作られる最初のプロセスであり,名前空間内での特定の管理タスクを実行する.
最初の調査
新しい PID 名前空間は CLONE_NEWPID フラグを指定して clone() を呼ぶことで作られる.ここで clone() を使って新しい PID 名前空間を作る簡単なサンプルプログラムを示そう.このプログラムを使って,PID 名前空間の基本コンセプトのいくつかをまとめよう.プログラム (pidns_init_sleep.c) のソース全体は ここ にある.このシリーズの前の記事と同様に,簡潔化のために,記事中では完全なソースでは入っているエラーチェックのコードを省いている.
main プログラムは clone() を使った新しい PID 名前空間を作成し,その結果生成された子供の PID を表示する.
child_pid = clone(childFunc, child_stack + STACK_SIZE, /* Points to start of downwardly growing stack */ CLONE_NEWPID | SIGCHLD, argv[1]); printf("PID returned by clone(): %ld\n", (long) child_pid);
新しい子プロセスは childFunc() 内で実行を開始する.これは clone() の最後の引数 (argv[1]) を自身の引数として受け取る.この引数の目的は後で明らかにする.
chileFunc() 関数はプロセス ID と clone() が作成した子プロセスの親プロセスの ID を表示する.そして,標準の sleep プログラムを実行して終了する.
printf("childFunc(): PID = %ld\n", (long) getpid()); printf("ChildFunc(): PPID = %ld\n", (long) getppid()); ... execlp("sleep", "sleep", "1000", (char *) NULL);
sleep プログラムを実行する目的は,プロセスのリストで親から子プロセスを区別する事を簡単にするためである.
このプログラムを実行すると,出力の最初の行は以下のようになる.
$ su # Need privilege to create a PID namespace Password: # ./pidns_init_sleep /proc2 PID returned by clone(): 27656 childFunc(): PID = 1 childFunc(): PPID = 0 Mounting procfs at /proc2
pidns_init_sleep の出力の最初の 2 行は,2 つの異なった PID 名前空間の見方から子プロセスの PID を示している.clone() を呼び出した側の名前空間と名前空間内の子プロセス側からである.言い換えると,子プロセスは 2 つの PID を持つということである.一つは親の名前空間の 27656,もう 1 つは clone() によって作られた PID 名前空間内の 1 である.
出力の次の行は,PID 名前空間内の子供側からのコンテキスト内での,子供の親プロセスの ID を示している (getppid() の返り値).親の PID は 0 である.PID 名前空間の少し変わった所を示している.あとで述べるように,PID 名前空間は階層構造を形成している.プロセスは自身の PID 名前空間内と,その PID 名前空間以下にネストした子供の名前空間内に含まれるプロセスのみを見ることができる.clone() が作成した子供の親は異なる名前空間にいるので,子供は親を見ることができない.このため,getppid() はゼロとして親の PID を返すのである.
pidns_init_sleep の出力の最後の行の説明のため,childFunc() 関数の実装の議論の時にスキップした部分のコードに戻る必要がある.