哈希表的主要作用是根据进程的pid可以快速地找到对应的进程,但它没有反映进程创建的顺序,也无法反映进程之间的亲属关系,因此引入进程双向循环链表。
struct task_struct task;
unsigned long stack[INIT_TASK_SIZE/sizeof(long)];
};
#define init_task (init_task_union.task) //声明init_task
union task_union init_task_union = { INIT_TASK(init_task_union.task) }; // 初始化init_task_union
struct task_struct init_task,它对应的是进程0(pid为0),也就是所谓的空进程,它是所有进程的祖先。
宏SET_LINK用来向该双循环链表中插入一个进程描述符*p,插入位置在*init_task.prev_task和init_task之间,即双向循环链表的尾端,插入后*p的父进程为创建其的进程,兄进程为插入前的tail,插入后*p作为tail,即链表的尾端,这刚好吻合创建时间顺序:
(p)->next_task = &init_task; \
(p)->prev_task = init_task.prev_task; \
init_task.prev_task->next_task = (p); \
init_task.prev_task = (p); \ //前四句进行插入操作
(p)->p_ysptr = NULL; \ //指向弟进程的指针初始化为NULL.
if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \ //插入后p的兄进程初始化为插入前父进程的tail子进程
(p)->p_osptr->p_ysptr = p; \ //插入前父进程的tail子进程的弟进程为插入进程p
(p)->p_pptr->p_cptr = p; \ //父进程的描述符的p_cptr记录最后插入的子进程地址
} while (0)
p_pptr(Parent),父进程,记录当前进程的父进程是谁。
p_cptr(Child),子进程,记录当前进程的最后一个子进程是谁。
p_ysptr(Younger sibling), 弟进程,记录当前进程的弟进程是谁,如果当前进程是其父进程的最后一个子进程,则其为NULL,即当前进程没有弟弟。
p_osptr(Older sibling),兄进程,记录当前进程的兄进程是谁,一般指向插入前*init_task->prev_task。