操作系统学习笔记(三)进程与线程

我们为什么需要进程?
进程又是什么,由什么组成?
进程如何解决问题?

进程的概念和特征

进程的概念

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

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

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

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

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

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

值得注意的是,进程映像是静态的,进程是动态的,PCB是进程存在的唯一标志

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

  • 进程是程序的一次执行过程。
  • 进程是一个程序及其数据在处理机上顺序执行时所发生的活动。
  • 进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。(这里需要准确理解系统资源,它指处理机,存储器和其他设备服务于某个进程的“时间”,例如把处理机资源理解为处理机时间片才是准确的)

进程的特征

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

  • 动态性。进程是程序的一次执行,它有着创建,活动,暂停和中止等过程,具有一定的生命周期。动态性是进程最基本的特征
  • 并发性。多个进程实体同时存在于内存中,能在一段时间内同时运行,并发性是进程的重要特征,同时也是操作系统的重要特征。引入进程的目的就是使程序能够与其他进程的程序并发执行
  • 独立性。进程实体是一个能独立运行,独立获得资源和独立接受调度的基本单位。凡是未建立PCB的程序,都不能作为一个独立的单位参与运行
  • 异步性。由于进程的相互制约,使得进程具有执行的间断性,即进程按个子独立的,不可预知的速度前进。异步性会导致执行结果的不可再现。为此操作系统必须配置与之对应的进程同步机制。
  • 结构性。每个进程都配置一个PCB对其进行描述,从结构上看,进程实体是由程序段,相关数据段和PCB组成的。

进程的状态和转换

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

  1. 运行态。进程正在处理机上运行。在单处理机环境下,每个时刻最多只有一个进程处于运行态。
  2. 就绪态。进程获得了除处理机外的一切资源,一旦得到处理机,便可立即运行。
  3. 阻塞态。又称等待态,进程正在等待某一事件而暂停运行,如等待某资源(不包括处理机)为可用或等待输入输出完成。即使处理机空闲,该进程也不能运行。
  4. 创建态。该进程正在被创建,尚未转到就绪态。进程的创建通常需要多个步骤,首先申请一个空白的PCB,并向PCB中填写一些控制和管理进程的信息;然后系统为该进程分配运行时所必需的资源,最后把进程转为就绪态
  5. 结束态。进程正从系统中消失,可能是进程正常结束或其他原因中断退出运行。进程需要结束运行时,系统首先必须将该进程置为结束态,然后进一步处理资源释放和回收等工作。

注意区分就绪态和等待态,就绪态指的是只缺少处理机,只要获得处理机资源就可以立即运行,而等待态则是指进程需要其他资源或等待某一事件。
之所以把处理机和其他资源分开,是因为在分时系统的时间片轮转机制中,每个进程分到的时间片是若干毫秒,也就是说,进程得到处理机时间很短且非常频繁,进程在运行过程中实际上是频繁地切换到就绪态的。

进程的组织

进程是一个独立的运行单位,也是操作系统进行资源分配和调度的基本单位,它由以下三部分组成,最核心的是进程控制块(PCB)。

进程控制块

进程创建结束时,操作系统会为它创建一个PCB,该结构之后会常驻内存,任意时刻都可以存取,并在进程结束时删除。PCB是进程实体的一部分,是进程存在的唯一标志。

当进程执行时,系统通过其PCB了解其现行状态和优先级,以便操作系统进行控制和管理。

  • 在调度某个进程时,要根据其PCB中所保存的处理机状态信息,设置该进程恢复运行的现场,并根据其PCB中程序和数据的内存地址,找到其程序和数据。
  • 进程在运行过程中,当需要和与之合作的进程实现同步,通信或者访问文件时,也需要访问PCB。
  • 当进程由于某种原因而暂停运行时,又需要其断点的处理及环境保存在PCB中

系统总是通过PCB对进程进行控制,亦即系统唯有通过进程的PCB才能感知到该进程的存在。

PCB主要包括进程描述信息,进程控制和管理信息,资源分配清单和处理机相关信息等。

  • 进程描述信息。进程标识符:标志各个进程,每个进程都有一个唯一的标识号。用户标识符:进程归属的用户,进程标识符主要为共享和保护服务。
  • 进程控制和管理信息。进程当前状态:描述进程的状态信息,作为处理机调度的依据。进程优先级:描述进程抢占处理机的优先级,优先级高的进程可优先获得处理机。
  • 资源分配清单。用于说明有关内存地址空间或虚拟地址空间的情况,所打开的文件列表或者所使用的设备信息。
  • 处理机相关信息,也称为处理机上下文,主要指处理机中各寄存器的值。当进程处于执行态时,处理机的许多信息都在寄存器中。当进程被切换时,处理机状态信息必须保存在相应的PCB中,以便在该进程重新执行时,能从断点继续执行。

