差分
このページの2つのバージョン間の差分を表示します。
両方とも前のリビジョン 前のリビジョン 次のリビジョン | 前のリビジョン | ||
linux:kernel:namespace:more_on_pid_namespaces [2013/08/20 11:22] – [PID 名前空間の init プロセス] tenforward | linux:kernel:namespace:more_on_pid_namespaces [2013/08/27 15:12] (現在) – tenforward | ||
---|---|---|---|
行 1: | 行 1: | ||
- | ====== PID 名前空間について更にもう少し ====== | + | ====== |
この記事では,PID 名前空間の先週の議論を続ける (そして名前空間に関する進行中のシリーズを続ける).PID 名前空間の利用法の一つは,パッケージ化したプロセス (コンテナ) を実装することである.これは独立した Linux システムのように振る舞う.伝統的なシステムのキーとなる部分は init プロセスである.そしてこれは PID 名前空間を使ったコンテナでも同様である.なので,init プロセスの特別な役割を調べ,伝統的な init プロセスとは違う点について 1 つ,2 つ述べる.加えて,PID 名前空間に適用する時の名前空間 API の他の詳細を調べる. | この記事では,PID 名前空間の先週の議論を続ける (そして名前空間に関する進行中のシリーズを続ける).PID 名前空間の利用法の一つは,パッケージ化したプロセス (コンテナ) を実装することである.これは独立した Linux システムのように振る舞う.伝統的なシステムのキーとなる部分は init プロセスである.そしてこれは PID 名前空間を使ったコンテナでも同様である.なので,init プロセスの特別な役割を調べ,伝統的な init プロセスとは違う点について 1 つ,2 つ述べる.加えて,PID 名前空間に適用する時の名前空間 API の他の詳細を調べる. | ||
行 60: | 行 60: | ||
===== シグナルと init プロセス ===== | ===== シグナルと init プロセス ===== | ||
- | 伝統的な Linux の init は,シグナルに関して特別な扱いを行う.init に届けられることのできるシグナルは,プロセスがシグナルハンドラを確立したシグナルだけに限られる.他の全てのシグナルは無視される.これにより,その存在がシステムの安定した動きに極めて重要である init プロセスが,スーパーユーザによってでも,誤って kill されるのを防ぐ. | + | 伝統的な Linux の init は,シグナルに関して特別な扱いを行う.init に届けられることのできるシグナルは,(その) |
PID 名前空間は,名前空間個別の init プロセスに対していくつか似た振る舞いを実装する.名前空間内の他のプロセスは (特権プロセスであっても) init プロセスがハンドラを確立したシグナルのみを送ることができる.このことは,名前空間のメンバーが名前空間内で不可欠なプロセスを不注意に kill することを防ぐ.しかし,(伝統的な init プロセスに関しては) カーネルは通常の状況 (ハードウェアの例外,端末の生成する SIGTTOU のようなシグナル,タイマの Expire 等) 全てで,依然として PID 名前空間の init プロセスに対してシグナルを生成することが可能であることに注意が必要である. | PID 名前空間は,名前空間個別の init プロセスに対していくつか似た振る舞いを実装する.名前空間内の他のプロセスは (特権プロセスであっても) init プロセスがハンドラを確立したシグナルのみを送ることができる.このことは,名前空間のメンバーが名前空間内で不可欠なプロセスを不注意に kill することを防ぐ.しかし,(伝統的な init プロセスに関しては) カーネルは通常の状況 (ハードウェアの例外,端末の生成する SIGTTOU のようなシグナル,タイマの Expire 等) 全てで,依然として PID 名前空間の init プロセスに対してシグナルを生成することが可能であることに注意が必要である. | ||
+ | シグナルは (通常のパーミッションのチェックを受け),祖先の PID 名前空間のプロセスから PID 名前空間の init プロセスにも送られる.ここでも,そのシグナルのためのハンドラが設定されたシグナルのみ送る事ができる.ここで 2 つだけ例外があり,SIGKILL と SIGSTOP である.祖先の PID 名前空間内のプロセスがこれらの 2 つのシグナルを init に送った時,シグナルは強制的に配送される (そして受け取られないかもしれない).SIGSTOP シグナルは init プロセスを停止させ,SIGKILL は終了させる.init プロセスは PID 名前空間を機能させるのに必要であるから,もし init プロセスが SIGKILL によって (もしくは他の理由で) 終了した場合,カーネルは SIGKILL シグナルを送り名前空間内の他の全てのプロセスを終了させる. | ||
+ | |||
+ | 通常は PID 名前空間も,空間内の init プロセスが終了する場合には終了する.しかし,通常ではあまりないケースがある.名前空間内のプロセスの一つの / | ||
+ | |||
+ | ===== procfs ファイルシステムのマウント (再考) ===== | ||
+ | |||
+ | このシリーズの先の記事で,PID 名前空間のための /proc ファイルシステム (procfs) を伝統的な /proc マウントポイント以外の他の場所にマウントした.これは,root PID 名前空間内で見えるプロセスを見るために ps コマンドを使うのと同時に,新しい PID 名前空間それぞれと一致する /proc/PID ディレクトリのコンテンツを見るためにシェルコマンドを使えるようにするためである. | ||
+ | |||
+ | しかし ps のようなツールは,必要な情報を取得するために /proc にマウントされた procfs のコンテンツに頼っている.それゆえ,もし PID 名前空間内で正しく ps コマンドを使いたい場合,その名前空間用の procfs をマウントする必要がある.simple_init プログラムはシェルコマンドの実行が可能なので,この処理をコマンドラインから以下のように実行可能である. | ||
+ | |||
+ | < | ||
+ | # ./ | ||
+ | init$ mount -t proc proc /proc | ||
+ | init$ ps a | ||
+ | PID TTY STAT TIME COMMAND | ||
+ | 1 pts/8 S 0:00 ./ | ||
+ | 3 pts/8 R+ 0:00 ps a | ||
+ | </ | ||
+ | |||
+ | ps コマンドは /proc 経由でアクセス可能な全てのプロセスをリストアップしている.この場合,2 つのプロセスのみ見る事ができる.このことからすると,この名前空間では 2 つのプロセスのみが実行されている. | ||
+ | |||
+ | 上記のように ns_child_exec コマンドを実行したとき,プログラムの -m オプションを使っている.これは子プロセス (つまり simple_init で実行するプロセス) を,分離されたマウント名前空間を作成し,その中に置く.結果として,mount コマンドは,名前空間外のプロセスから見える /proc には影響を与えない. | ||
+ | |||
+ | ===== unshare() と setns() ===== | ||
+ | |||
+ | このシリーズの Part.2 の記事で,名前空間 API の一部をなす 2 つのシステムコール unshare() と setns() について説明した.Linux 3.8 以降,これらのシステムコールは PID 名前空間で使用可能になった.しかし,これらのシステムコールは,これらの (訳注: この?) 名前空間で使用する場合は特異な点がいくつかある. | ||
+ | |||
+ | unshare() の呼び出しで CLONE_NEWPID フラグを指定すると新しい PID 名前空間が作られる.しかし,この新しい名前空間内に呼び出し元は入らない.呼び出し元が作成した子プロセスは全て,新しい名前空間内に入る.このような最初の子プロセスは名前空間の init プロセスになるだろう. | ||
+ | |||
+ | setns() システムコールも現在 PID 名前空間をサポートしている. | ||
+ | |||
+ | < | ||
+ | setns(fd, 0); /* 2 つ目の引数は,fd が PID 名前空間を参照するかをチェックするために | ||
+ | | ||
+ | </ | ||
+ | |||
+ | 引数 fd は呼び出し元の PID 名前空間の子孫である PID 名前空間を指すファイルディスクリプタである.ファイルディスクリプタは目的の名前空間内のプロセスの一つに対する / | ||
+ | |||
+ | このシリーズの 2 つ目の記事で紹介した [[http:// | ||
+ | |||
+ | < | ||
+ | ns_run [-f] [-n / | ||
+ | </ | ||
+ | |||
+ | このプログラムは -n オプションで指定した / | ||
+ | |||
+ | ターミナルウィンドウで,通常の実行方法で新しい PID 名前空間内で simple_init プログラムを実行したと仮定しよう.子プロセスを刈り取った時に分かるように冗長なログを取りながら. | ||
+ | |||
+ | < | ||
+ | # ./ | ||
+ | init: my PID is 1 | ||
+ | init$ | ||
+ | </ | ||
+ | |||
+ | そしてここで 2 つ目のターミナルウィンドウにスイッチし,ここで ns_run プログラムで orphan プログラムを実行する.これは simple_init が管理する PID 名前空間内で 2 つプロセスを作成することになる. | ||
+ | |||
+ | < | ||
+ | # ps -C sleep -C simple_init | ||
+ | PID TTY TIME CMD | ||
+ | 9147 pts/8 00:00:00 simple_init | ||
+ | # ./ns_run -f -n / | ||
+ | | ||
+ | | ||
+ | # | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | |||
+ | orphan プログラムが実行されたとき,作成された「親の」プロセス (PID 2) からの出力を見ると,親のプロセス ID が 0 であることが分かる.これは,orphan プロセスを開始させたプロセス (ns_run) が異なる名前空間,つまりその名前空間のメンバーが「親の」プロセスからは見えない事を示している.[[http:// | ||
+ | |||
+ | 以下の図は orphan の「親」プロセスが終了する前のプロセスの関係を示している.矢印はプロセス間の親子関係を示している. | ||
+ | |||
+ | {{: | ||
+ | |||
+ | simple_init プログラムを実行したウィンドウに戻ると,以下のような出力が見える. | ||
+ | |||
+ | < | ||
+ | init: SIGCHLD handler: PID 3 terminated | ||
+ | </ | ||
+ | |||
+ | orphan によって作られた「子の」プロセス (PID 3) は simple_init によって刈り取られる.しかし,「親の」プロセス (PID 2) は刈り取られていない.これは「親の」プロセスは異なる名前空間の自身の親 (ns_run) が刈り取っているからである.以下の図は orphan の「親の」プロセスが終了し,「子」が終了する前のプロセスとその関係を示している. | ||
+ | |||
+ | {{: | ||
+ | |||
+ | setns() と unshare() は PID 名前空間を特別に扱うと強調する価値はある.他のタイプの名前空間に対しては,これらのシステムコールは呼び出し元の名前空間を変更する.これらのシステムコールが呼び出し元の PID 名前空間を変更しない理由は,他の PID 名前空間のメンバーになることは,自身の PID のプロセスを変更させることにつながる.getpid() は,そのプロセスが属する PID 名前空間に関するプロセスの PID を報告するからである.多数のユーザ空間のプログラムやライブラリは (getpid() が報告する) プロセスの PID は一定であるという仮定がある (事実,GNU C ライブラリの getpid() のラッパー関数は PID を[[http:// | ||
+ | |||
+ | ===== 最後に ===== | ||
+ | |||
+ | この記事では,PID 名前空間の init の特別な役割を見て,ps などのツールが使えるようにするためにどのように PID 名前空間に対して procfs をマウントするかを示し,PID 名前空間を使う場合の unshare() と setns() の特殊性をいくつか見た.これで PID 名前空間についての話は完了した.次の記事では,ユーザネームスペースについて見ていく. |