日期:2014-05-16 浏览次数:20721 次
第一章首先从进程开始讲起,进程是操作系统最基本的东西,我们依靠进程提供服务,利用进程完成我们需要完成的工作。首先需要明白什么是进程。它是一个动态实体,是程序的一次执行过程。一个进程需要一些必须的资源才能够运行,而最基本的资源就是 CPU 和内存。进程获得 CPU 它就处于运行状态,进程的运行肯定需要程序段,数据段,堆,栈这样的资源,而这些就是内存提供的。在Linux中的实现是两种虚拟机制,被称作是“虚拟处理器”和“虚拟内存”。虚拟处理器的概念就好像是每个进程独占CPU,虚拟内存的概念是每个进程拥有自己的内存地址空间,而这个地址空间涵盖了整个内存空间。进程的创建工作是通过 fork() 函数,进程的终止是通过 wait4() 函数,这些内容的详细介绍将在下文提及。
进程描述符用于描述一个进程,在其中存储了一个进程的所有信息,每个进程都有一个进程描述符。而所有的进程描述符都被存储在一个双向循环链表中。由此,我们可以想到,从链表中的任何一个进程开始,我们可以遍历系统的所有进程。进程描述符比较重要的字段有 :进程PID,进程的父进程,进程的状态等等。进程描述符是由 slab 分配器分配的,它是作为页高速缓存特殊存在的一种结构。当一个进程创建时,slab 分配器分配一个进程描述符给它,当进程消亡时,进程描述符被 slab 回收,可以循环利用。
和进程描述符密切相关的是一个结构--thread_info。这个结构的定义如下:
union thread_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
我们可以看到,它是和一个栈存储在一起的,这个栈被称作是内核栈。thread_info 的位置在内核栈的尾端(往低地址方向增长)。thread_info 中存储了进程描述符的地址,所以,我们可以很方便的获得当前进程的描述符,这对于 X86 架构的计算机是非常优良的设计,因为我们可以不占用昂贵的寄存器来存储地址。
说了这么多,好吧,下面这张图更能显示它们之间的关系:
进程是一个活动的实体,当然,它是有生命的,所以,它就必然会有生命中的各个状态。一般情况下,进程的状态会被划分为五种。
1.TASK_RUNNING 进程处于就绪态或者是运行态。这个状态很有意思,它可以代表进程的两种很相似的状态。这两种状态的区别是进程时候获得 CPU 的使用权,如果获得了,那么进程处于运行态,反过来,如果没有,那么进程就处于就绪态。
2.TASK_INTERRUPTIBAL 进程处于可中断状态,此时进程由于某些情况不能运行而被挂起,这时会引起进程的切换,当进程希望的事件到来或者接收到信号时,进程就有可能会被唤醒,等待重新被调度执行。
3.TASK_UNINTERUPTIBLE 进程处于不可中断状态,从命名方式我们就可以看出,它和上一种进程状态是很相似的,的确,它们在表现上也是极为相似的,都是由于进程期待的事情没有发生而被挂起,不能运行。但是,这种状态的进程不能被信号唤醒,所以,我们不能发送一个终止信号给进程使进程死掉,所以,这种状态的进程并不多,一个常见的例子就是,进程等待磁盘的数据传输。
4.TASK_PTRACED 进程处于被跟踪的状态,这时,会发生一些很有意思的事,进程的父进程会变成跟踪进程,当然,这只是临时的,此时,进程描述符的 parent 字段会被修改。
5.TASK_STOPPED 进程处于死亡状态,这时的进程不能再被投入运行,它此刻拥有的资源也仅仅是 进程描述符,内核栈,thread_info 结构,此时,进程会发送信号给父进程,等待父进程收回它的资源。
进程上下文其实就是进程切换的过程,就是指进程的所有信息。
我们都知道,类UNIX系统组织系统中的文件都是以树的形式完成的,根则是文件系统的根目录,之后是子目录,接着是子目录的子目录。。。,最后才是文件。由于这种思想能够很好的管理计算机资源,所以,系统中的进程也是以这种方式来管理的。进程被组织成一棵树,树的根是进程1 ,也就是 init 进程。
熟悉 Linux 编程的朋友都知道一个函数