差分
このページの2つのバージョン間の差分を表示します。
両方とも前のリビジョン 前のリビジョン 次のリビジョン | 前のリビジョン | ||
linux:kernel:namespace:the_namespaces_api [2013/07/18 11:31] – [新しい名前空間内で子プロセスを生成する: clone()] tenforward | linux:kernel:namespace:the_namespaces_api [2016/07/12 18:23] (現在) – [名前空間の操作,その 2: 名前空間 API] tenforward | ||
---|---|---|---|
行 3: | 行 3: | ||
名前空間はグローバルなシステムリソースを抽象的なものの中に包みこむ.隔離されたリソースのインスタンスを持つ名前空間内にプロセスを出現させるということである.名前空間は様々な目的に使われる.最も有名なのは軽量の仮想化テクニックであるコンテナの実装である.これは名前空間と名前空間 API の詳細を見ていく記事のシリーズの第 2 部である.この記事では名前空間 API を少し詳細に見ていく.そして多数のサンプルプログラムで実際の API を説明する. | 名前空間はグローバルなシステムリソースを抽象的なものの中に包みこむ.隔離されたリソースのインスタンスを持つ名前空間内にプロセスを出現させるということである.名前空間は様々な目的に使われる.最も有名なのは軽量の仮想化テクニックであるコンテナの実装である.これは名前空間と名前空間 API の詳細を見ていく記事のシリーズの第 2 部である.この記事では名前空間 API を少し詳細に見ていく.そして多数のサンプルプログラムで実際の API を説明する. | ||
- | 名前空間の API は 3 つのシステムコール,clone(), | + | 名前空間の API は 3 つのシステムコール,clone(), |
===== 新しい名前空間内で子プロセスを生成する: | ===== 新しい名前空間内で子プロセスを生成する: | ||
行 83: | 行 83: | ||
このシンボリックリンクの使い方の一つに,2 つのプロセスが同じ名前空間に属しているかどうかを判別する事がある.カーネルは,もし 2 つのプロセスが同じ名前空間に属している場合は,/ | このシンボリックリンクの使い方の一つに,2 つのプロセスが同じ名前空間に属しているかどうかを判別する事がある.カーネルは,もし 2 つのプロセスが同じ名前空間に属している場合は,/ | ||
+ | |||
+ | さて,先の demo_uts_namespace プログラムを実行した前のシェルセッションに戻りましょう.親プロセスと子プロセスの / | ||
+ | |||
+ | < | ||
+ | ^Z # Stop parent and child | ||
+ | [1]+ Stopped | ||
+ | # jobs -l # Show PID of parent process | ||
+ | [1]+ 27513 Stopped | ||
+ | # readlink / | ||
+ | uts: | ||
+ | # readlink / | ||
+ | uts: | ||
+ | </ | ||
+ | |||
+ | 上記のように,/ | ||
+ | |||
+ | / | ||
+ | |||
+ | < | ||
+ | # touch ~/uts # Create mount point | ||
+ | # mount --bind / | ||
+ | </ | ||
+ | |||
+ | Linux 3.8 以前では,/ | ||
+ | |||
+ | ===== 既に存在する名前空間に参加する: | ||
+ | |||
+ | 参加しているプロセスが存在しない名前空間を維持する事は,あとでそこにプロセスを加えるつもりがある場合には有効な事である.これは setns() システムコールの仕事である.このシステムコールを呼び出して,既存の名前空間にプロセスを参加させる事が可能である: | ||
+ | |||
+ | < | ||
+ | int setns(int fd, int nstype); | ||
+ | </ | ||
+ | |||
+ | もっと正確に言うと,setns() は,特定の種類の名前空間のインスタンスから呼び出したプロセスを切り離し,同じ種類の名前空間の他のインスタンスにプロセスを関連付けるということを行う. | ||
+ | |||
+ | fd 引数は参加させたい名前空間を指定する.これは / | ||
+ | |||
+ | nstype 引数で fd が参照する名前空間の種類を呼び出し元でチェックする事が可能である.もし,この引数がゼロと指定された場合は,チェックは行われない.これは,呼び出し時に名前空間の種類を知っている場合か,種類について気にしない場合に役に立つ.この少し後で議論するサンプルプログラム (ns_exec.c) は後者である.つまりどんな種類の名前空間でも動くようにデザインされている.代わりに CLONE_NEW* 定数のうちの 1 つを指定すると,カーネルは fd が指定した名前空間と同じ種類かどうかをチェックする.例えばこれは,呼び出し元が UNIX ドメインソケット経由でファイルディスクリプタを受け取り,参照する名前空間の種類を確認する必要がある場合などに役に立つ. | ||
+ | |||
+ | setns() と execve() (もしくは他の exec() 系の関数) を使用して,シンプルだが役に立つツールを作る事が可能になる: | ||
+ | |||
+ | 我々のプログラム (ns_exec.c, ソースは[[http:// | ||
+ | |||
+ | < | ||
+ | fd = open(argv[1], | ||
+ | |||
+ | setns(fd, 0); /* 名前空間に参加 */ | ||
+ | |||
+ | execvp(argv[2], | ||
+ | </ | ||
+ | |||
+ | 名前空間内で実行すると面白いプログラムはもちろんシェルである.demo_uts_namespaces で作成した新しい UTS 名前空間内でシェルを実行するために, ns_exec プログラムで先に作成した UTS 名前空間に対する bind マウントを使用することができる. | ||
+ | |||
+ | < | ||
+ | # ./ns_exec ~/uts / | ||
+ | My PID is: 28788 | ||
+ | </ | ||
+ | |||
+ | ホスト名と / | ||
+ | |||
+ | < | ||
+ | # hostname | ||
+ | bizarro | ||
+ | # readlink / | ||
+ | uts: | ||
+ | # readlink / | ||
+ | uts: | ||
+ | </ | ||
+ | |||
+ | 以前までのバージョンのカーネルでは,setns() を使って mount, PID, ユーザ名前空間に参加する事はできなかった.しかし 3.8 以降で,全ての名前空間に参加が可能になった. | ||
+ | |||
+ | ===== 名前空間から抜ける: | ||
+ | |||
+ | 名前空間 API の最後のシステムコールは unshare() である. | ||
+ | |||
+ | <code c> | ||
+ | int unshare(int flags); | ||
+ | </ | ||
+ | |||
+ | unshare() システムコールは clone() と同様の機能を提供する.しかし,呼び出したプロセス上での操作となる.つまり,CLONE_NEW* ビットを引数として取り新しい名前空間を作成し,呼び出し元を名前空間のメンバーにする (clone() と同様に,我々がここでは扱わない名前空間の操作以外の機能を提供する).unshare() の主な目的は,(clone() が行うような) 新しいプロセスやスレッドを作る必要なしに名前空間 (や他) の副次的な効果を隔離することである. | ||
+ | |||
+ | clone() システムコールの他の効果はひとまず置いといて, | ||
+ | |||
+ | <code c> | ||
+ | |||
+ | という形式での呼び出しと,名前空間という点で,大体同じとなるのは以下のようなものである. | ||
+ | |||
+ | <code c> | ||
+ | if (fork() == 0) | ||
+ | unshare(CLONE_NEWXXX); | ||
+ | </ | ||
+ | |||
+ | unshare() システムコールの使い方の一つは,unshare コマンドの実装にある.このコマンドは,シェルとは別の名前空間内でコマンドを実行できるというものである.おおまかなコマンドの実行方法は | ||
+ | |||
+ | < | ||
+ | unshare [options] program [arguments] | ||
+ | </ | ||
+ | |||
+ | options は arguments を引数として program を実行する前に unshare する名前空間を指定するコマンドラインのオプションである. | ||
+ | |||
+ | unshare コマンドの実装のキーとなる部分はわかりやすい. | ||
+ | |||
+ | <code c> | ||
+ | /* Code to initialize ' | ||
+ | omitted */ | ||
+ | |||
+ | | ||
+ | |||
+ | /* Now execute ' | ||
+ | of the next command-line argument after options */ | ||
+ | |||
+ | | ||
+ | </ | ||
+ | |||
+ | unshare コマンドのシンプルな実装 (unshare.c) は [[http:// | ||
+ | |||
+ | 以下のシェルのセッションでは,unshare.c を使い,別のマウント名前空間でシェルを実行している.先週の記事で注意したようにマウント名前空間は,プロセスのグループから見えるファイルシステムのマウントポイントの組を隔離するものであり,異なるマウント名前空間内のプロセスは異なったファイルシステム構造が持てるというものである. | ||
+ | |||
+ | < | ||
+ | # echo $$ # シェルの PID を見る | ||
+ | 8490 | ||
+ | # cat / | ||
+ | mqueue /dev/mqueue mqueue rw, | ||
+ | # readlink / | ||
+ | mnt: | ||
+ | # ./unshare -m / | ||
+ | # readlink / | ||
+ | mnt: | ||
+ | </ | ||
+ | |||
+ | 2 度実行した readlink コマンドの出力を比べると,2 つのシェルは別のマウント名前空間にいることが示されている.片方の名前空間のマウントポイントの組を変化させ,他の名前空間で変化が見えるかどうかをチェックすることで,2 つのプログラムが別の名前空間内にいることを別の方法でデモする. | ||
+ | |||
+ | < | ||
+ | # umount / | ||
+ | # cat / | ||
+ | # cat / | ||
+ | mqueue /dev/mqueue mqueue rw, | ||
+ | </ | ||
+ | |||
+ | 最後の 2 つのコマンドの出力から分かるように,/ | ||
+ | |||
+ | ===== 最後に ===== | ||
+ | |||
+ | この記事では,名前空間API の基本的な部分を見て,それをどのように組み合わせて使うかを示した.後に続く記事では,他のいくつかの名前空間でより深い部分を見ていく.特に PID とユーザ名前空間を見る.ユーザ名前空間は,アプリケーションが,従来は特権アプリに限られていたカーネルインターフェースの使用するための新しい可能性を広げるものである. | ||