进程与线程

我们在学习 Java 的时候,听过太多的多线程,然后大部分开发(比如我自己)可能没有弄清楚进程和线程的根本区别。

本系列就来学习下进程和线程。

进程的概念

在多道程序环境下,允许多个程序并发执行,此时它们将失去封闭性,并具有间断性及不可再现性的特征。

为此引入了进程(Process)的概念,以便更好地描述和控制程序的并发执行,实现操作系统的并发性和共享性。

PCB

为了使参与并发执行的程序(含数据)能独立地运行,必须为之配置一个专门的数据结构,称为进程控制块(Process Control Block, PCB)。

系统利用PCB来描述进程的基本情况和运行状态,进而控制和管理进程。

相应地,由程序段、相关数据段和PCB三部分构成了进程映像(进程实体)。

所谓创建进程,实质上是创建进程映像中的PCB;而撤销进程,实质上是撤销进程的PCB。

值得注意的是,进程映像是静态的,进程则是动态的。

注意:PCB是进程存在的唯一标志!

ps: 个人理解,PCB 只是对于进程的数据结构描述而已,就像 class 只是定义描述,创建的实体才是对象本身。

定义

从不同的角度,进程可以有不同的定义,比较典型的定义有:

  1. 进程是程序的一次执行过程。

  2. 进程是一个程序及其数据在处理机上顺序执行时所发生的活动。

  3. 进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。

在引入进程实体的概念后,我们可以把传统操作系统中的进程定义为:

进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。

进程的特征

进程是由多程序的并发执行而引出的,它和程序是两个截然不同的概念。进程的基本特征是对比单个程序的顺序执行提出的,也是对进程管理提出的基本要求。

动态性:进程是程序的一次执行,它有着创建、活动、暂停、终止等过程,具有一定的生命周期,是动态地产生、变化和消亡的。动态性是进程最基本的特征。

并发性:指多个进程实体,同存于内存中,能在一段时间内同时运行,并发性是进程的重要特征,同时也是操作系统的重要特征。引入进程的目的就是为了使程序能与其他进程的程序并发执行,以提高资源利用率。

独立性:指进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。凡未建立PCB的程序都不能作为一个独立的单位参与运行。

异步性:由于进程的相互制约,使进程具有执行的间断性,即进程按各自独立的、 不可预知的速度向前推进。异步性会导致执行结果的不可再现性,为此,在操作系统中必须配置相应的进程同步机制。

结构性:每个进程都配置一个PCB对其进行描述。从结构上看,进程实体是由程序段、数据段和进程控制段三部分组成的。

进程的状态与转换

状态

进程在其生命周期内,由于系统中各进程之间的相互制约关系及系统的运行环境的变化,使得进程的状态也在不断地发生变化(一个进程会经历若干种不同状态)。

通常进程有以下五种状态,前三种是进程的基本状态。

1) 运行状态:进程正在处理机上运行。在单处理机环境下,每一时刻最多只有一个进程处于运行状态。

2) 就绪状态:进程已处于准备运行的状态,即进程获得了除处理机之外的一切所需资源,一旦得到处理机即可运行。

3) 阻塞状态,又称等待状态:进程正在等待某一事件而暂停运行,如等待某资源为可用(不包括处理机)或等待输入/输出完成。即使处理机空闲,该进程也不能运行。

4) 创建状态:进程正在被创建,尚未转到就绪状态。创建进程通常需要多个步骤:首先申请一个空白的PCB,并向PCB中填写一些控制和管理进程的信息;然后由系统为该进程分配运行时所必需的资源;最后把该进程转入到就绪状态。

5) 结束状态:进程正从系统中消失,这可能是进程正常结束或其他原因中断退出运行。当进程需要结束运行时,系统首先必须置该进程为结束状态,然后再进一步处理资源释放和回收等工作。

注意区别就绪状态和等待状态:就绪状态是指进程仅缺少处理机,只要获得处理机资源就立即执行;而等待状态是指进程需要其他资源(除了处理机)或等待某一事件。

之所以把处理机和其他资源划分开,是因为在分时系统的时间片轮转机制中,每个进程分到的时间片是若干毫秒。

也就是说,进程得到处理机的时间很短且非常频繁,进程在运行过程中实际上是频繁地转换到就绪状态的;而其他资源(如外设)的使用和分配或者某一事件的发生(如I/O操作的完成)对应的时间相对来说很长,进程转换到等待状态的次数也相对较少

