From 8e37f9b1690a59ce285eb351eb08d5fd15b42d05 Mon Sep 17 00:00:00 2001 From: gatieme Date: Mon, 16 May 2016 14:53:06 +0800 Subject: [PATCH] ... --- study/kernel/process/task/pid/README.md | 115 ++++++++++++++---- .../task/pid/images/pid-to-struct_pid.png | Bin 0 -> 21947 bytes 2 files changed, 92 insertions(+), 23 deletions(-) create mode 100644 study/kernel/process/task/pid/images/pid-to-struct_pid.png diff --git a/study/kernel/process/task/pid/README.md b/study/kernel/process/task/pid/README.md index 452441a..9f64eec 100644 --- a/study/kernel/process/task/pid/README.md +++ b/study/kernel/process/task/pid/README.md @@ -488,11 +488,80 @@ struct upid 图中关于如果分配唯一的 PID 没有画出,但也是比较简单,与前面两种情形不同的是,这里分配唯一的 PID 是有命名空间的容器的,在PID命名空间内必须唯一,但各个命名空间之间不需要唯一。 至此,已经与 Linux 内核中数据结构相差不多了。 + #进程ID管理函数 ------- 有了上面的复杂的数据结构,再加上散列表等数据结构的操作,就可以写出我们前面所提到的三个问题的函数了: +##pid号到struct pid实体 +------- + +很多时候在写内核模块的时候,需要通过进程的pid找到对应进程的task_struct,其中首先就需要通过进程的pid找到进程的struct pid, +然后再通过struct pid找到进程的task_struct + +我知道的实现函数有三个。 + +```c +struct pid *find_pid_ns(int nr, struct pid_namespace *ns) +struct pid *find_vpid(int nr) +struct pid *find_get_pid(pid_t nr) +``` + + + +find_pid_ns获得 pid 实体的实现原理,主要使用哈希查找。内核使用哈希表组织struct pid,每创建一个新进程,给进程的struct pid都会插入到哈希表中,这时候就需要使用进程 +的进程pid和命名ns在哈希表中将相对应的struct pid索引出来,现在可以看下find_pid_ns的传入参数,也是通过nr和ns找到struct pid。 + +根据局部PID以及命名空间计算在 pid_hash 数组中的索引,然后遍历散列表找到所要的 upid, 再根据内核的 container_of 机制找到 pid 实例。 + +代码如下: + +```c +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; +} +``` + +而另外两个函数则是对其进行进一步的封装,如下 + +```c +struct pid *find_vpid(int nr) +{ + return find_pid_ns(nr, current->nsproxy->pid_ns); +} +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; +} +``` + +三者之间的调用关系如下 + +![调用关系](./images/pid-to-struct_pid.png) + +由图可以看出,find_pid_ns是最终的实现,find_vpid是使用find_pid_ns +实现的,find_get_pid又是由find_vpid实现的。 + +由原代码可以看出find_vpid和find_pid_ns是一样的,而find_get_pid和find_vpid有一点差异,就是使用find_get_pid将返回的struct pid中的字段count加1,而find_vpid没有加1。 + + ##获得局部ID ------- @@ -508,7 +577,7 @@ static inline struct pid *task_pid(struct task_struct *task) ``` 获取线程组的ID,前面也说过,TGID不过是线程组组长的PID而已,所以: -``` +```c static inline struct pid *task_tgid(struct task_struct *task) { return task->group_leader->pids[PIDTYPE_PID].pid; @@ -546,10 +615,12 @@ pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns) return nr; } ``` + 这里值得注意的是,由于PID命名空间的层次性,父命名空间能看到子命名空间的内容,反之则不能,因此,函数中需要确保当前命名空间的level 小于等于产生局部PID的命名空间的level。 除了这个函数之外,内核还封装了其他函数用来从 pid 实例获得 PID 值,如 pid_nr、pid_vnr 等。在此不介绍了。 结合这两步,内核提供了更进一步的封装,提供以下函数: + ```c pid_t task_pid_nr_ns(struct task_struct *tsk, struct pid_namespace *ns); pid_t task_tgid_nr_ns(struct task_struct *tsk, struct pid_namespace *ns); @@ -558,30 +629,19 @@ pid_t task_session_nr_ns(struct task_struct *tsk, struct pid_namespace *ns); ``` 从函数名上就能推断函数的功能,其实不外于封装了上面的两步。 -##查找进程task_struct +##根据PID查找进程task_struct ------- -根据局部ID、以及命名空间,怎样获得进程的task_struct结构体呢?也是分两步: -获得 pid 实体。根据局部PID以及命名空间计算在 pid_hash 数组中的索引,然后遍历散列表找到所要的 upid, 再根据内核的 container_of 机制找到 pid 实例。代码如下: -``` -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) //pid_hashfn() 获得hash的索引 - if (pnr->nr == nr && pnr->ns == ns) //比较 nr 与 ns 是否都相同 - return container_of(pnr, struct pid, //根据container_of机制取得pid 实体 - - numbers[ns->level]); - return NULL; -} -``` +* 根据PID号(nr值)取得task_struct 结构体 -##根据ID类型取得task_struct 结构体 -------- -``` +* 根据PID以及其类型(即为局部ID和命名空间)获取task_struct结构体 + +如果根据的是进程的ID号,我们可以先通过ID号(nr值)获取到进程struct pid实体(局部ID),然后根据局部ID、以及命名空间,获得进程的task_struct结构体 + + +可以使用pid_task根据pid和pid_type获取到进程的task + +```c struct task_struct *pid_task(struct pid *pid, enum pid_type type) { struct task_struct *result = NULL; @@ -597,14 +657,23 @@ struct task_struct *pid_task(struct pid *pid, enum pid_type type) } ``` +那么我们根据pid号查找进程task的过程就成为 +```c +pTask = pid_task(find_vpid(pid), PIDTYPE_PID); +``` + 内核还提供其它函数用来实现上面两步: + ```c struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns); struct task_struct *find_task_by_vpid(pid_t vnr); struct task_struct *find_task_by_pid(pid_t vnr); ``` -具体函数实现的功能也比较简单。 + +>由于linux进程是组织在双向链表和红黑树中的,因此我们通过遍历链表或者树也可以找到当前进程,但是这个并不是我们今天的重点 + + ##生成唯一的PID ------- diff --git a/study/kernel/process/task/pid/images/pid-to-struct_pid.png b/study/kernel/process/task/pid/images/pid-to-struct_pid.png new file mode 100644 index 0000000000000000000000000000000000000000..6296cc1773d86093c638916ea07e324e8efc6c05 GIT binary patch literal 21947 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 Yc7N0^6zmNyT*Z=+RD6&xZsh;J06G#=s{jB1 literal 0 HcmV?d00001