进程与线程

143 阅读14分钟

点击阅读:操作系统系列文章


1. 进程的概念、组成及特征

1.1 进程的概念

首先了解一下程序的概念:

程序:是静态的,是存放在磁盘里的可执行文件,是一系列指令的集合。

进程:是动态的,是程序的一次执行过程

注:同一程序多次执行会对应多个进程。

1.2 进程的组成

一个进程实体(进程映像)由 PCB程序段数据段组成。

进程是动态的,进程实体(进程映像)是静态的。进程实体反应了进程在某一时刻的状态,进程是进程实体的运行过程

PCB 是给操作系统用的。程序段、数据段是给进程自己用的。

1.2.1 PCB

PCB(Process Control Block)进程控制块,一个数据结构。操作系统管理进程所需要的信息,都存放在 PCB 中。

PCB 是进程存在的唯一标志,当进程被创建时,操作系统为其创建 PCB,当进程结束时,会回收其 PCB。

PCB 包含:

  1. 进程描述信息
    • 进程标识符 PID:进程被创建时,OS 会为该进程分配一个唯一的、不重复的 PID(Process ID)。
    • 用户标识符 UID
  2. 进程控制和管理信息
    • CPU、磁盘、网络流量等使用情况的统计
    • 进程当前状态:就绪态/阻塞态/运行态
  3. 资源分配清单
    • 正在使用哪些文件
    • 正在使用哪些内存区域
    • 正在使用哪些I/O设备
  4. 处理机相关信息
    • 如 PSW、PC 等等各种寄存器的值(用于实现进程切换)

1.2.2 程序段

程序的代码,指令序列

1.2.3 数据段

运行过程中产生的各种数据,如程序中定义的变量

1.3 进程的特征

  1. 动态性最基本特性。进程是程序的一次执行过程,是动态地产生、变化和消亡的。
  2. 并发性:内存中有多个进程实体,各进程可并发执行。
  3. 独立性:进程是能独立运行、独立获得资源的基本单位。
  4. 异步性:各进程按各自独立的、不可预知的速度向前推进,操作系统要提供进程同步机制来解决异步问题。
  5. 结构性:进程由程序段、数据段、PCB 组成。

2. 进程的状态与转换

2.1 进程的状态

  1. 创建状态:进程正在被创建时。在这个阶段操作系统会为进程分配资源初始化 PCB
  2. 就绪状态:当进程创建完成后,便进入就绪态,处于就绪态的进程已经具备运行条件,但由于没有空闲 CPU,此时不能运行。
  3. 运行状态:进程正在 CPU 上运行。
  4. 阻塞状态:在进程运行的过程中,可能会请求等待某个事件的发生(如等待某种系统资源的分配,或者等待其他进程的响应),进程会进入阻塞状态。
  5. 终止状态:一个进程可以执行 exit 系统调用,请求操作系统终止该进程。此时该进程会进入“终止态”,操作系统会让该进程下CPU,并回收内存空间等资源,最后还要回收该进程的PCB。

进程PCB中,会有一个变量 state 来表示进程的当前状态。

2.2 进程状态间的转换

image.png

  1. 创建态 -> 就绪态:系统完成创建进程的相关工作。
  2. 就绪态 -> 运行态:进程被调度。
  3. 运行态 -> 就绪态:时间片到,或者 CPU 被其他高优先级的进程抢占。
  4. 运行态 -> 阻塞态:等待系统资源分配,或等待某事件发生(主动行为)。
  5. 阻塞态 -> 就绪态:资源分配到位,等待的时间发生(被动行为)。
  6. 运行态 -> 终止态:进程运行结束,或运行过程中遇到不可修复的错误。

2.3 进程的组织方式

  1. 链接方式:按照进程状态将 PCB 分为多个队列,操作系统持有指向各个队列的指针。
  2. 索引方式:根据进程状态的不同,建立几张索引表,操作系统持有指向各个索引表的指针。

3. 进程控制

3.1 进程控制是什么

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

如何实现进程控制呢?使用原语实现,因为原语的执行具有原子性,一气呵成。

原语的原子性是如何实现的?原语是使用关中断指令开中断指令这两个特权指令实现的。

正常情况:CPU每执行完一条指令都会例行检查是否有中断信号需要处理,如果有,则暂停运行当前这段程序,转而执行相应的中断处理程序。

CPU执行了关中断指令之后,就不再例行检查中断信号,直到执行开中断指令之后才会恢复检查。

