プロセスIDの取得
Rev.1を表示中。最新版はこちら。
プロセスIDはtsk->nsproxy->pid_ns下のPIDネームスペースの元で管理されていて、alloc_pid関数でPIDを割り当てます。ネームスペース下ではプロセスIDはユニークです。従ってtsk->nsproxy->pid_nsのネームスペース毎のビットマップから未使用のPIDを取得します。その関数がalloc_pidmap関数です。PIDネームスペースは親子関係と、そしてトップPIDネームスペースからのネストレベルを有しています。親ネームスペースがns->parentで、ネストレベルがns->levelです。for (i = ns->level; i >= 0; i--)ループではネストレベル数、それぞれのPIDネームスペース下でPIDを取得しています。pid->numbers[i].nr/pid->numbers[i].nsがそれに相当します。すなわち、そのプロセスIDは異なるものの、親のネームスペースから、子のネームスペースのプロセスを識別することが可能ということです。
struct pid *pidは特定のプロセスそのものを表す構造体とも言えます。カーネルがプロセスIDからプロセス構造体を取得するのは、プロセスIDからstruct pidを取得します。これはプロセスIDとネームスペースのアドレスをキーとするハッシュテーブルpid_hashから簡単に取得できます。そこからそのstruct pid下にリスト管理しているリストを走査することでtask_structを取得します。そのための処理が以下の2つのループです。
for (type = 0; type < PIDTYPE_MAX; ++type)ループは、グループ/セッション/プロセスをtask_structをリストするヘッダー初期化します。PIDTYPE_PGID, PIDTYPE_SID, PIDTYPE_PID,です。
for (i = ns->level; i >= 0; i--)ループはpid_hashハッシュテーブルにupid->nr, upid->nsをキーとしてpid->numbers[i]を登録しています。
そしてalloc_pid関数で取得したpidのpid->numbers[0].nrをプロセスIDとしてtask_struct->pid/tgidに設定されます。psコマンドはtask_struct->tgidを取得します。従ってpsプロセスのネームスペース(シェルのネームスペース)に関係なく、いかなるネームスペースのプロセスIDも表示されます。
struct pid *alloc_pid(struct pid_namespace *ns) { struct pid *pid; enum pid_type type; int i, nr; struct pid_namespace *tmp; struct upid *upid; pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL); if (!pid) goto out; tmp = ns; for (i = ns->level; i >= 0; i--) { nr = alloc_pidmap(tmp); if (nr < 0) goto out_free; pid->numbers[i].nr = nr; pid->numbers[i].ns = tmp; tmp = tmp->parent; } get_pid_ns(ns); pid->level = ns->level; atomic_set(&pid->count, 1); for (type = 0; type < PIDTYPE_MAX; ++type) INIT_HLIST_HEAD(&pid->tasks[type]); spin_lock_irq(&pidmap_lock); for (i = ns->level; i >= 0; i--) { upid = &pid->numbers[i]; hlist_add_head_rcu(&upid->pid_chain, &pid_hash[pid_hashfn(upid->nr, upid->ns)]); } spin_unlock_irq(&pidmap_lock); out: return pid; : }CLONE_NEWPIDが設定されていると、copy_namespacesから最終的にcreate_pid_namespace関数でプロセスIDのネームスペースが作成されます。copy_pid_ns関数でcreate_pid_namespace関数で、new_nsとして新規にPIDネームスペースを取得し、new_ns->parentに親のネームスペースを設定しています。get_pid_ns関数は引数のネームスペースがinit_pid_nsで無いことを確認し、そうでないならその参照カウンタns->krefをインクリメントし、引数のold_nsそのものを返しています。なおcreate_pid_namespace関数の引数は、新規のネームスペースのlevelとなります。+ 1としているように、階層的にネームスペースを作る毎に、そのレベルが深くなることを表しています。
struct pid_namespace *copy_pid_ns(unsigned long flags, struct pid_namespace *old_ns) { struct pid_namespace *new_ns; : new_ns = create_pid_namespace(old_ns->level + 1); if (!IS_ERR(new_ns)) new_ns->parent = get_pid_ns(old_ns); out_put: put_pid_ns(old_ns); out: return new_ns; }・備考
ネームスペースのlevelがネームスペースの階層レベルを表しています。しかもPID取得においてすべてのネームスペースからそれぞれのPIDを取得し、それをpid->number[]に設定していました。ところがstruct pidを見るとそのメンバーはnumbers[1]となっています。検証はしてませんが、よろしくないのでは(バグ?)・・・。
struct pid { atomic_t count; unsigned int level; /* lists of tasks that use this pid */ struct hlist_head tasks[PIDTYPE_MAX]; struct rcu_head rcu; struct upid numbers[1]; };