Chinaunix首页 | 论坛 | 博客
  • 博客访问: 141502
  • 博文数量: 21
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 233
  • 用 户 组: 普通用户
  • 注册时间: 2013-03-17 15:31
个人简介

学习中。。

文章分类

全部博文(21)

文章存档

2015年(1)

2014年(17)

2013年(3)

我的朋友

分类: LINUX

2014-03-31 09:18:23

原文地址:Linux 命名空间(三) 作者:datao0907

struct pid已经分配,需要链接到具体task中时,执行代码如下:

  1. int fastcall attach_pid(struct task_struct *task,enum pid_type type,
  2.                 struct pid *pid)
  3. {
  4.     struct pid_link *link;
  5.     
  6.     //从指定的task[type]中取出函数指针    
  7.     link = &task->pids[type];
  8.     //将task赋值pid
  9.     link ->pid = pid;
  10.     //将该task放入pid的tasks链表中
  11.     hlist_add_head_rcu(&link->node,&pid->tasks[type]);
  12.     
  13.     return 0;
  14. }

内核提供了大量的函数来进行pidtask之间的映射管理及维护,主要包括两部分:

1.指定局部数值pid,对应的命名空间,查找到对应的进程。

这种情况主要有以下几种情况:

1.获取与pid相关联的task,task_pid,task_tgid,task_pgrp,task_session用于不同类型的ID

  1. //每一task有四种不同类型的type,每一种type中包含有一个pid
  2. static inline struct pid *task_pid(struct task_struct *task)
  3. {
  4.     return task->pids[PIDTYPE_PID].pid;
  5. }

其它的也与上面的类似。

2.通过pid和命名空间namespace来查找进程的pid_t

  1. [kernel/pid.c]
  2. pid_t pid_nr_ns(struct pid *pid,struct pid_namespace *ns)
  3. {
  4.     struct upid *upid;
  5.     pid_t nr = 0;
  6.     //指定的命名空间深度必须比pid高
  7.     if(pid && ns->level <= pid->level)
  8.     {
  9.         upid = & pid->numbers[pid->level];
  10.         if(upid->ns == ns)
  11.             nr = upid->nr;    
  12.     }
  13.         return nr;
  14. }

内核还采用了另外的方法来访问进程的pid:

1.pid_vnrid所属的命名空间中返回局部pid,:

  1. pid_t task_pid_vnr(struct task_struct*);
  2. pid_t task_tgid_vnr(struct task_struct*);
  3. pid_t task_pgrp_vnr(struct task_struct*);
  4. pid_t task_session_vnr(struct task_struct*);

这些函数都是通过pid_nr_ns来实现的

2.pid_nrinit进程中获取全局pi

这两个函数实际都通过指明level级别(0表示全局)调用了函数pid_nr_ns,它则是通过nr_ns系列函数来进行访问,如:

  1. pid_t task_pid_nr_ns(struct task_struct *,struct pid_namespace *);
  2. pid_t task_tgid_nr_ns(struct task_struct *,struct pid_namespace*);
  3. pid_t task_pgrp_nr_ns(struct task_struct*,struct pid_namespace*);
  4. pid_t task_session_nr_ns(struct task_struct *,struct pid_namespace *);

这些函数也是通过pid_nr_ns来实现的。

另外,还可以通过pid中的数值id-nr和命名空间来获取对应的pid,如下:

  1. struct pid *fastcall find_pid_ns(int nr,struct pid_namespace *ns)
  2. {
  3.     struct hlist_node *elem;
  4.     struct upid *pnr;
  5.     //通过nr进行hash查找到对应的struct upid,pid_hash为全局唯一
  6.     hlist_for_each_entry_rcu(pnr,elem,
  7.         &pid_hash[pid_hashfn(nr,ns)],pid_chain)
  8.      if(pnr->nr == ns)
  9.     //通过pid中的成员变量upid来查找对应的struct pid
  10.     return container_of(pnr,struct pid,    numbers[ns->level]);
  11. }