3.2 进程控制相关的原语

3.2.1 进程的创建

创建原语

  1. 申请空白PCB
  2. 为新进程分配所需资源
  3. 初始化PCB
  4. 将PCB插入就绪队列

引起进程创建的事件

  1. 用户登录:分时系统中,用户登录成功,系统会建立为其建立一个新的进程
  2. 作业调度:多道批处理系统中,有新的作业放入内存时,会为其建立一个新的进程
  3. 提供服务:用户向操作系统提出某些请求时,会新建一个进程处理该请求
  4. 应用请求:由用户进程主动请求创建一个子进程

3.2.2 进程的终止

撤销原语

  1. 从PCB集合中找到终止进程的PCB
  2. 若进程正在运行,立即剥夺CPU,将CPU分配给其他进程
  3. 终止其所有子进程(进程间的关系是树形结构)
  4. 将该进程拥有的所有资源归还给父进程或操作系统
  5. 删除PCB

引起进程终止的事件

  1. 正常结束:进程自己请求终止(exit系统调用)
  2. 异常结束:整数除以0、非法使用特权指令,然后被操作系统强行杀掉
  3. 外界干预:Ctrl+Alt+delete,用户选择杀掉进程

3.2.3 进程的阻塞

阻塞原语

  1. 找到要阻塞的进程对应的PCB
  2. 保护进程运行现场,将PCB状态信息设置为“阻塞态”,暂时停止进程运行
  3. 将PCB插入相应事件的等待队列

引起进程阻塞的事件

  1. 需要等待系统分配某种资源
  2. 需要等待相互合作的其他进程完成工作

3.2.4 进程的唤醒

唤醒原语

  1. 在事件等待队列中找到PCB
  2. 将PCB从等待队列移除,设置进程为就绪态
  3. 将PCB插入就绪队列,等待被调度

引起进程唤醒的事件

  1. 等待的事件发生(因何事阻塞,就应由何事唤醒)

3.2.5 进程的切换

切换原语

  1. 将运行环境信息存入PCB
  2. PCB移入相应队列
  3. 选择另一个进程执行,并更新其PCB
  4. 根据PCB恢复新进程所需的运行环境

引起进程切换的事件

  1. 当前进程时间片到
  2. 有更高优先级的进程到达
  3. 当前进程主动阻塞
  4. 当前进程终止

4. 进程通信

进程间通信(Inter-Process Communication, IPC) 是指两个进程之间产生数据交互。

进程是分配系统资源的单位(包括内存地址空间),因此各进程拥有的内存地址空间相互独立

为了保证安全,一个进程不能直接访问另一个进程的地址空间。进程通信需要操作系统的支持。

4.1 共享存储

设置一个共享内存区域,并映射到进程的虚拟地址空间。

互斥地访问共享空间(由通信进程自己负责实现互斥)。各个进程可使用操作系统内核提供的同步互斥工具(如P、V操作)。

共享存储的两种方式:

  1. 基于存储区的共享:操作系统在内存中划出一块共享存储区,数据的形式、存放位置都由通信进程控制,而不是操作系统。这种共享方式速度很快,是一种高级通信方式
  2. 基于数据结构的共享:比如共享空间里只能放一个长度为10的数组。这种共享方式速度慢、限制多,是一种低级通信方式

4.2 消息传递

进程间的数据交换以格式化的消息(Message)为单位。进程通过操作系统提供的“发送消息/接收消息”两个原语进行数据交换。

消息包含消息头消息体。消息头包括:发送进程ID接收进程ID消息长度等格式化的信息。

消息传递的两种方式

  1. 直接通信方式:消息发送进程要指明接收进程的ID。消息直接挂到接收进程的消息队列里。
  2. 间接通信方式:通过信箱间接地通信。因此又称信箱通信方式。消息先发到中间体(信箱),可以多个进程往同一个信箱send消息,也可以多个进程从同一个信箱中receive消息。

4.3 管道通信