这样来看,就绪状态和等待状态是进程生命周期中两个完全不同的状态,很显然需要加以区分。

转换流程

图2-1说明了五种进程状态的转换,而三种基本状态之间的转换如下:

图2-1 五种进程状态的转换

就绪状态 -> 运行状态:处于就绪状态的进程被调度后,获得处理机资源(分派处理机时间片),于是进程由就绪状态转换为运行状态。

运行状态 -> 就绪状态:处于运行状态的进程在时间片用完后,不得不让出处理机,从而进程由运行状态转换为就绪状态。此外,在可剥夺的操作系统中,当有更高优先级的进程就绪时,调度程度将正执行的进程转换为就绪状态,让更高优先级的进程执行。

运行状态 -> 阻塞状态:当进程请求某一资源(如外设)的使用和分配或等待某一事件的发生(如I/O操作的完成)时,它就从运行状态转换为阻塞状态。进程以系统调用的形式请求操作系统提供服务,这是一种特殊的、由运行用户态程序调用操作系统内核过程的形式。

阻塞状态 -> 就绪状态:当进程等待的事件到来时,如I/O操作结束或中断结束时,中断处理程序必须把相应进程的状态由阻塞状态转换为就绪状态。

进程控制

进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能。

在操作系统中,一般把进程控制用的程序段称为原语,原语的特点是执行期间不允许中断,它是一个不可分割的基本单位。

进程的创建

允许一个进程创建另一个进程。

此时创建者称为父进程,被创建的进程称为子进程。子进程可以继承父进程所拥有的资源。当子进程被撤销时,应将其从父进程那里获得的资源归还给父进程。此外,在撤销父进程时,也必须同时撤销其所有的子进程。

在操作系统中,终端用户登录系统、作业调度、系统提供服务、用户程序的应用请求等都会引起进程的创建。

创建过程

操作系统创建一个新进程的过程如下(创建原语):

  1. 为新进程分配一个唯一的进程标识号,并申请一个空白的PCB(PCB是有限的)。若PCB申请失败则创建失败。

  2. 为进程分配资源,为新进程的程序和数据、以及用户栈分配必要的内存空间(在PCB 中体现)。注意:这里如果资源不足(比如内存空间),并不是创建失败,而是处于”等待状态“,或称为“阻塞状态”,等待的是内存这个资源。

  3. 初始化PCB,主要包括初始化标志信息、初始化处理机状态信息和初始化处理机控制信息,以及设置进程的优先级等。

  4. 如果进程就绪队列能够接纳新进程,就将新进程插入到就绪队列,等待被调度运行。

进程的终止

引起进程终止的事件主要有:正常结束,表示进程的任务已经完成和准备退出运行。

异常结束是指进程在运行时,发生了某种异常事件,使程序无法继续运行,如存储区越界、保护错、非法指令、特权指令错、I/O故障等。

外界干预是指进程应外界的请求而终止运行,如操作员或操作系统干预、父进程请求和父进程终止。

过程

操作系统终止进程的过程如下(撤销原语):

  1. 根据被终止进程的标识符,检索PCB,从中读出该进程的状态。

  2. 若被终止进程处于执行状态,立即终止该进程的执行,将处理机资源分配给其他进程。

  3. 若该进程还有子进程,则应将其所有子进程终止。

  4. 将该进程所拥有的全部资源,或归还给其父进程或归还给操作系统。

  5. 将该PCB从所在队列(链表)中删除。

进程的阻塞和唤醒

正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。

可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。

阻塞原语

阻塞原语的执行过程是:

  1. 找到将要被阻塞进程的标识号对应的PCB。

  2. 若该进程为运行状态,则保护其现场,将其状态转为阻塞状态,停止运行。

  3. 把该PCB插入到相应事件的等待队列中去。

当被阻塞进程所期待的事件出现时,如它所启动的I/O操作已完成或其所期待的数据已到达,则由有关进程(比如,提供数据的进程)调用唤醒原语(Wakeup),将等待该事件的进程唤醒。

唤醒原语

唤醒原语的执行过程是:

  1. 在该事件的等待队列中找到相应进程的PCB。

  2. 将其从等待队列中移出,并置其状态为就绪状态。

  3. 把该PCB插入就绪队列中,等待调度程序调度。

需要注意的是,Block原语和Wakeup原语是一对作用刚好相反的原语,必须成对使用。

Block原语是由被阻塞进程自我调用实现的,而Wakeup原语则是由一个与被唤醒进程相合作或被其他相关的进程调用实现的。

进程切换

对于通常的进程,其创建、撤销以及要求由系统设备完成的I/O操作都是利用系统调用而进入内核,再由内核中相应处理程序予以完成的。

进程切换同样是在内核的支持下实现的,因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。

进程切换是指处理机从一个进程的运行转到另一个进程上运行,这个过程中,进程的运行环境产生了实质性的变化。

切换过程

进程切换的过程如下:

  1. 保存处理机上下文,包括程序计数器和其他寄存器。

  2. 更新PCB信息。

  3. 把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。

  4. 选择另一个进程执行,并更新其PCB。

  5. 更新内存管理的数据结构。

  6. 恢复处理机上下文。

注意,进程切换与处理机模式切换是不同的,模式切换时,处理机逻辑上可能还在同一进程中运行。

如果进程因中断或异常进入到核心态运行,执行完后又回到用户态刚被中断的程序运行,则操作系统只需恢复进程进入内核时所保存的CPU现场,无需改变当前进程的环境信息。

但若要切换进程,当前运行进程改变了,则当前进程的环境信息也需要改变。

进程的组织:控制块、程序段和数据段

进程是操作系统的资源分配和独立运行的基本单位。

它一般由以下三个部分组成。

  1. 进程控制块

  2. 程序段

  3. 数据段

进程控制块

进程创建时,操作系统就新建一个PCB结构,它之后就常驻内存,任一时刻可以存取, 在进程结束时删除。

PCB是进程实体的一部分,是进程存在的唯一标志。

当创建一个进程时,系统为该进程建立一个PCB;当进程执行时,系统通过其 PCB 了 解进程的现行状态信息,以便对其进行控制和管理;当进程结束时,系统收回其PCB,该进程随之消亡。

操作系统通过PCB表来管理和控制进程。

  • 表 2-1 PCB通常包含的内容
进程描述信息 进程控制和管理信息 资源分配清单 处理机相关信息    
进程标识符(PID) 进程当前状态 代码段指针 通用寄存器值
用户标识符(UID) 进程优先级 数据段指针 地址寄存器值
  代码运行入口地址 堆栈段指针 控制寄存器值
  程序的外存地址 文件描述符 标志寄存器值
  进入内存时间 键盘 状态字
  处理机占用时间 鼠标  
  信号量使用    

进程的通信:共享存储、消息传递和管道通信

进程通信是指进程之间的信息交换。

PV操作是低级通信方式,髙级通信方式是指以较高的效率传输大量数据的通信方式。

高级通信方法主要有以下三个类。

共享存储

在通信的进程之间存在一块可直接访问的共享空间,通过对这片共享空间进行写/读操作实现进程之间的信息交换。

在对共享空间进行写/读操作时,需要使用同步互斥工具(如 P操作、V操作),对共享空间的写/读进行控制。

共享存储又分为两种:低级方式的共享是基于数据结构的共享;高级方式则是基于存储区的共享。

操作系统只负责为通信进程提供可共享使用的存储空间和同步互斥工具,而数据交换则由用户自己安排读/写指令完成。

需要注意的是,用户进程空间一般都是独立的,要想让两个用户进程共享空间必须通过特殊的系统调用实现,而进程内的线程是自然共享进程空间的。

消息传递

在消息传递系统中,进程间的数据交换是以格式化的消息(Message)为单位的。

若通信的进程之间不存在可直接访问的共享空间,则必须利用操作系统提供的消息传递方法实现进程通信。

进程通过系统提供的发送消息和接收消息两个原语进行数据交换。

1) 直接通信方式:发送进程直接把消息发送给接收进程,并将它挂在接收进程的消息缓冲队列上,接收进程从消息缓冲队列中取得消息。

2) 间接通信方式:发送进程把消息发送到某个中间实体中,接收进程从中间实体中取得消息。这种中间实体一般称为信箱,这种通信方式又称为信箱通信方式。该通信方式广泛应用于计算机网络中,相应的通信系统称为电子邮件系统。

管道通信

管道通信是消息传递的一种特殊方式。

所谓“管道”,是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件,又名pipe文件。

向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入(写)管道;而接收管道输出的接收进程(即读进程),则从管道中接收(读)数据。

为了协调双方的通信,管道机制必须提供以下三方面的协调能力:互斥、同步和确定对方的存在。

参考资料

操作系统的基本概念

https://lgwain.gitbooks.io/os/content/unit11.html