上一篇文章里提到task_struct里有一个PID散列表
- struct pid_link pids[PIDTYPE_MAX];
这里简单说一下pid_link
- struct pid_link
- {
- struct hlist_node node;
- struct pid *pid;
- };
其中hlist_node是双向链表的一个节点
- struct hlist_node {
- struct hlist_node *next, **pprev;
- };
由以上代码可知,pid的散列表是一个双向链表
下面介绍一些基本的函数
1、添加一个新的PID到PID散列表
关于PID的代码都在kernel/pid.c中
- void attach_pid(struct task_struct *task, enum pid_type type,
- struct pid *pid)
- {
- struct pid_link *link;
-
- link = &task->pids[type];
- link->pid = pid;
- hlist_add_head_rcu(&link->node, &pid->tasks[type]);
- }
以上是添加一个PID到对应的散列表的代码:
1、首先初始化节点,设置对应的链表指针,设置PID
2、将节点加入到对应的链表中
2、获得task_struct关联的PID实例
- <sche.h>
- static inline struct pid *task_pid(struct task_struct *task)
- {
- return task->pids[PIDTYPE_PID].pid;
- }
这个函数比较简单,task_struct中的
- struct pid_link pids[PIDTYPE_MAX];
这个字段将task对应的
- enum pid_type
- {
- PIDTYPE_PID,
- PIDTYPE_PGID,
- PIDTYPE_SID,
- PIDTYPE_MAX
- };
三种PID都存在一个数组中,因此,要取PID,则只需取PIDTYPE_PID对应的实例即可,同理也可以取得PGID和SID
3、取得PID对应的数字ID
首先PID是在指定的命名空间里的,所以只要取得pid在指定命名空间里的upid的nr字段即可
- <kernel/pid.c>
- pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns)
- {
- struct upid *upid;
- pid_t nr = 0;
-
- if (pid && ns->level <= pid->level) {
- upid = &pid->numbers[ns->level];
- if (upid->ns == ns)
- nr = upid->nr;
- }
- return nr;
- }
upid
=
&
pid
-
>
numbers
[
ns
-
>
level
]
;这一句是取得指定深度对应的upid,pid最末端的number[1]字段是可扩展的,里面存放了pid在不同命名空间里对应的upid(即是局部pid),
ns->level<=pid->level这个判断的主要原因是:保证pid在命名空间的可见范围内
最后判断取得的upid的命名空间和传入的是否一致,若一致则取出nr并返回。
4、分配一个PID
内核中Pid是用位图来存储的,分配pid即是在位图中寻找第一个没有使用的pid(也即是第一个值为0的比特),对于一个PID,能够看得到这个pid的命名空间都需要分配一个upid,同时upid要放入到pid散列表中
主要通过以下两个函数实现:
- 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]);
-
- upid = pid->numbers + ns->level;
- spin_lock_irq(&pidmap_lock);
- for ( ; upid >= pid->numbers; --upid)
- hlist_add_head_rcu(&upid->pid_chain,
- &pid_hash[pid_hashfn(upid->nr, upid->ns)]);
- spin_unlock_irq(&pidmap_lock);
-
- out:
- return pid;
-
- out_free:
- while (++i <= ns->level)
- free_pidmap(pid->numbers + i);
-
- kmem_cache_free(ns->pid_cachep, pid);
- pid = NULL;
- goto out;
- }
代码比较长,我们主要关心以下代码
- struct pid *pid;
- enum pid_type type;
- int i, nr;
- struct pid_namespace *tmp;
- struct upid *upid;
- ....
- tmp = ns;
- for (i = ns->level; i >= 0; i--) {
- nr = alloc_pidmap(tmp);
- .....
-
- pid->numbers[i].nr = nr;
- pid->numbers[i].ns = tmp;
- tmp = tmp->parent;
- }
- ...
- pid->level = ns->level;
for ( ; upid >= pid->numbers; --upid)
hlist_add_head_rcu(&upid->pid_chain,
&pid_hash[pid_hashfn(upid->nr, upid->ns)]);
1)首先,循环里是对每个命名空间都分配一个nr,从子命名空间一直到全局命名空间
2)将每个upid放到PID散列表中。
参考资料:
1、《深入Linux内核架构》