管道是一个特殊的共享文件,又名 pipe文件。其实就是在内存中开辟一个大小固定的内存缓冲区

  1. 管道只能采用半双工通信,某一时间段内只能实现单向的传输。如果要实现双向同时通信,则需要设置两个管道。
  2. 各进程要互斥地访问管道(由操作系统实现)。
  3. 当管道写满时,写进程将阻塞,直到读进程将管道中的数据取走,即可唤醒写进程。
  4. 当管道读空时,读进程将阻塞,直到写进程往管道中写入数据,即可唤醒读进程。
  5. 管道中的数据一旦被读出,就彻底消失。因此,当多个进程读同一个管道时,可能会错乱。对此,通常有两种解决方案:第一种:一个管道允许多个写进程,一个读进程;第二种:允许有多个写进程,多个读进程,但系统会让各个读进程轮流从管道中读数据。
  6. 写进程往管道写数据,即便管道没被写满,只要管道没空,读进程就可以从管道读数据。
  7. 读进程从管道读数据,即便管道没被读空,只要管道没满,写进程就可以往管道写数据。

5. 线程概念

进程是程序的一次执行。有的进程可能需要同时做很多事,而传统的进程只能串行地执行一系列程序。为此,引入了线程,来增加并发度

引入线程后,线程成为了程序执行流的最小单位

对计算机来说每一个任务就是一个进程,在每一个进程内部至少要有一个线程是在运行中的,可以把线程理解为轻量级进程线程是一个基本的CPU执行单元,也是程序执行流的最小单位。

引入线程之后,不仅是进程之间可以并发,进程内的各线程之间也可以并发,从而进一步提升了系统的并发度,使得一个进程内也可以并发处理各种任务(如QQ视频、文字聊天、传文件)。如果是同一进程内的线程切换,则不需要切换进程环境,系统开销小

引入线程后,进程只作为除CPU之外的系统资源的分配单元(如打印机、内存地址空间等都是分配给进程的)。线程则作为处理机的分配单元。进程是资源分配的基本单位,线程是调度的基本单位

  1. 每个线程都有一个线程ID、线程控制块(TCB)。
  2. 线程也有就绪、阻塞、运行三种基本状态。
  3. 线程几乎不拥有系统资源。同一进程的不同线程间共享进程的资源。由于共享内存地址空间,同一进程中的线程间通信甚至无需系统干预
  4. 同一进程中的线程切换,不会引起进程的切换。不同进程中的线程切换,会引起进程切换。

6. 线程的实现方式

6.1 用户级线程(User-Level Thread, ULT)

早期的操作系统(如:早期Unix)只支持进程,不支持线程。当时的“线程”是由线程库实现的。

image.png

public void test(){
    int i = 0;
    while(true){
        if (i == 0){//处理视频聊天的代码}
        if (i == 1){//处理文字聊天的代码}
        if (i == 2){//处理文件传输的代码}
        i = (i + 1) % 3;
    }
}
  1. 用户级线程由应用程序通过线程库实现,所有的线程管理工作都由应用程序负责(包括线程切换)。
  2. 用户级线程中,线程切换可以在用户态下即可完成,无需操作系统干预
  3. 在用户看来,是有多个线程。但是在操作系统内核看来,并意识不到线程的存在。用户级线程就是从用户视角看能看到的线程。

用户级线程的优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高。

用户级线程的缺点:当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高多个线程不可在多核处理机上并行运行

6.2 内核级线程(Kernel-Level Thread, KLT)

内核级线程是由操作系统支持的线程。

image.png

  1. 内核级线程的管理工作由操作系统内核完成。
  2. 线程调度、切换等工作都由内核负责,因此内核级线程的切换必然需要在核心态下才能完成。
  3. 操作系统会为每个内核级线程建立相应的 TCB(Thread Control Block,线程控制块),通过 TCB 对线程进行管理。内核级线程就是从操作系统内核视角看能看到的线程。

内核级线程的优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。

内核级线程的缺点:一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。

7. 多线程模型

在支持内核级线程的系统中,根据用户级线程和内核级线程的映射关系,可以划分为几种多线程模型。

7.1 一对一模型

一个用户级线程映射到一个内核级线程。每个用户进程有与用户级线程同数量的内核级线程。

image.png

优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。

缺点:一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。

7.2 多对一模型

多个用户级线程映射到一个内核级线程。且一个进程只被分配一个内核级线程。

image.png

优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高

缺点:当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可在多核处理机上并行运行

7.3 多对多模型

n 用户及线程映射到 m 个内核级线程(n >= m)。每个用户进程对应 m 个内核级线程。

克服了多对一模型并发度不高的缺点(一个阻塞全体阻塞),又克服了一对一模型中一个用户进程占用太多内核级线程,开销太大的缺点。

image.png

8. 线程的状态与转换

与进程相似,主要为以下三个状态: image.png

9. 线程的组织与控制

image.png


点击阅读:操作系统系列文章