find_task_by_vpidを追ってみる
kernel 2では、引数のPIDでfind_pid()をコールして、struct pidを取得します。struct pidはpid_hashをヘッドとするPIDタイプ毎(プロセスID/グループID/セッションID)のハッシュリストでリストされています。そのリストヘッドからPIDの同じものを走査することで取得します。
find_task_by_pid()はEXPORT_SYMBOLされています。
kernel 3ではfind_task_by_pid()に相当する関数は、find_task_by_vpid()になります。pidはプロセス毎のネームスペース下で仮想化されているため、vpidとなっていて、PIDとネームスペースを引数にfind_task_by_pid_ns()しています。そこではvnrとcurrent->nsproxy->pid_nsのハッシュリストとして、対応するstruct task_strucを取得します。
ただし、find_task_by_vpid()はEXPORT_SYMBOLされておらず、LKMから使うことはできません。
単に取得だけなら、get_pid_task()を使ことになります。
find_task_by_pid()はEXPORT_SYMBOLされています。
typedef struct task_struct task_t; task_t *find_task_by_pid(int nr) { struct pid *pid = find_pid(PIDTYPE_PID, nr); if (!pid) return NULL; return pid_task(pid->task_list.next, PIDTYPE_PID); } EXPORT_SYMBOL(find_task_by_pid); inline struct pid *find_pid(enum pid_type type, int nr) { struct list_head *elem, *bucket = &pid_hash[type][pid_hashfn(nr)]; struct pid *pid; __list_for_each(elem, bucket) { pid = list_entry(elem, struct pid, hash_chain); if (pid->nr == nr) return pid; } return NULL; }
kernel 3ではfind_task_by_pid()に相当する関数は、find_task_by_vpid()になります。pidはプロセス毎のネームスペース下で仮想化されているため、vpidとなっていて、PIDとネームスペースを引数にfind_task_by_pid_ns()しています。そこではvnrとcurrent->nsproxy->pid_nsのハッシュリストとして、対応するstruct task_strucを取得します。
ただし、find_task_by_vpid()はEXPORT_SYMBOLされておらず、LKMから使うことはできません。
struct task_struct *find_task_by_vpid(pid_t vnr) { return find_task_by_pid_ns(vnr, current->nsproxy->pid_ns); } struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns) { rcu_lockdep_assert(rcu_read_lock_held(), "find_task_by_pid_ns() needs rcu_read_lock()" " protection"); return pid_task(find_pid_ns(nr, ns), PIDTYPE_PID); }LKMで使用する場合、get_pid_task()かpid_task()を使うことになります(ただし引数はPIDでなくstruct pid)。get_pid_task()はrcu_read_lock()してpid_task()をコールしているだけです。pid_task()をEXPORT_SYMBOLとしているのは、pidからtask_structを取得し、それから他のRCUで管理されている情報を取得しなければならない一連の処理で、LKNの呼び出しサイドで、rcu_read_lock()した方がいいようなケースを想定してのことかと思います。
単に取得だけなら、get_pid_task()を使ことになります。
struct task_struct *get_pid_task(struct pid *pid, enum pid_type type) { struct task_struct *result; rcu_read_lock(); result = pid_task(pid, type); if (result) get_task_struct(result); rcu_read_unlock(); return result; } EXPORT_SYMBOL_GPL(get_pid_task); struct task_struct *pid_task(struct pid *pid, enum pid_type type) { struct task_struct *result = NULL; if (pid) { struct hlist_node *first; first = rcu_dereference_check(hlist_first_rcu(&pid->tasks[type]), lockdep_tasklist_lock_is_held()); if (first) result = hlist_entry(first, struct task_struct, pids[(type)].node); } return result; } EXPORT_SYMBOL(pid_task);struct pidfind_get_pid()は、pidからstruct pidを取得します。pid_tはintでdefineされています。今後いろんな機能を付加していくための宣言だと思います(structに変更)。そこからfind_vpid()とコールしていますが、この2つも先の2つの関数のように、rcu_read_lock()の取り扱いによるものです。
struct pid *find_get_pid(pid_t nr) { struct pid *pid; rcu_read_lock(); pid = get_pid(find_vpid(nr)); rcu_read_unlock(); return pid; } EXPORT_SYMBOL_GPL(find_get_pid); struct pid *find_vpid(int nr) { return find_pid_ns(nr, current->nsproxy->pid_ns); } EXPORT_SYMBOL_GPL(find_vpid);find_pid_ns()でpidとネームスペースをハッシュとして、&pid_hashのリストヘッドを取得し、PIDとネームスペースの一致するノードを走査します。find_vpid()から呼ばれる時、 nsはcurrent->nsproxy->pid_nsで、ここを任意のプロセスのネームスペースの pidを取得することが、LKMから行う事が可能となっています。
struct pid *find_pid_ns(int nr, struct pid_namespace *ns) { struct hlist_node *elem; struct upid *pnr; hlist_for_each_entry_rcu(pnr, elem, &pid_hash[pid_hashfn(nr, ns)], pid_chain) if (pnr->nr == nr && pnr->ns == ns) return container_of(pnr, struct pid, numbers[ns->level]); return NULL; } EXPORT_SYMBOL_GPL(find_pid_ns);