PCB的组织方式有链接方式和索引方式。链接方式将同一状态的PCB链接成一个队列,不同状态对应不同的队列,也可以把处于阻塞态的进程的PCB,根据其阻塞原因不同,排成多个阻塞队列。索引方式将同一状态的进程组织到一个索引表中,索引表的表项指向相应的PCB

程序段

程序段就是能被进程调度程序调度到的CPU执行的程序代码片。注意,程序可被多个进程共享,即多个进程可以运行同一个程序。

数据段

一个进程的数据段,可以是进程对应的程序加工处理的原始数据,也可以是程序执行时产生的中间或最终结果。

进程控制

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

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

进程的创建

允许一个进程创建另一个进程,此时创建者又称为父进程,被创建的进程称为子进程。子进程可以继承父进程所拥有的资源。当子进程被撤销时,应将其从父进程那里获得的资源还给父进程。此外,在撤销父进程时,通常也会撤销其所有子进程。
操作系统创建一个新进程的过程如下(创建原语):

  • 为新进程分配一个唯一的进程标识符,并申请一个空白PCB(PCB是有限的),若PCB申请失败,则进程创建失败。
  • 为进程分配其运行所需要的资源,如内存,文件,I/O设备等(在PCB中体现)。这些资源或从操作系统中获得,或仅从父进程获得。如果资源不足,则不是创建失败,而是出于创建态,等待资源。
  • 初始化PCB,主要包括初始化标志信息,初始化处理机状态信息,初始化处理机控制信息以及设置进程优先级等。
  • 若进程就绪队列能够容纳新进程,则将新进程插入就绪队列,等待被调度执行。

进程的终止

引起进程终止的实践主要有:正常结束,表示进程的任务已经完成并准备退出运行;异常结束,表示进程在运行时遇到了某种异常,使程序无法继续运行,如存储区越界,保护错,非法指令,特权指令错,运行超时,算术运算错,I/O故障等;外界干预:指进程应外界的请求而终止运行,如操作员或操作系统干预,父进程请求,父进程终止等。
终止原语如下:

  • 根据被终止的进程标识符,检索出该进程的PCB,从中读取该进程的状态。
  • 若被终止进程处于运行态,立即终止该进程的执行,将处理机资源分配给其他进程。
  • 若该进程还有子孙进程,应将其子孙进程终止。
  • 将该进程所拥有的全部资源,或归还其父进程,或归还操作系统。
  • 将该PCB从所在队列中删除。

进程的阻塞和唤醒

正在执行的进程,由于期待的某些事件没有发生,进程便通过调用阻塞原语,使自己从运行态变为阻塞态,可见,阻塞时进程自身的一种主动行为,也因此只有处于运行态的进程,才有可能变为阻塞态,阻塞原语如下:

  • 找到要被阻塞的进程的PCB
  • 保护其现场,将其状态变为阻塞态,停止运行
  • 把该PCB插入相应事件的等待队列,将处理机资源调度给其他就绪进程。

当被阻塞的进程期待的事件出现时,由有关进程调用唤醒原语:

  • 在该事件的等待队列中找到相应的PCB
  • 将其从等待队列中移除,并设置其状态为就绪态
  • 将该PCB插入就绪队列,等待调度。
    阻塞原语和唤醒原语必须成对出现,如果在某进程中调用了Block原语,必须在与之合作的或其他相关进程中安排一条对应的warkup原语。

进程通信

进程通信指的是进程间的信息交换。PV操作是低级通信方式,高级通信方式是指以较高的效率传输大量数据的通信方式。高级通信方式主要有以下三类:

共享存储

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

在对共享空间进行读写操作时,需要使用同步互斥工具(如PV操作)。

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

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

消息传递

进程间的数据交换以格式化的消息(Message)为单位。若通信的进程之间不存在可直接访问的共享空间,则必须利用操作系统提供的消息传递方法实现进程通信。

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

这种方式隐藏了通信细节,是当前应用最广泛的进程间通信方式,在微内核操作系统中,微内核和服务器之间的通信就采取了消息传递。

  1. 直接通信方式、发送进程直接把消息发送给接收进程,并将它挂在接收进程的消息缓冲队列上。接收进程从消息缓冲队列中取得消息。
  2. 间接通信方式、发送进程将消息发送到某个中间实体,接收进程从中间实体取得消息,中间实体一般称为“信箱”。

