123
分类: LINUX
2013-09-23 13:07:38
原文地址:进程的组织 作者:chenjifeng
内核在创建了很多进程后,如何管理组织这些进程就显得尤为重要了,要不然你的内核中就会充满许多无组织的进程。从task_struct中的几个字段就可以知道内核是如何组织管理这些进程的。
|
字段:tasks:
进程在内核中是以PCB的形式存在的,那么一个操作系统有很多进程,即就是很多PCB同时存在,那么如何有效的管理进程的组织方式,显然是一个很重要的问题,最简单的是将这些PCB直接用双向链表链起来,这样就可以遍历内核中所有的进程了,内核也确实这样做了,task_struct 结构中的tasks字段就是用来连接所有进程描述符的。在这个链中,链表头是init_task描述符,它是所谓的0进程或者swapper进程的描述符,init_task.tasks.prev字段指向链表中最后插入的进程描述符的tasks字段。用图来简单的表示下:
可以用宏for_each_process(p)实现进程链的遍历,此宏定义如下:
define for_each_process(p) \
for (p = &init_task ; (p = next_task(p)) != &init_task ; )
p首先指向0进程的描述符,然后在指向下一个进程描述符,在指向下一个描述符时用到了宏next_task(p),这个宏是用来找寻p描述符的下一个进程描述符的。定义如下:
#define next_task(p) \
list_entry(rcu_dereference((p)->tasks.next), struct task_struct,
tasks)
又出现了一个宏list_entry( ),这个我在前面的文章中介绍过,这里不再多说。
虽然这样可以访问到系统中的任何一个进程,但是其效率是很低的,这对于内核来说绝对不能容忍,所以内核大师们用到了其他的办法。
字段run_list:
在内核中有一个队列是可运行队列,即就是保存了进程状态为:TASK_RUNNING的进程PCB,这个队列就是run_list字段,内核为每个优先级都设置了一个可运行队列,即如果一个进程的优先级为k(取值范围是0到139),那么内核就将此进程链入到优先权为k的可运行队列中,这样CPU可以从高优先级可可运行队列去选择要运行的进程,这样提高了效率,但为此却要把所有可运行的进程队列拆分成140个不同的队列。这些队列有一个单独的数据结构prio_array_t数据结构 来实现,定义如下(摘自linux2.6.11):
|
其中的nr_active为链表中进程描述符的数量,bitmap是优先权位图,当且仅当某个优先权的进程链表不为空时设置相应的为标志。queue是140个优先权队列的头节点。以上的组织方式在2.6.11中是如此,但在2.6.23中却只有将运行队列的这种组织方式取消了,只有实时进程才有这种组织结构,我的理解是这样作增强了内核的实时性。
字段 children、 sibling和thread_group:
这三个字段分别是进程的子进程链表、兄弟链表和线程组链表。可以根据children和sibling字段找到一个进程家族的进程。进程在创建后必须处于某一个组中,通过thread_group来形成一个进程组。
字段real_parent、parent和group_leader:
real_parent指向了创建此进程的描述符,如果此进程的父进程不存在就指向进程1(init)的描述符,如用户在shell运行一个后台程序,当用户退出shell 时,此后台进程就会成为init的子进程。parent指向了当前父进程,于real_parent通常相同,但有时不同,如当另一个进程发出监控此进程的ptrace()系统调用请求时,跟踪进程会将被跟踪进程的parent字段改为跟踪进程的描述符,当跟踪结束时再将parent改回来,即改为real_parent字段的值。group_leader指向线程组领头进程的描述符。
在很多情况下内核必须能很快的找到对应的进程描述符,如杀死一个进程,杀死一组进程,这些都需要内核在很短的时间内找到这些进程描述符。为了达到这个目的,内核采用了哈希表结构来实现。在内核中有四个哈希表,为什么需要三个哈希表,是因为进程描述符包含了表示不同类型PID的字段,而每种类型的PID需要它自己的哈希表,比如想找到一个进程的PID或者一个进程组的PID。这四个哈希表在内核初始化期间被动态地分配了空间,并把他们的地址存入pid_hash数组中。pid_hash初始化在kernel/pid.c中的pidhash_inti()函数中,其中为pid_hash申请空间保存四个哈希表的操作是:
pid_hash = alloc_bootmem(pidhash_size * sizeof(*(pid_hash)));
四个哈希表类型被定义成了枚举类型,即PIDTYPE_PID=0,PIDTYPE_PGID=1依次往下。
enum pid_type
{
PIDTYPE_PID,
PIDTYPE_PGID,
PIDTYPE_SID,
PIDTYPE_MAX
};
看下面的图可以帮助理解,现在的内核已经没有PIDTYPE_TGID那一项了。