PID

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的支持,需要达到以下目标:

  1. namespace要能反映其树状结构关系
  2. task在不同的namespace需要有不同的pid num,因此,每个namespace需要能管理其分配的pid num
  3. 要提供机制能获取 task 在指定namespace下的pid num
  4. 要提供机制能获取 pid num 在指定namespace 下对应的 task
  5. pid 包括上述的类型,需要能方便地获取到task对应的不同类型pid
  6. 一个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,如下:


你可能感兴趣的:(PID)