struct pid 与 struct task_struct 都是全局的(内核的),struct pid 的 level 为最高的命名空间层次(全局的命名空间层次为0),
task_struct.pid 为全局的pid,find_get_pid(pid_t nr);nr 必须为全局pid_t pid ,因为后续的namespace 是根据current 宏变量寻找的全局pid_namespace;
首先给出一个总图,其中红线是结构描述,黑线是指向。其中进程A,B,C是一个进程组的,A是组长进程,所以B和C的task_struct结构体中的pid_link成员的node字段就被邻接到进程A对应的struct pid中的tasks[1]。
struct upid通过pid_hash和pid数值关联了起来,这样就可以通过pid数值快速的找到所有命名空间的upid结构,numbers是一个struct pid的最后一个成员,利用可变数组来表示这个pid结构当前有多少个命名空间.
pid_task(struct pid *pid, enum pid_type type);
struct pid是进程ID在内核结构的一个表示。但有可能会出现多个task_struct实例共享同一个进程ID,这就需要将所有共享进程ID的task_struct都以一种方式和struct pid关联起来,这里就是struct pid结构中的tasks数组
pid->tasks[type]
此图可以看到第一个PID(session group) struct pid.tasks[PIDTYPE_PID] 只有一个struct task_struct 指向,struct pid.tasks[PIDTYPE_SID]有三个,所以可以根据struct pid 和 type 寻找struct task_struct, 而通过namespace 和 nr 可以找到struct pid;
namespace+nr –>struct pid-> struct task_struct
struct pid也可以通过struct hlist_head tasks[PIDTYPE_MAX]自己作为进程tasks[PIDTYPE_PID], 也就是A,但是在实际测试中,无法找到自己作为会话进程的会话其他进程,也无法找到作为组长进程时其它进程tasks[PIDTYPE_PGID],也就是B或者C,。显然可以使用type=PIDTYPE_PID,其它两种情况不能使用。实际测试中, IDTYPE_PID可以通过,IDTYPE_PGID未通过。PIDTYPE_SID未测试
pid_t__task_pid_nr_ns(struct task_struct *tsk, enum pid_type type,struct pid_namespace *ns)
Struct task_struct + pid_type -> struct pid;( find_pid_ns函数)
struct pid[numbers] + pid_namespace.level ->upid->nr
task_struct 可以通过pid_link[PIDTYPE_MAX] 找到组长进程pid_link[PIDTYPE_PGID],比如通过B的task_struct, PIDTYPE_PGID找到A,自己作为进程pid_link [PIDTYPE_PID], 也就是A,自己作为会话进程的会话其他进程。实际测试中,PIDTYPE_PGID,IDTYPE_PID可以通过,IDTYPE_SSID
未通过。PIDTYPE_SID未测试
组长进程tasks[PIDTYPE_PID] == tasks[PIDTYPE_PGID],
__task_pid_nr_ns(),参数task_struct(B), PIDTYPE_PGID ==参数task_struct(A), PIDTYPE_PID==参数task_struct(A), PIDTYPE_PGID
第一个等于是因为if (type != PIDTYPE_PID) task = task->group_leader;
第二个等于是因为组长进程tasks[PIDTYPE_PID] == tasks[PIDTYPE_PGID]。
pid_t __task_pid_nr_ns(struct task_struct *task, enum pid_type type,
struct pid_namespace *ns)
{……
if (likely(pid_alive(task))) {
if (type != PIDTYPE_PID)
task = task->group_leader;
nr = pid_nr_ns(task->pids[type].pid, ns);
}
task_struct 找自己在全局的pid,然后通过指向的pid 和命名空间level,找到在命名空间pid(upid->nr)的局部pid. 将struct upid按数字PID和命名空间映射存储,当给定数字PID和命名空间时,可以获取struct upid,然后根据container_of获取struct pid。struct pid中的tasks字段的第一个task_struct。
pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns)
{…….
if (pid && ns->level <= pid->level) {
upid = &pid->numbers[ns->level];
if (upid->ns == ns)
nr = upid->nr;
return nr; …….}
struct pid_link pids[PIDTYPE_MAX];
enum pid_type
{
PIDTYPE_PID,
PIDTYPE_PGID,
PIDTYPE_SID,
PIDTYPE_MAX
};
Static inline struct pid *task_pid(struct task_struct *task){
return task->pids[PIDTYPE_PID].pid; /* Error?! */
}
find_get_pid(pid_t nr)
图片为2.6.11内核,3.19内核pid_hash没有 0, 1, 2, 3也就是PID,PGID,SID,TGID.相当于只有一个PID.《深入理解linux内核》第三版使用的时2.6.11,而《linux内核完全参考手册》第二是3.19
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;
}
find_pid_ns() 在pid_hash的哈希表中根据哈希函数得到pid所在的链表头部,然后遍历得到 id 和 ns 都匹对的pid结构体指针;
void __init pidhash_init(void)
{
unsigned int i, pidhash_size;
pid_hash = alloc_large_system_hash("PID", sizeof(*pid_hash), 0, 18,
HASH_EARLY | HASH_SMALL,
&pidhash_shift, NULL,
0, 4096);
pidhash_size = 1U << pidhash_shift;
for (i = 0; i < pidhash_size; i++)
INIT_HLIST_HEAD(&pid_hash[i]);
}
从pidhash_init函数中可以看出,pid_hash至少含有16项,最多4096项,它是系统中所有进程的哈希表,根据id分配,但此哈希表与PGID,TGID并无直接关系;
struct pid *find_get_pid(pid_t nr)
#define get_current() (current_thread_info()->task)
#define current get_current()
find_get_pid()函数用于通过全局pid和其类型找到对应的task结构,find_get_pid(current->pid);current为全局struct task_struct.
task->pid为全局。Find_get_pid()的参数必须为全局task的pid_t pid. (task->pids[PIDTYPE_PID].pid 也是全局,pid_namespace.nr 不是全局)
find_get_pid调用struct pid_namespace *task_active_pid_ns(struct task_struct *tsk)
task_active_pid_ns根据形参task获得对应的pid namespace
其使用的例程如下:
static int acct_on(struct filename *pathname)
{
struct file *file;
struct vfsmount *mnt, *internal;
struct pid_namespace *ns = task_active_pid_ns(current);
}
本例中就根据当前task current 获得current对应的pid namespace
其源码分析如下:
struct pid_namespace *task_active_pid_ns(struct task_struct *tsk)
{
return ns_of_pid(task_pid(tsk));
}
可以看到这个函数分为两步,第一步通过task_pid 拿到当前task的pid,这里的pid是全局的。
在已pid 为形参通过ns_of_pid 得到pid 对应的namespace
这里为什么说task_pid拿到pid是全局的呢?
static inline struct pid *task_pid(struct task_struct *task)
{
return task->pids[PIDTYPE_PID].pid;
}
可以看到这里的pid是保存在task中的pids 这个成员数组中,根本和namespace不属于任何的命令空间
而属于系统启动期间开始的init进程,即初始的命名空间。属于task本身的数据结构。因此说task_pid得到的pid是全局的。