linux内核进程命名空间函数分析

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是组长进程,所以BCtask_struct结构体中的pid_link成员的node字段就被邻接到进程A对应的struct pid中的tasks[1]

struct upid通过pid_hashpid数值关联了起来,这样就可以通过pid数值快速的找到所有命名空间的upid结构,numbers是一个struct pid的最后一个成员,利用可变数组来表示这个pid结构当前有多少个命名空间.

pid_task(struct pid *pid, enum pid_type type);

 

 

 

struct pid是进程ID在内核结构的一个表示。但有可能会出现多个task_struct实例共享同一个进程ID,这就需要将所有共享进程IDtask_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 pidstruct 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()的参数必须为全局taskpid_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是全局的。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值