`
ppenny
  • 浏览: 32191 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

Linux Kernel 学习笔记 3-关于process

阅读更多
Linux内核中存在process,thread,和light thread三种形式的“进程”,light thread是前两者的互补。
老版本的内核采用全局静态变量current来区分正在运行的进程,现在的内核则是将进程描述符与stack合并:
union thread_union {
struct thread_info thread_info;
unsigned long stack[2048]; /* 1024 for 4KB stacks */
};
内核使用alloc_thread_info 和 free_thread_info 宏来分配和释放存放进程(线程)信息数据结构和堆栈的内存区。
如果thread_union结构是8 KB内核mask掉esp的低13位来得到thread_info structure的基址; 如果是4KB则mask掉12位.这些通过current_thread_info( )函数完成, 产生的汇编代码与一下类似:
movl $0xffffe000,%ecx /* or 0xfffff000 for 4KB stacks */
andl %esp,%ecx
movl %ecx,p
执行这三条指令之后p存用目前运行在cpu上的进程执行这段代码的tHRead_info structure的指针。
大部分情况下内核需要的是进程描述符而不是thread_info structure的结构指针。内核通过轰 current来的到运行在cpu上的进程的进程描述符,它等同于current_thread_info( )->task产生的汇编代码与一下类似:
movl $0xffffe000,%ecx /* or 0xfffff000 for 4KB stacks */
andl %esp,%ecx
movl (%ecx),p
因为task域位于thread_info struct结构的0偏移地址处, 执行这段代码后p就指向了当前cpu运行进程的得到进程描述符。
current宏经常作为内核代码的前缀来访问进程描述符,例如 current->pid返回当前cpu上正在运行的进程的id。


关于Linux内核中大量使用的链表:
对一个操作系统来说,链表作为基本的数据结构是绝对不会少的,对于linux更是如此,无论是在内存管理,定时器,进程管理还是在驱动程序的编写中链表大量存在,双向链表最为好用,Linux为开发者提供了相应的宏来完成创建、释放链表、上溯链表owner等等功能,从而提高了效率。注意:linux中的这种链表结点叫做list_head,linux中这种双向链表的使用并不是用pre、next指针指向它的前一个和后一个结点的整个数据结构,而仅仅是指向其下一个结点的对应的list head结构,要访问其所指向的整个数据结构,是通过内核提供的特有的宏(类似于上溯到其owner的宏)来访问的。其实list head可以被看作是被嵌入在特定数据结构中的。


进程链表:
每个task_struct结构都包括一个list_head域 它的pre和next分别指向前一个和后一个task_struct结构。这个进程链表的表头是init_task的task描述符;也就是所谓的0号task或者叫做wrapper(就是那个idle)它的pre指向新插入的进程描述符的list_head。SET_LINKS和REMOVE_LINKS宏用来插入和善除尽程度列中的进程描述符。这些宏的操作也会考虑到进程间的关系。


关于运行队列:
用来加快task调度的方法的一种是通过将运行队列中的task划分成多个运行队列链表,每一个队列有一个task优先级。每一个task_struct描述符结构包括一个list_head类型的run_list成员。如果task具有优先级k,那么就把整个task插入到k优先级的运行队列中。这是一个通过增加数据结构负载性来提高运行效率的典型例子。
像我们看到的一样,内核需要一个复杂的数据结构来保存进程运行队列的数据,但是, 主要的数据结构是由一运行队列中的进程描述符。所有的这些链表都由prio_array_t数据结构实现。
Type Field Description
int nr_active 链入入链表的进程描述符数目
unsigned long [5] bitmap 优先级映射表,list不为空对应的一位置1
struct list_head [140] queue 140个链表的表头
enqueue_task(p,array)函数向运行队列承认一个进程描述符,代码本质上类似于:

list_add_tail(&p->run_list, &array->queue[p->prio]);
__set_bit(p->prio, array->bitmap);
array->nr_active++;
p->array = array;


进程间关系:
real_parent 指向创建进程p的进程描述符,或是指向1号进程号的进程描述符(父进程不存在后会被作为1好进程的子进程)
parent 当前的父进程。
children 进程P窗间的所有子进程的链表头。
sibling 拥有相同父进程的兄弟进程。
除此之外还存在其他的进程间关系:一个进程可以是一个进程组伙食一个登录会话的头,它可以作为一个领导进程来跟踪组中的其它进程。

PID hash表:
在许多情况下进程必须能够能通过PID来访问进程描述符。一个近程可以发送kill()系统调用而将令一个进程的pid作为参数传送。内核通过pid来找到进程描述符,并找到其挂起状态数据。
扫描进程列表查看pid域可以实现,但是效率低。linux通过四个hash表来加速查找,由于进程描述符包括代表不同类型的pid,而不同pid要求有自己的hash查找表,所以会引入四个hash表。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics