読者です 読者をやめる 読者になる 読者になる

Linux: inodeからtask_struct構造体を取得

kernel linux

Namespace周りのコードを読んでいたらinodeからtask_struct構造体を取得をしているところがあって、こんなことできるんだ〜などと思ったのでどんな方法で取得するのか見てみます。

使ってた場所はproc_ns_follow_link()とかです。 ↓のように。

 37         struct task_struct *task;
 38         struct path ns_path;
 39         void *error = ERR_PTR(-EACCES);
 40 
 41         task = get_proc_task(inode);

関数はget_proc_task()で、fs/proc/internal.hにあります。 実装としては、2個の関数を呼んで結果を返すだけの1行関数です。関数の引数でinode構造体を受け取ってそこからtask_struct構造体を返します。

 95 static inline struct task_struct *get_proc_task(struct inode *inode)
 96 {
 97         return get_pid_task(proc_pid(inode), PIDTYPE_PID);
 98 }

では、最初にproc_pid()から見ていきましょう。 PROC_Iマクロっぽい名前ですね。このPROC_Iからpid構造体を取得してreturnしています。

 90 static inline struct pid *proc_pid(struct inode *inode)
 91 {
 92         return PROC_I(inode)->pid;
 93 }
 94 

PROC_I()はマクロっぽい名前の割に関数でした。 container_ofマクロを使って、inodeからproc_inode構造体を取得して返します。

 75 static inline struct proc_inode *PROC_I(const struct inode *inode)
 76 {
 77         return container_of(inode, struct proc_inode, vfs_inode);
 78 }

container_ofマクロはこれです。

799 #define container_of(ptr, type, member) ({                      \
800         const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
801         (type *)( (char *)__mptr - offsetof(type,member) );}) 

container_ofが展開されるとこんな感じになって、__mptjからvfs_inodeのoffsetを引いた位置がproc_inode構造体のアドレスですね。

const typeof( ((struct proc_inode *)0)->vfs_inode ) *__mptr = (inode);
(struct proc_inode *)( (char *)__mptr - offsetof(struct proc_inode,vfs_inode) );

proc_inode構造体はこのようになっています。

 61 struct proc_inode {
 62         struct pid *pid;
 63         int fd;
 64         union proc_op op;
 65         struct proc_dir_entry *pde;
 66         struct ctl_table_header *sysctl;
 67         struct ctl_table *sysctl_entry;
 68         const struct proc_ns_operations *ns_ops;
 69         struct inode vfs_inode;
 70 };

これでproc_pid()がPROC_I(inode)->pidでpid構造体を返せている理由がわかりましたね。 このpid構造体がget_pid_task()の1番目の引数です。

2番目の引数は整数でinclude/linux/pid.hにてenum型として定義されています。

  6 enum pid_type
  7 {
  8         PIDTYPE_PID,
  9         PIDTYPE_PGID,
 10         PIDTYPE_SID,
 11         PIDTYPE_MAX
 12 };

内容は名前の通りで、pid、pdig、sidなどの種別ですね。

では、get_pid_task()を見てみます。 ここでの処理はpid_task()get_task_struct()くらいですね。

476 struct task_struct *get_pid_task(struct pid *pid, enum pid_type type)
477 {
478         struct task_struct *result;
479         rcu_read_lock();
480         result = pid_task(pid, type);
481         if (result)
482                 get_task_struct(result);
483         rcu_read_unlock();
484         return result;
485 }

それではまずはpid_task()を見ます。 関数のプロトタイプから、この関数がtask_struct構造体を返すのがわかります。

434 struct task_struct *pid_task(struct pid *pid, enum pid_type type)
435 {
436         struct task_struct *result = NULL;
437         if (pid) {
438                 struct hlist_node *first;
439                 first = rcu_dereference_check(hlist_first_rcu(&pid->tasks[type]),
440                                               lockdep_tasklist_lock_is_held());
441                 if (first)
442                         result = hlist_entry(first, struct task_struct, pids[(type)].node);
443         }
444         return result;
445 }

hlist_node構造体はハッシュリストのノードです。 pid構造体はこのようになっています。なので、PIDTYPEがPIDなリストを取得しています。

 57 struct pid
 58 {
 59         atomic_t count;
 60         unsigned int level;
 61         /* lists of tasks that use this pid */
 62         struct hlist_head tasks[PIDTYPE_MAX];
 63         struct rcu_head rcu;
 64         struct upid numbers[1];
 65 };
 66 

リストの取得にはhlist_first_rcu()を使っていて、これはリストの先頭要素を取得するものです。

369 /*
370  * return the first or the next element in an RCU protected hlist
371  */
372 #define hlist_first_rcu(head)   (*((struct hlist_node __rcu **)(&(head)->first)))

取得したfirstがnullでなければ、hlist_entry()でtask_struct構造体を取得します。hlist_entry()はマクロで、container_ofマクロの別名なだけです。

688 #define hlist_entry(ptr, type, member) container_of(ptr,type,member)

これも展開してみるとこんな風になります。

800         const typeof( ((struct task_struct *)0)->pids[(type)].node) ) *__mptr = (first);    \
801         (struct task_struct *)( (char *)__mptr - offsetof(struct task_struct,pids[(type)].node)) );}) 

task_struct構造体にはpidsというメンバ変数があり、

1408         /* PID/PID hash table linkage. */
1409         struct pid_link pids[PIDTYPE_MAX];

pidsはpid_link構造体型で、内容は下記のようになります。 これでfirstがtask_structのpids配列のインデックスがPIDTYPE_PIDの先頭要素となりました。

 69 struct pid_link
 70 {
 71         struct hlist_node node;
 72         struct pid *pid;
 73 };

あとはoffset_ofで引き算したらtask_struct構造体の先頭アドレスが取れるので、get_proc_task()がtask_struct構造体を返すという流れがわかりました。

( ´ー`)フゥー...

Androidのなかみ InsideAndroid

Androidのなかみ InsideAndroid