1 基础
1.1 namespace
上图是基于pid namespace的层级结构示意图,类似树状架构,而在下层的task,在其上的节点上,都需要有对应的pid,且其分配互为独立。
1.2 pid type
名称 | 描述 | 获取方式 |
---|---|---|
tid | task的id,线程id | task->pid 中存放task对应0层ns的tid, 对应其它ns,需要从 task->pids[PIDTYPE_PID]中获取,参见task_pid() |
tgid | 进程主线程id,进程id | task->tgid 中存放task对应0层ns的tgid, 对应其它ns,需要从 task->group_leader->pids[PIDTYPE_PID]>pids[PIDTYPE_PID]中获取(参见:task_tgid()) |
gid | 进程组id | task->group_leader->pids[PIDTYPE_PGID] 中获取(参见:get_pgrp()) |
sid | 事务id | task->group_leader->pids[PIDTYPE_SID]中获取,参见(task_session()) |
注意:上面的 task_xxx() 获取的是 struct pid*, 如果需要获取到nr的话,还需要再从中获取到 number[] 中的nr字段。
2 设计目标
当加入了namespace后,对于pid的支持,需要达到以下目标:
- namespace要能反映其树状结构关系
- task在不同的namespace需要有不同的pid num,因此,每个namespace需要能管理其分配的pid num
- 要提供机制能获取 task 在指定namespace下的pid num
- 要提供机制能获取 pid num 在指定namespace 下对应的 task
- pid 包括上述的类型,需要能方便地获取到task对应的不同类型pid
- 一个pid可以被多个task共享(如gid or sid),需要方便获取到pid所对应的tasks
因此,linux内核如下设计管理结构:
- 针对前述1,2点,对namespace如下设计数据结构:
struct pid_namespace
{
struct kref kref;
struct pidmap pidmap[PIDMAP_ENTRIES];
int last_pid;
struct task_struct *child_reaper;
struct kmem_cache *pid_cachep;
unsigned int level;
struct pid_namespace *parent;
}
其中,每个namespace 包括 *parent 和 level , 可以知道其所处层次和上层namespace。这里不需要获取child 信息,因为不需要。而每个 namespace都包含 pidmap 结构,可以管理自身的pid分配。
- 对第3点,由于前述表格中,可以获取对应TYPE下的pid*, 而其中的numbers[]保存了该PID对应各namespace层级的upid信息,其中的nr,就是对应该ns的数值:
struct upid {
int nr; // upid的数值
struct pid_namespace *ns; //该upid对应的ns
struct hlist_node pid_chain; //所有upid都挂接在全局pid_hash队列中
};
/* 获取pid数值的函数 */
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;
}
- 对4,前述的upid按照 ns + nr 进行hash,并挂入全局hash列表 pid_hash,这样,如果可以根据 ns和nr,找到对应的upid,而upid是嵌入在pid->number中的,因此可以获取到pid*,再由 pid->tasks列表,可以找到对应的task:
struct pid
{
atomic_t count;
unsigned int level;
struct hlist_head tasks[PIDTYPE_MAX]; //挂入的task
struct rcu_head rcu;
struct upid numbers[1]; // 针对对应层级的upid,运行时会按照实际情况分配内存
};
//根据 pid nr 和指定的namespace,获取对应的pid
struct pid *find_pid_ns(int nr, struct pid_namespace *ns)
{
struct upid *pnr;
hlist_for_each_entry_rcu(pnr,
&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;
}
- 针对5,定义了不同类型的PID_TYPE, 并在 task_struct 中定义了 struct pids[PIDTYPE_MAX],如果要获取task的对应类型PID,可以使用对应的TYPE来获取(注意 PGID和SID类型只在group_leader中保存,参考前面的表格)
struct pid_link
{
struct hlist_node node;
struct pid *pid;
};
struct task_struct {
....
struct pid_link pids[PIDTYPE_MAX];
struct task_struct *group_leader;
....
}
enum pid_type
{
PIDTYPE_PID,
PIDTYPE_PGID,
PIDTYPE_SID,
PIDTYPE_MAX,
/* only valid to __task_pid_nr_ns() */
__PIDTYPE_TGID
};
-
前面已描述,在struct pid 中,有tasks[]列表,里面存放共享该pid的task,task->pids[type].node 是每个task的挂节点,这个应该主要针对PGID和SID,如下: