Linux内核之进程(1)

进程:程序执行的一个实例,在Linux源代码中,常把进程称为任务(task)或者线程(thread)。

从内核观点来看,进程的目的是担当分配系统资源(CPU的时间、内存等)的实体。

当一个进程创建时,几乎与父进程相同,接受父进程地址空间的一个逻辑拷贝,并从进程创建系统调用的下一条指令开始执行与父进程相同的代码。尽管父子进程可以共享含有程序代码的页,但是它们有各自独立的数据拷贝(栈和堆)。因此子进程对一个单元的修改对父进程是不可见的(反之亦然)。

早期的系统采用这种模式,现代Unix支持多线程应用程序——拥有相对独立执行流的用户程序共享应用程序的大部分数据结构。一个进程由几个用户线程组成,每个线程代表进程的一个执行流。现在大部分应用程序都是pthread库的标准库函数集编写的。

使用轻量级进程基本上可以共享一些资源,诸如地址空间、打开的文件等。只要其中一个修改共享资源,另一个就能立即查看这种修改。两个线程访问共享资源时就必须同步它们自己。

实现多线程应用程序的一个简单方式是把轻量级进程和每个线程关联起来,这样线程之间就可以通过简单地共享同一内存地址空间、同一打开的文件集等来访问相同的程序数据结构集;同时每个线程可以由内核独立调度,以便一个睡眠的同时另一个仍然是可以运行的。

1.进程状态

进程描述符中state字段描述了当前进程所处的状态。

1)可运行状态:进程要么在CPU上执行,要么准备执行。

2)可中断的等待状态:进程被挂起,直到某个条件为真。产生一个硬件中断,释放进程正等待的系统资源,或传递一个信号都是可以唤醒进程的条件。

3)不可中断的等待状态:与可中断的等待状态类似,但有一个例外,把信号传递给睡眠进程不能改变它的状态。

4)暂停状态:进程执行被暂停。当进程接收到,SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOUT信号后,进程进入暂停状态。

5)跟踪状态:进程的执行已由debugger程序暂停。当进程被另一个进程监控时,任何信号都可以把这个进程置于TASK_TRACED状态。

6)僵死状态:进程的执行被终止,但是父进程还未发布wait4()或waitpid()系统调用来返回有关死亡进程的信息。发布wait()类系统调用前,内核不能丢弃包含在死进程描述符中的数据,因为父进程可能还需要它。

7)僵死撤销状态:最终状态:由于父进程刚发出wait4()或者waitpid()系统调用,因为进程由系统删除。为了防止其他执行线程在同一进程上也执行wait()类系统调用,因为将进程由僵死状态改为僵死撤销状态。

2.标志一个进程

能被独立调度的执行上下文都必须拥有自己的进程描述符。

进程和进程描述符之间有非常严格的一一对应关系,这使得用32位的进程描述符标识进程成为一种方式,进程描述符指针指向这些地址,内核对进程的大部分引用都是通过进程描述符指针进行的。

类Unix系统使用进程标识符process ID(PID)的数来标识进程,PID存放在进程描述符的pid字段中,PID被顺序编号。在缺省情况下,最大的PID是32767;64位体系结构中,系统管理员可以把PID的上限扩展到4194303

3.进程描述符处理

进程是动态实体,生命周期从几毫秒到几个月。因此,内核必须能够同时处理多个进程,并把进程描述符放到动态内存中,而不是放到永久的分配各内核的分配区。

4.进程间的关系

程序创建的进程具有父子关系,多个子进程之间具有兄弟关系。进程1(init)是所有进程的祖先。

image

image

image

顺序扫描进程链表(双向链表)并检查进程描述符的pid字段是可行但相当低效的。为了加速查找,引入4个散列表。

需要4个散列表是因为进程描述符包含了表示不同类型的PID字段,每种类型的PID需要自己的散列表。

image

内核初始化期间动态地为4个散列表分配空间,并把他们存入pid_hash数组。一个散列表的长度依赖于可用RAM的容量,例如:一个系统拥有512M的RAM,那么每个散列表就被存在于4个页框中,可以拥有2048个表项。

具有链表的散列法比从PID到表索引的线性转换更优越。

PID散列表包含四个pid结构的数组。

image

image

我们考虑线程组4351的PID链表:散列表的进程描述符的pid_list字段中存放链表头,同时每个PID链表中指向前一个元素和后一个元素的指针也存放每个链表元素的pid_list字段中。

copyright@2015 liupan

liu.pan@datatom.com

posted @ 2015-04-21 14:56  silianpan  阅读(269)  评论(0编辑  收藏  举报