2.指定一个进程,id类型,及命名空间,查找到对应的进程pid,find_task系列函数:

  1. [kernel/pid.c]
  2. struct task_struct *find_task_by_pid_type_ns(int type,int nr,
  3.          struct pid_namespace *ns);
  4. struct task_struct *find_task_by_pid(pid_t nr);
  5. struct task_struct *find_task_by_vpid(pid_t vnr);
  6. struct task_struct *find_task_by_pid_ns(pid_t nr,struct namespace *ns);

这些函数都是通过find_task_by_pid_type_ns来实现的

  1. struct task_struct *find_task_by_pid_type_ns(int type,int nr,
  2.             struct pid_namspace *ns)
  3. {
  4.     return pid_task(find_pid_ns(nr,ns),type);
  5. }

pid_task实现如下:

  1. struct task_struct *fastcall pid_task(struct pid *pid,enum pid_type type)
  2. {
  3.     struct task_struct *result = NULL;
  4.     if(pid){
  5.         struct hlist_node *first;
  6.         //获取指定type的task_struct
  7.         first = rcu_dereference(pid->tasks[type].node);
  8.         if(first)
  9. //获取first中的pid_link域的pids[(type)].node值
  10. //因为pid_link中node域就是自己所以就直接获取,
  11. //那里指向自己的hlist_head就是为了满足这里的统一
  12.             result = hlist_entry(first,struct task_struct,pids[(type)].node)
  13.     }
  14.         return result;
  15. }

pid的分配

为了记录pid的分配与释放情况,内核使用了一张pid位图,可以从pid位图中的位置来获取对应的pid值,同时将pid值从0改为1,相反释放时只需修改10

  1. static int alloc_pidmap(struct pid_namespace *pid_ns)
  2. {
  3.     int i,offset,max_scan,pid,last = pid_ns ->last_pid;//上一次
  4.     struct pidmap *map;
  5.     
  6.     pid = last + 1;
  7.     
  8.     if(pid >= pid_max)
  9.          pid = RESERVED_PIDS;
  10.     //从pid中获取具体位数的偏移量,位图即每一位一个pid
  11.     offset = pid & BITS_PER_PAGE_MASK;
  12.     map = &pid_ns->pidmap[pid/BITS_PER_PAGE];
  13.     max_scan= (pid_max + BITS_PER_PAGE – 1)/BITS_PER_PAGE - !offset;
  14.     for(i = 0;i<= max;++i)
  15.     {
  16.             //如果内存页没有分配就分配一项
  17.             if(unlikely(!map->page)) {
  18.             void *page = kzalloc(PAGE_SIZE,GFP_KERNEL);
  19.             spin_lock_irq(&pidmap_lock);
  20.             if(map->page) kfree(page);
  21.             else map->page = page;
  22.             spin_unlock_irq(&pidmap_lock);
  23.             if(unlikely(!map->page))
  24.                 break;
  25.         }
  26.         if(likely(atomic_read(&map->nr_free))){
  27.         do{
  28.             //扫描到空位即可
  29.             if(!test_and_set_bit(offset,map->page)){
  30.                 atomic_dec(&map->nr_free);
  31.                 pid_ns->last_pid = pid;
  32.                 return pid;
  33.             }
  34.             offset = find_next_offset(map,offset);
  35.             pid = mk_pid(pid_ns,map,offset);
  36.         }while(offset < BITS_PER_PAGE && pid<pid_max &&
  37.              (i != max_scan || pid<last ||
  38.              !((last+1)&&BITS_PER_PAGE_MASK)));
  39.         }
  40.          if(map < &pid_ns->pidmap[(pid_max-1)/BITS_PER_PAGE]) {
  41.             ++map;
  42.             offset = 0;
  43.     }else {
  44.         map = &pid_ns->pidmap[0];
  45.         offset = RESERVED_PIDS;
  46.         if(unlikely(last = offset)) break;    
  47.     }
  48.     //通过一个扫描来的偏移量,生成一个pid
  49.     pid = mk_pid(pid_ns,map,offset);    
  50.     }
  51.     return -1;
  52. }

参考资料

linux2.6.24内核源码

professional Linux architecture

understanding linux kernel 


阅读(1317) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~