管道(共享文件)通信

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

向管道提供输入的发送进程,以字符流形式将大量数据送入写管道;而接收管道输出的接收进程则从管道中接收数据。为了协调双方通信,管到机制必须提供以下三方面的协调能力:互斥,同步和确定对方存在。

本质上讲,管道也是一种文件,但是又和一般文件不同,管道可以克服使用文件通信的两个问题:

  • 限制管道大小。实际上,管道是一个固定大小的缓冲区,这使得它的大小不会不加检验地增长。使用单个固定缓冲区也有问题,比如管道满的时候,这种情况,随后对管道的write调用会被阻塞,等待某些数据被读出。
  • 读进程也可能工作比写进程快。当管道空的时候,一个随后的read调用将默认地被阻塞。

从管道读数据只能是一次性操作,数据一旦被读取,就释放空间以便写更多数据。管道只能采取半双工通信,即某一时刻只能单向运输。

线程和多线程模型

线程的基本概念

引入进程的目的是更好的使多道程序并发,提高资源利用率和系统吞吐量,而引入线程的目的则是减少程序在并发执行的时候所付出的时空开销,提高操作系统的并发性能

线程最直接的理解就是“轻量级进程”,它是一个基本的CPU执行单元,也是程序执行流的最小单元,由线程ID,程序计数器,寄存器集合和堆栈组成。

线程是进程的一个实体,被系统独立调度和分配的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其他线程共享所拥有的全部资源。

一个线程可以创建和撤销另一个线程,同一个进程中的多个线程可以并发执行。

由于线程之间的相互制约,所以线程在运行过程中也会出现间断性。线程也有运行,就绪,阻塞三种基本状态。

引入线程之后,进程的内涵发生了变化,进程只作为除CPU外的系统资源的分配单元,而线程作为处理机的分配单元。

线程与进程的比较

  1. 调度。简单来说,就是有些需要多进程的事,可以多线程来做了。在传统操作系统中,拥有资源和独立调度的基本单位都是进程,每次调度都要进行上下文切换,开销大。而引入线程后,线城市独立调度的单位,线程切换的代价远低于进程。
  2. 并发性。同一个进程中的多个线程可以并发执行,提高了系统资源的利用率和系统吞吐量。
  3. 拥有资源。进程是系统中拥有资源的基本单位,而线程不拥有系统资源(仅有一点必不可少,能保证独立运行的资源),但线程可访问其隶属的进程的系统资源,这主要表现在属于同一进程的所有县城具有相同的地址空间。
  4. 独立性。每个进程都拥有独立的地址空间和资源。除了共享全局变量,不允许其他进程访问。某进程中的线程对其他进程不可见。同一进程中的不同线程是为了提高并发性以及相互之间的合作而创建的,它们共享进程的地址空间和资源。
  5. 系统开销。创建或撤销进程,系统都要为之分配和回收PCB和其他资源,如内存空间,I/O设备等,系统开销大。进程切换时涉及进程上下文切换,而线程切换时只需要保存和设置少量的寄存器内容,开销很小。
  6. 支持多处理机系统。对于传统单线程进程,不管有多少个处理机,进程只能运行在一个处理机上,而多线程进程,可以将进程中的多个线程运行在多个处理机上。

线程的属性

多线程操作系统中的进程已经不再是一个基本的执行实体,但它仍具有与执行相关的状态。所谓进程处于“执行”状态,实际上是指该进程中的某个线程正在执行。

现成的主要属性如下:

  • 线程是一个轻型实体,它不拥有系统资源,但每个线程都有一个唯一的标识符和一个线程控制块,线程控制块记录了线程执行的寄存器和栈等现场状态。
  • 不同的线程可以执行相同的程序。即同一个服务程序被不同用户调用时,系统可以将它们创建成不同的线程。
  • 同一进程中的各个线程共享该进程所拥有的资源。
  • 线程是处理机的独立调度单位。在单CPU计算机中,线程可交替使用CPU,在多CPU系统中,各线程可占用不同的CPU。
  • 一个线程被创建后,便开始了它的生命周期,直至终止。

线程的状态与转换

  • 执行状态:线程已获得处理机而正在运行。
  • 就绪状态:线程已具备各种执行条件,只需要再获得CPU就可以立即执行。
  • 阻塞状态:线程在执行中因某事件受阻而处于暂停状态。

线程的组织与控制

线程控制块

