linuxカーネルのpid管理(取得周り)
カーネルのpid管理と取得周りを調べてみた
PID名前空間
Linuxではpid_namespace(詳しくはman 7参照)に対応しているため、 1つのプロセスに対して複数のプロセスIDを複数持てるようになっている。
pid_namespaceは階層構造になっていて、 子のプロセスIDは同一プロセスでもそれぞれの名前空間で別のプロセスidとなる
親で作成されたのプロセスIDは子からは見えないようになっている
したがってプロセスIDは作成された階層の深度分プロセスIDを持つようになっている
カーネルでの表現
カーネル内部ではPIDはpidとupidという構造で表される
enum pid_type { PIDTYPE_PID, PIDTYPE_PGID, PIDTYPE_SID, PIDTYPE_MAX }; struct upid { int nr; // 実際のID struct pid_namespace *ns; // 属しているネームスペース struct hlist_node pid_chain; }; struct pid { atomic_t count; unsigned int level; // このプロセスが作成された時のname_spaceの深度 struct hlist_head tasks[PIDTYPE_MAX]; // pid構造体のプロセス、プロセスグループ、セッションに属するタスク struct rcu_head rcu; struct upid numbers[1]; // 可変長でlevel分だけupidを持つ };
pid構造体のIDはネームスペース毎にネームスペース毎に持つため、所属している深度分のupidを持つようになっていて、upidに実際の番号を持っている。
pid->tasksメンバ
tasksメンバはそのpidのプロセス、プロセスグループ、セッションに属するtask_structの一覧を管理する
pid構造体に属するPIDTYPE_PIDに結びつくプロセスは1つしかないので、 1つまでしか入らないことが保証されている。
IDからpidへの変換処理
IDからpidへの変換処理はよく行われるので、 pid_hashというIDとネームスペースのポインタをキーにしたハッシュを使い処理を高速化している
関数としてはネームスペースを渡すバージョンと、現在実行中のタスクのネームスペースを使うものの二種類がある。
extern struct pid *find_pid_ns(int nr, struct pid_namespace *ns); extern struct pid *find_vpid(int nr);
pidからidへの変換処理
ネームスペースのlevelからupidを探し出してnrを返す関数
こちらもネームスペースを渡すバージョンと、現在実行中のタスクのネームスペースを使うものの二種類がある。
pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns); pid_t pid_vnr(struct pid *pid);
現在実行中のプロセスのネームスペース取得方法
// 呼び出し元 task_active_pid_ns(current->group_leader); // currentはスレッドに対応するのでプロセスのtask_structで呼び出し struct pid_namespace *task_active_pid_ns(struct task_struct *tsk) { return ns_of_pid(task_pid(tsk)); // task_pidでpid構造体を取得してpid構造体からネームスペースを取得する } static inline struct pid *task_pid(struct task_struct *task) { return task->pids[PIDTYPE_PID].pid; // カレントプロセスのPID構造体を取得 } static inline struct pid_namespace *ns_of_pid(struct pid *pid) { struct pid_namespace *ns = NULL; if (pid) ns = pid->numbers[pid->level].ns; // カレントプロセスの最大深度 = カレントプロセスが作成された名前空間となる。 return ns; }