当struct
pid已经分配,需要链接到具体task中时,执行代码如下:
- int fastcall attach_pid(struct task_struct *task,enum pid_type type,
-
struct pid *pid)
-
{
-
struct pid_link *link;
-
-
//从指定的task[type]中取出函数指针
-
link = &task->pids[type];
-
//将task赋值pid
-
link ->pid = pid;
-
//将该task放入pid的tasks链表中
-
hlist_add_head_rcu(&link->node,&pid->tasks[type]);
-
-
return 0;
-
}
内核提供了大量的函数来进行pid到task之间的映射管理及维护,主要包括两部分:
1.指定局部数值pid,对应的命名空间,查找到对应的进程。
这种情况主要有以下几种情况:
1.获取与pid相关联的task,task_pid,task_tgid,task_pgrp,task_session用于不同类型的ID。
- //每一task有四种不同类型的type,每一种type中包含有一个pid
-
static inline struct pid *task_pid(struct task_struct *task)
-
{
-
return task->pids[PIDTYPE_PID].pid;
-
}
其它的也与上面的类似。
2.通过pid和命名空间namespace来查找进程的pid_t
- [kernel/pid.c]
-
pid_t pid_nr_ns(struct pid *pid,struct pid_namespace *ns)
-
{
-
struct upid *upid;
-
pid_t nr = 0;
-
//指定的命名空间深度必须比pid高
-
if(pid && ns->level <= pid->level)
-
{
-
upid = & pid->numbers[pid->level];
-
if(upid->ns == ns)
-
nr = upid->nr;
-
}
-
return nr;
-
}
内核还采用了另外的方法来访问进程的pid:
1.pid_vnr从id所属的命名空间中返回局部pid,如:
- pid_t task_pid_vnr(struct task_struct*);
-
pid_t task_tgid_vnr(struct task_struct*);
-
pid_t task_pgrp_vnr(struct task_struct*);
-
pid_t task_session_vnr(struct task_struct*);
这些函数都是通过pid_nr_ns来实现的
2.pid_nr从init进程中获取全局pi
这两个函数实际都通过指明level级别(0表示全局)调用了函数pid_nr_ns,它则是通过nr_ns系列函数来进行访问,如:
- pid_t task_pid_nr_ns(struct task_struct *,struct pid_namespace *);
-
pid_t task_tgid_nr_ns(struct task_struct *,struct pid_namespace*);
-
pid_t task_pgrp_nr_ns(struct task_struct*,struct pid_namespace*);
-
pid_t task_session_nr_ns(struct task_struct *,struct pid_namespace *);
这些函数也是通过pid_nr_ns来实现的。
另外,还可以通过pid中的数值id-nr和命名空间来获取对应的pid,如下:
- struct pid *fastcall find_pid_ns(int nr,struct pid_namespace *ns)
-
{
-
struct hlist_node *elem;
-
struct upid *pnr;
-
//通过nr进行hash查找到对应的struct upid,pid_hash为全局唯一
-
hlist_for_each_entry_rcu(pnr,elem,
-
&pid_hash[pid_hashfn(nr,ns)],pid_chain)
-
if(pnr->nr == ns)
-
//通过pid中的成员变量upid来查找对应的struct pid
-
return container_of(pnr,struct pid, numbers[ns->level]);
-
}
2.指定一个进程,id类型,及命名空间,查找到对应的进程pid,如find_task系列函数:
- [kernel/pid.c]
-
struct task_struct *find_task_by_pid_type_ns(int type,int nr,
-
struct pid_namespace *ns);
-
struct task_struct *find_task_by_pid(pid_t nr);
-
struct task_struct *find_task_by_vpid(pid_t vnr);
-
struct task_struct *find_task_by_pid_ns(pid_t nr,struct namespace *ns);
这些函数都是通过find_task_by_pid_type_ns来实现的
- struct task_struct *find_task_by_pid_type_ns(int type,int nr,
-
struct pid_namspace *ns)
-
{
-
return pid_task(find_pid_ns(nr,ns),type);
-
}
而pid_task实现如下:
- struct task_struct *fastcall pid_task(struct pid *pid,enum pid_type type)
-
{
-
struct task_struct *result = NULL;
-
if(pid){
-
struct hlist_node *first;
-
//获取指定type的task_struct
-
first = rcu_dereference(pid->tasks[type].node);
-
if(first)
-
//获取first中的pid_link域的pids[(type)].node值
-
//因为pid_link中node域就是自己所以就直接获取,
-
//那里指向自己的hlist_head就是为了满足这里的统一
-
result = hlist_entry(first,struct task_struct,pids[(type)].node)
-
}
-
return result;
-
}
pid的分配
为了记录pid的分配与释放情况,内核使用了一张pid位图,可以从pid位图中的位置来获取对应的pid值,同时将pid值从0改为1,相反释放时只需修改1为0
- static int alloc_pidmap(struct pid_namespace *pid_ns)
-
{
-
int i,offset,max_scan,pid,last = pid_ns ->last_pid;//上一次
-
struct pidmap *map;
-
-
pid = last + 1;
-
-
if(pid >= pid_max)
-
pid = RESERVED_PIDS;
-
//从pid中获取具体位数的偏移量,位图即每一位一个pid
-
offset = pid & BITS_PER_PAGE_MASK;
-
map = &pid_ns->pidmap[pid/BITS_PER_PAGE];
-
max_scan= (pid_max + BITS_PER_PAGE – 1)/BITS_PER_PAGE - !offset;
-
for(i = 0;i<= max;++i)
-
{
-
//如果内存页没有分配就分配一项
-
if(unlikely(!map->page)) {
-
void *page = kzalloc(PAGE_SIZE,GFP_KERNEL);
-
spin_lock_irq(&pidmap_lock);
-
if(map->page) kfree(page);
-
else map->page = page;
-
spin_unlock_irq(&pidmap_lock);
-
if(unlikely(!map->page))
-
break;
-
}
-
if(likely(atomic_read(&map->nr_free))){
-
do{
-
//扫描到空位即可
-
if(!test_and_set_bit(offset,map->page)){
-
atomic_dec(&map->nr_free);
-
pid_ns->last_pid = pid;
-
return pid;
-
}
-
offset = find_next_offset(map,offset);
-
pid = mk_pid(pid_ns,map,offset);
-
}while(offset < BITS_PER_PAGE && pid<pid_max &&
-
(i != max_scan || pid<last ||
-
!((last+1)&&BITS_PER_PAGE_MASK)));
-
}
-
if(map < &pid_ns->pidmap[(pid_max-1)/BITS_PER_PAGE]) {
-
++map;
-
offset = 0;
-
}else {
-
map = &pid_ns->pidmap[0];
-
offset = RESERVED_PIDS;
-
if(unlikely(last = offset)) break;
-
}
-
//通过一个扫描来的偏移量,生成一个pid
-
pid = mk_pid(pid_ns,map,offset);
-
}
-
return -1;
-
}
参考资料
linux2.6.24内核源码
professional
Linux architecture
understanding
linux kernel
阅读(3647) | 评论(0) | 转发(1) |