理解多线程编程,掌握什么是线程和进程以及它们之间的关系很重要。
1. 何为进程?
进程就是执行中的一段程序。一旦程序被装载到了内存中并准备执行,它就是一个进程,具备文本(text)、数据(data)和堆栈片段(stack)以及它自己的资源(像文件、对象句柄、设备、信号量、互斥量、管道等)。OS管理进程,进程相关的信息都包含在进程信息块的结构中。它里面包含有进程名字(进程ID)、内存和分配资源的指针、寄存器保存区以及优先级,运行所在的CPU,命令行参数和环境块的指针等。
进程的属性包含保存在进程信息块中的部分或全部信息。I/O计数器和虚拟内存操作计数器是记录I/O操作类型和数量以及进程的线程所执行虚拟内存操作的变量。退出状态是进程终止的原因。异常/调试端口为进程间通信渠道。当进程的线程之一产生一个异常或调试进程时,操作系统发送消息给异常/调试端口。
进程的环境是一个由系统和用户定义指针的集合,它为进程设置缺省信息。环境变量协助定义进程的行为。环境列表的创建和初始化由用户完成,其中一些变量具有系统初始化的缺省设置。当进程运行时,它寻找这些变量,并根据它们调整自己的行为。这些变量包括用户初始工作目录、当前路径以及搜索命令的目录列表。还可以通过调用函数来创建和初始化特定进程的变量(在此有时又被称做局部变量)。
2. 进程状态
进程状态是进程某时某刻所处的模式或条件,它决定了将来的事件以及进程可能进入的状态。
准备执行的进程处于就绪状态,它位于就绪队列中。然后由OS scheduler从此队列中挑选出一个进程在处理器上执行,此时被选进程就处于运行状态。如果进程等待某个事件的发生而不能执行,则它就进入阻塞状态。就绪、运行以及阻塞被看作激活状态,是最常见的实现进程状态。
新建和空闲是新创建进程的初始状态。此状态下,进程准备执行但尚不能运行。
在单处理器系统中,只能有一个进程处于备用状态(standby state)。它是就绪状态之后、运行状态之前的状态。挂起-就绪状态和挂起-阻塞状态是两种挂起进程执行的状态。此状态下的进程为非激活。
进程完成执行后,退出系统,所有相关信息都被删除,而且地址空间与相关资源也都被释放。此时进程为完成或终止状态。在释放它之前,进程则处于僵化状态。
进程的不同状态之间会根据外界事件、自身条件等的变化而进行相互转换。
进程状态变化有不同的原因。对于就绪、运行和阻塞状态有4种可能的转换:
1>. 分派(diapatch);
2>. 时间耗尽(timerunout);
3>. 阻塞(block);
4>. 唤醒(wakeup)。
当进程由就绪队列中被提交给处理器,此进程就被分派了。它会运行一段时间,称做时间片(quantum)。如果时间段用完,进程就会从运行状态返回到就绪状态(此转换称做时间耗尽);而如果进程需要I/O或等待某事件的发生而不能继续运行,则会从运行状态转换到阻塞状态(这种转换即为阻塞)。
进程还可能被挂起,有以下一些原因:
1>. 系统运行状况不良或中断了;
2>. 系统可能超载,把一些进程挂起可能会减轻超载;
3>. 用户可能决定挂起进程,因为它给了不正确的结果,将它置为挂起状态后,直到问题得到更正为止;
4>. 为了等待另一个进程同步执行,自动进入挂起状态;
5>. 进程请求I/O操作,而因为某些原因,对应的资源不可用或I/O操作没有发生,此时进入挂起状态。
就绪、运行和阻塞进程都可以被挂起。如果阻塞进程的资源不可用,则进程可能被挂起,直到资源可用为止。而在运行中,用户可以因为进程产生了不正确的结果而决定挂起它。一旦问题纠正后,它再被重新激活,回到就绪或阻塞状态。
一旦进程执行完成,它可能退出系统,并销毁进程。此时它的所有资源将被释放,而且定义进程的所有信息,包括表或列表中的项都被删除,这是一个进程的正常终止。也有可能进程在完成执行后,但其资源没有被释放,与进程相关的信息没有被删除,此时的进程将处于僵化状态。
3. 进程优先权
系统使用优先权方案(priority scheme)来决定就绪进程使用处理器的顺序。进程被分配一个优先类以及该类中的优先级(类和级从低到高)。系统从就绪队列中选择一个最高优先权进程来使用处理器。同一类中同一级别的就绪进程按一种轮询方式来使用处理器,在更高优先权进程进入系统前,每个进程都有机会使用处理器。进程由外部资源和系统分配优先权。子进程继承了父进程的优先权,之后它又可以对其进行改变。如果没有分配优先权,则赋予它缺省优先权。
优先权可以是动态,也可是静态。进程具有一个初始分配的优先权,只保持短暂的时间。当系统发生变化时,可能需要改变进程的优先权来增加系统的响应速度。动态优先权方案可以帮助较低优先权进程使用处理器。高优先权进程可以降低为较低优先权,而较低优先权进程可以提升优先权。一个进程可被分配 一个静态高优先权来确保它总是使用处理器。在某些系统中,抢先进程的优先权值被降低,而等待进程的优先权值被提升。
4. 进程上下文切换
进程的上下文包括系统需要的进程有关信息以及在重新启动进程环境时需要的相关信息。上下文包含可执行映像(executable image)、程序计数器(program counter)、寄存器(register)、堆栈(stack)以及用于动态和静态变量的分配内存。系统可能也需要知道有关进程状态、进程和用户标识、规划和计数信息、优先权以及I/O方面的信息。还需知道是否等待着某个事件以及进程占有资源的信息。
5 进程间关系
进程创建后就被分配了一个独立的地址空间。文本片断通过一个可执行映像初始化。数据和堆栈片断以及进程的属性都同时被初始化。进程对应的资源也被分配。进程的环境包括工作目录、标准输入、输出、错误、路径等。它是一个独立的实体,可以创建或产生其他进程。原始进程称做父(进程),产生的进程称做子(进程)。子进程可以反过来成为父进程,于是创建了一个进程的树或层次结构。
父和子进程描述了它们之间的关系。进程的关系决定了进程的初始化、终止以及父进程对子进程的控制。
Unix环境中,创建子进程是对其父进程的复制。子进程具有父进程地址空间的一份拷贝。父进程和子进程的数据和堆栈片断是私有的,但它们共享文本片断。子进程和父进程可以访问在两种进程地址空间之外创建的共享内存区域。子进程可以继承父进程的环境、优先权、规划属性以及父进程的部分资源(包括文件描述符)。
子进程与父进程是两个相互独立的进程。它们具有独立的程序和堆栈计数器。子进程可以更改它的变量值而不影响父进程。对于创建子进程前父进程打开的文件和设备等资源,子进程可以立即访问,但创建子进程后,子进程就不能访问之后由父进程分配的任何额外资源。父进程也不能访问子进程分配的任何资源。
Unix环境中,直到子进程返回退出代码到其父进程中时,它才能得到释放。如果父进程不能接受退出代码,或者由于该代码被忽略,或者由于父进程过期了,那么子进程就不能被释放,将一直驻留在内存中,直到它与另一个接受它的退出代码的进程联合为止。
如果父子关系不再维持,子进程可以终止和释放,且与派生它的进程无关。
当父进程挂起执行,直到子进程终止为止,这些进程就是同步执行(synchronous execution)。
父和子进程也可以异步运行。如果父和子进程相互独立运行,这些进程就是异步执行。
异步运行的子进程可以是分离进程(detached process)。分离进程是与其父进程并发运行,但没有从父进程继承任何资源的子进程。父进程终止时,它们不会终止。它们与父进程分离。父进程可以消灭分离的子进程。
6. 进程映射
进程有3个片断:文本片断、堆栈片断以及数据片断。进程的地址空间既有物理模型也有逻辑模型。物理模型有关进程如何实际保存在RAM中。逻辑模型在布局的底端有文本断、然后是数据断,堆栈断则位于顶部。
7. 进程资源
资源指进程执行任务所使用的硬件设备和软件素材。它可以是进程在任何给定时间使用的任何东西,可以是数据源或信息,或者是显示数据或信息的方式。进程可以使用由操作系统支持和管理的各种不同类型的资源。一些资源为多个进程所共享。这样的资源允许多个进程并发访问,或者在允许另一个进程访问前暂时只允许单个进程的访问。
基本的资源类型有如下三种:
1>. 硬件;
2>. 软件;
3>. 数据。
硬件资源为物理设备。像处理器,主内存和RAM,以及I/O设备。
数据是另一种资源。进程可以使用和操纵数据(比如文件、对象和句柄)、系统数据(比如环境变量)以及全局定义变量(比如信号量和互斥量)。可以通过使用信号量(semaphore)、互斥量(mutex)、临界区(critical section)等来实现同步。
软件资源可以是其他进程或动态链接库。共享软件资源的进程共享程序的一个拷贝,但拥有数据的单独拷贝。程序的进程虚拟地址被映射到相同的物理内存位置。使用时不发生变化的程序可以被多个进程使用。使用时可能发生变化的程序在进程每次使用它时重新初始化。
为了让系统区分各个进程,以确保足够的系统操作性能。进程可以给进程任意分配优先权。可以基于进程使用的资源分配进程优先权。通信密集进程需要相对高的优先权。使用或控制时间敏感设备的进程甚至需要更高的优先级。