PID 名前空間はプロセス ID 番号空間を分離する。 これは、異なる PID 名前空間のプロセスは同じ PID を持つことができることを意味する。 PID 名前空間を使うことで、コンテナー内のプロセス群を中断、再開したり、 コンテナー内のプロセスの PID を保持したままコンテナーを新しいホストに移行したりするといった機能をコンテナーが提供することが可能になる。
新しい PID 名前空間の PID は、 独立したシステムであるかのように、 1 から始まる。 fork(2), vfork(2), clone(2) を呼び出すと、 その名前空間内で一意な PID でプロセスが生成される。
PID 名前空間を使用するには、設定 CONFIG_PID_NS が有効になったカーネルが必要である。
PID 名前空間の "init" プロセスが終了すると、 カーネルはその名前空間の全プロセスを SIGKILL シグナルで終了する。 この動作は、 PID 名前空間の正しい操作のためには "init" プロセスは不可欠であるという事実を反映したものである。 この場合、 その PID 名前空間へのそれ以降の fork(2) はエラー ENOMEM で失敗する。 "init" プロセスが終了している PID 名前空間に新しいプロセスを作成することはできない。 このような状況は、 例えば、 名前空間にいたプロセスに対応する /proc/[pid]/ns/pid ファイルに対してオープンしたファイルディスクリプターを使って、 "init" プロセスが終了した後にその名前空間に setns(2) を行った場合に起こり得る。 unshare(2) を呼び出した後にも、この状況は起こり得る。 それ以降に fork(2) で作成された最初の子プロセスが終了すると、 それ以降の fork(2) の呼び出しは NOMEM で失敗する。
PID 名前空間の他のメンバーは、 "init" プロセスがシグナルハンドラーを設定したシグナルだけを、 "init" プロセスに送信することができる。 この制限は特権プロセスに対しても適用される。 この制限により、 PID 名前空間の他のメンバーがうっかり "init" プロセスを殺してしまうのを防ぐことができる。
同様に、 先祖の名前空間のプロセスは、 "init" プロセスがそのシグナルに対するハンドラーを設定している場合にのみ、 kill(2) で説明されている通常のアクセス許可のチェックを経た上で、 子供の PID 名前空間の "init" プロセスにシグナルを送信できる。 (ハンドラー内では、 sigaction(2) に説明がある siginfo_t の si_pid フィールドは 0 になる。) SIGKILL と SIGSTOP は例外として扱われ、 これらのシグナルが先祖の PID 名前空間から送信された場合には強制的に配送される。 これらのシグナルはどちらも "init" プロセルが捕捉することはできない。 そのため、これらのシグナルに関連付けられた通常のアクション (それぞれ、プロセスの終了とプロセスの強制停止) が実行される。
Linux 3.4 以降では、 reboot(2) システムコールを呼び出すと、 シグナルがその名前空間の "init" プロセスに送信される。 詳細は reboot(2) を参照。
プロセスは、所属する PID 名前空間の他のプロセスから見える。また、 root PID 名前空間に向かう直径の先祖の各 PID 名前空間のプロセスからも見える。 この場合、「見える」とは、 あるプロセスが、 他のプロセスがプロセス ID を指定するシステムコールを使う際に操作の対象にできることを意味する。 逆に、子供 PID 名前空間のプロセスから親や先祖の名前空間のプロセスは見えない。 あるプロセスは自分自身の PID 名前空間とその子孫の名前空間のプロセスだけが見える (例えば、kill(2) でシグナルを送信したり、 setpriority(2) で nice 値を設定したり、など)。
プロセスは、そのプロセスが見える PID 名前空間の階層の各層においてプロセス ID を一つ持ち、 直接の先祖の名前空間を辿ることで通って root PID 名前空間に至ることができる。 プロセス ID に対して操作を行うシステムコールは、常に、呼び出し元プロセスの PID 名前空間で見えるプロセス ID を使って操作を行う。 getpid(2) の呼び出しでは、 常に、 プロセスが作成された名前空間に関連付けられた PID を返す。
PID 名前空間内のプロセスは名前空間の外部に親プロセスを持つことができる。 例えば、その名前空間の初期プロセス (すなわち PID 1 を持つ init(1) プロセス) の親プロセスは必然的に別の名前空間に属すことになる。 同様に、 あるプロセスが setns(2) を使って子プロセスを PID 名前空間に参加させた場合、 子プロセスは setns(2) の呼び出し元とは異なる PID 名前空間に属す。 子プロセスで getppid(2) を呼び出すと 0 が返される。
プロセスは (setns(2) を CLONE_NEWPID で使うなどで) 子供の PID 名前空間に自由に入ることができるが、 逆の方向には移動できない。 つまり、 プロセスは先祖の名前空間 (親、親の親など) に入ることはできない。 PID 名前空間の変更は一方向の操作である。
別の言い方をすると、 あるプロセスがどの PID 名前空間に所属するかは、 そのプロセスが作成されたときに決定され、 それ以降は変更されることはない。 いろいろあるが、プロセス間の親子関係には、PID 名前空間の親子関係がそのまま反映されるということだ。 プロセスの親プロセスは、同じ名前空間にいるか、もしくは直接の親 PID 名前空間にいるかのいずれかである。
まとめると、 CLONE_THREAD, CLONE_SIGHAND, CLONE_VM では技術的な要件として PID 名前空間が共有されている点がある。 (さらに clone(2) では CLONE_THREAD か CLONE_SIGHAND が指定された際には CLONE_VM が指定されている必要がある点にも注意。) したがって、以下のような順序で呼び出しを行うと (エラー EINVAL で) 失敗する。
unshare(CLONE_NEWPID); clone(..., CLONE_VM, ...); /* Fails */ setns(fd, CLONE_NEWPID); clone(..., CLONE_VM, ...); /* Fails */ clone(..., CLONE_VM, ...); setns(fd, CLONE_NEWPID); /* Fails */ clone(..., CLONE_VM, ...); unshare(CLONE_NEWPID); /* Fails */
新しい PID 名前空間を作成した後、 子プロセスが、自身の root ディレクトリを変更し、新しい procfs インスタンスを /proc にマウントするのは ps(1) などのツールが正しく動作するためにも有用である。 clone(2) の flags 引き数に CLONE_NEWNS も指定されて新しいマウント名前空間が同時に作成された場合は、 root ディレクトリを変更する必要はない。 新しい procfs インスタンスを /proc にそのままマウントすることができる。
シェルから、コマンドで /proc のマウントを行うには次のようにする。
$ mount -t proc proc /proc
パス /proc/self に対して readlink(2) を呼び出すと、 procfs のマウントを行ったプロセスの PID 名前空間におけるプロセス ID が得られる。 これは調査目的でプロセスが他の名前空間で自身の PID を知りたい場合などに役立つ。
[man1]
[man2]
[man3]
[man4]
[man5]
[man6]
[man7]
[man8]
[a]
[b]
[c]
[d]
[e]
[f]
[g]
[h]
[i]
[j]
[k]
[l]
[m]
[n]
[o]
[p]
[q]
[r]
[s]
[t]
[u]
[v]
[w]
[x]
[y]
[z]