与进程类似,系统也为每个线程配置一个线程控制块TCB,通常包括:

  • 线程标识符。
  • 一组寄存器。包括程序计数器,状态寄存器和通用寄存器。
  • 线程运行状态。用于描述线程处于何种状态。
  • 优先级。
  • 线程专有存储区。线程切换是用于保存现场。
  • 堆栈指针。

同一进程中的线程完全共享进程的地址空间和全局变量,各个线程都可以访问进程地址空间的每个单元,所以一个线程可以读写甚至清除另一个线程的堆栈。

线程的创建

在系统中有用于创建和撤销线程的函数(或系统调用)

在用户程序启动时,通常仅有一个称为“初始化线程”的线程正在执行,其主要功能是用于创建新线程。

线程的终止

当一个线程完成自己的任务,或在运行过程中出现异常而要被强制终止时,由终止线程调用相应的函数执行终止操作。但是有些线程(主要是系统线程)一旦被建立,就会一直运行而不会被终止。

通常,线程被终止后并不立即释放它占用的资源,只有当进程中的其他线程执行了分离函数,被终止线程才与资源分离,此时资源才可以被其他线程利用。

被终止但尚未释放资源的线程仍可被其他线程调用,以使被终止的线程重新恢复运行。

线程的实现方式

线程的实现可以分为两类:用户级线程(User-Level Thread,ULT)和内核级线程(Kernel-Level Thread,KLT)。

用户级线程其实就是常说的协程,其实它也是线程

用户级线程(协程)

在用户级线程中,有关线程管理的所有工作都由应用程序在用户空间完成,内核意识不到线程存在。

应用程序可以通过使用线程库设计成多线程程序,通常,应用程序从单线程开始,在该线程中开始运行,在其运行的任何时刻,可以通过调用线程库中的派生例程创建一个在相同进程中运行的新线程。

对于设置了用户级线程的系统,其调度仍是以进程为单位进程,各个进程轮流执行一个时间片。假设进程A中包含一个用户级线程,进程B包含100个用户级线程,这样进程A中线程的运行时间将是进程B中各线程运行时间的100倍,因此对线程来说实质上是不公平的。

这种实现方式的优点:

  • 线程切换不需要转到内核空间,节省了模式切换的开销。
  • 调度算法可以是进程专用的,不同进程可以根据自身的需要,对自己的线程选择不同的调度算法。
  • 用户级线程的实现与操作平台无关,对线程管理的代码是属于用户进程的一部分。

这种实现方式的缺点:

  • 系统调用的阻塞问题,当线程执行一个系统调用的时候,不仅该线程被阻塞,而且进程内的所有线程均被阻塞。
  • 不能发挥多处理机的优势,内核每次分配给一个进程的只有一个CPU,因此进程中只有一个线程能执行。

内核级线程

内核级线程同样也是在内核的支持下运行的,线程管理的所有工作也是在内核空间实现的。内核空间也为每个内核级线程设置一个线程控制块,内核根据该控制块感知某线程的存在,并对其加以控制。

这种实现方式的优点:

  • 能发挥多处理机的优势,内核能同时调度一个进程内部的多个线程。
  • 如果进程中的一个线程被阻塞,内核可以调度该进程中的其他线程占用处理机,也可以运行其他进程中的线程。
  • 内核支持线程具有很小数据结构和堆栈,线程切换比较快,开销小。
  • 内核本身也可采用多线程技术。

缺点:同一进程中的线程切换,需要从用户态转到内核态

组合方式

内核支持多个内核级线程的建立,调度和管理,同时允许用户程序建立,调度和管理用户级线程。

一些内核级线程对应多个用户级线程,这是用户级线程通过时分复用内核级线程实现的。

多线程模型

有些系统同时支持用户线程和内核线程,由于二者的连接方式不同,从而形成了下面三种不同的多线程模型。

多对一模型

将多个用户级线程映射到一个内核级线程。这些用户线程一般属于一个进程。线程的调度和管理在用户空间完成。仅当用户线程需要访问内核时,才需要将其映射到一个内核线程上,且每次只允许一个线程进行映射。
优点:线程管理在用户空间进行,效率高
缺点:如果一个线程在访问内核时发生阻塞,整个进程都会被阻塞;在任何时刻,只有一个线程能够访问内核,多个线程不能同时在多个处理机上运行。

一对一模型

将每个用户级线程映射到一个内核级线程。
优点:每一进程被阻塞后,允许调度另一个内核级线程
缺点:每创建一个用户线程,就需要一个内核线程,开销大

多对多模型

将n个用户级线程映射到m个内核级线程上

特点:及克服了多对一模型并发不高的缺点,又克服了一对一模型一个用户进程占用太多内核级线程开销太大的缺点。