第二章 进程管理-2.1 进程与线程

2.1 进程与线程

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

1.1 进程的概念

  1. 程序
  • 是静态的,就是存放在磁盘里的可执行文件,就是一系列的指令集合
  1. 进程
  • 是动态的,是程序的一次执行过程, 是系统进行资源分配和调度的一个独立单位, 同一个程序多次执行会对应多个进程
    • 确切来说, 是进程实体的执行过程
    • 调度: 一个进程被“调度”,就是指操作系统决定让这个进程上CPU运行
    • 当引入线程后, 进程不再是CPU调度的基本单位, 仅仅是资源分配的基本单位

1.2 进程(进程实体)的组成

  • PCB(Process Control Block, 进程控制块)

    • 组成:
      • 当进程被创建时,操作系统会为该进程分配一个唯一的、不重复的“身份证号” -- PID (Process ID,进程ID)
      • 记录PID、进程所属用户UID
        • 基本的进程描述信息,可以让操作系统区分各个进程
      • 记录给进程分配了哪些资源(如:分配了多少内存、正在使用哪些I/O设备、正在使用哪些文件)
        • 可用于实现操作系统对资源的管理
      • 记录进程的运行情况(如:CPU使用时间、磁盘使用情况、网络流量使用情况、进程当前状态[就绪/运行/阻塞...]等)
        • 可用于实现操作系统对进程的控制、调度
      • 记录处理机相关信息(如:PSW、PC等各种寄存器的值)
        • 可用于实现进程切换
    • 这些信息都被保存在一个数据结构PCB中, 操作系统需要对各个并发运行的进程进行管理,操作系统对进程进行管理工作所需的信息都存在PCB中
    • PCB是进程存在的唯一标志,当进程被创建时,操作系统为其创建PCB,当进程结束时,会回收其PCB
  • 程序段

    • 程序代码被编译之后产生的机器指令序列
  • 数据段

    • 进程运行过程中产生的各种数据
  • PCB是给操作系统用的;程序段、数据段是给进程自己用的,与进程自身的运行逻辑有关

  • 一个进程实体(进程映像)由PCB、程序段、数据段组成。 进程是动态的,进程实体(进程映像)是静态的,进程实体反应了进程在某一时刻的状态

1.3 进程的特征

  • 动态性: 进程是程序的一次执行过程, 是动态地产生、变化和消亡的

    • 动态性是进程最基本的特征
  • 并发性: 内存中有多个进程实体, 各进程可并发执行

  • 独立性: 进程是能独立运行、独立获得资源、独立接受调度的基本单位

  • 异步性: 各进程按各自独立的、不可预知的速度向前推推进, 操作系统要提供"进程同步机制"来解决异步问题

    • 异步性会导致并发程序执行结果的不确定性
  • 结构性: 每个进程都会配置一个PCB, 结构上看, 进程由PCB、程序段、数据段组成

2. 进程的状态与转换、进程的组织

2.1 进程的状态

  • 创建态(New, 又称: 新建态)

    • 进程正在被创建时, 它的状态是"创建态", 在这个阶段操作系统会为进程分配资源、初始化PCB
  • 就绪态(Ready)

    • 当进程创建完成后, 便进入"就绪态", 处于就绪态的进程已经具备运行条件, 但由于没有空闲CPU,就暂时不能运行
  • 运行态(Running)

    • 当CPU空闲时, 操作系统就会选择一个就绪进程, 让它上处理机运行
    • 如果一个进程此时在CPU上运行, 那么这个进程处于"运行态", CPU会执行该进程对应的程序(指令序列)
    • 单CPU情况下, 同一时刻只会有一个进程处于运行态, 多核CPU情况下, 可能有多个进程处于运行态
  • 阻塞态(Blocked/Waiting, 又称: 等待态)

    • 在进程运行的过程中, 可能会请求等待某个事件的发生(如等待某种系统资源的分配, 或者等待其他进程的响应)
    • 在这个事件发生之前, 进程无法继续往下执行, 此时操作系统会让这个进程下CPU, 并让它进入"阻塞态"
    • 当CPU空闲时, 又会选择另一个"就绪态"进程上CPU运行
  • 终止态(Terminated, 又称: 结束态)

    • 一个进程可以执行exit系统调用, 请求操作系统终止该进程, 此时该进程会进入"终止态", 操作系统会让这个进程下CPU, 并回收内存空间等资源, 最后还要回收该进程的PCB

    • 进程的整个生命周期中, 大部分时间都处于运行态、就绪态、阻塞态

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

2.2 进程状态的转化

注意:不能由阻塞态直接转换为运行态,也不能由就绪态直接转换为阻塞态(因为进入阻塞态是进程主动请求的,必然需要 进程在运行时才能发出这种请求)

2.3 进程的组织

  • 为了对同一个状态下的各个进程进行统一的管理, 操作系统会将各个进程的PCB组织起来

  • 链接方式

    • 操作系统根据进程状态的不同, 将相同状态的PCB放入同一个队列之中, 很多操作系统还会根据阻塞原因的不同, 再分为多个阻塞队列, 优先级较高的就绪进程会被放入就绪队列的队头
      • 执行指针 → PCB
      • 就绪队列指针 → PCB → PCB
      • 阻塞队列指针 → PCB → PCB
  • 索引方式

    • 操作系统根据进程状态的不同, 创建几张索引表, 操作系统持有指向各个索引表的指针, 通过索引表找到对应的PCB

3. 进程控制

3.1 基本概念

  • 进程控制
    • 进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能
      • 简化理解:反正进程控制就是要实现进程状态转换
  • 如何实现进程控制?
    • 用"原语"实现
      • 原语是一种特殊的程序, 它的执行具有原子性。 也就是说,这段程序的运行必须一气呵成,不可中断
      • 如果不能“一气呵成”,就有可能导致操作系统中的某些关键数据结构信息不统一的情况, 这会影响操作系统进行别的管理工作
  • 如何实现原语的原子性?
    • 可以用 “关中断指令”和“开中断指令”这两个特权指令实现原子性
      • 正常情况下,CPU每执行完一条指令都会例行检查是否有中断信号需要处理,如果有, 则暂停运行当前这段程序,转而执行相应的中断处理程序。CPU执行了关中断指令之后,就不再例行检查中断信号,直到执行开中断指令之后才会恢复检查。 这样,关中断、开中断之间的这些指令序列就是不可被中断的,这就实现了“原子性“

3.2 进程控制相关的原语

  • 无论哪个进程控制原语,要做的无非三类事情:
    • 更新PCB中的信息
    • 将PCB插入合适的队列
    • 分配/回收资源
  • 创建原语
创建原语
  • 撤销原语

  • 阻塞原语 & 唤醒原语

  • 切换原语

  • 指令顺序执行的过程中,很多中间结果是放在各种寄存器中的,CPU中会设置很多 “寄存器(PSW、PC、IR、通用寄存器)”,用来存放程序运行过程中所需的某些数据,在进程切换时先在PCB中保存这个进程的运行环境 (保存一些必要的寄存器信息),当原来的进程再次投入运行时,可以通过PCB恢复它的运行环境

4. 进程通信

4.1 什么是进程通信?

  • 进程间通信(Inter-Process Communication, IPC)是指两个进程之间产生数据交互
    • 如b站分享视频到qq群中, 就需要不同进程之间的通信

4.2 为什么进程通信需要操作系统支持?

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

  • 为了保证安全,一个进程不能直接访问另一个进程的地址空间,但是进程之间的信息交换又是必须实现的,为了保证进程间的安全通信,操作系统提供了一些方法:

    • 共享存储
    • 消息传递
    • 管道通信

4.3 共享存储

  • 设置一个共享区, 将共享内存区映射到进程自己的地址空间中

    • 通过"增加页表项/段表项"即可将同一片共享内存区映射到各个进程的地址空间中
  • 为避免出错, 各个进程对共享空间的访问应该是互斥的, 操作系统只负责提供共享空间和同步互斥工具(如P、V操作

  • 两种方式:

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

4.4 消息传递

  • 进程间的数据交换以格式化的消息(Message)为单位。进程通过操作系统提供的“发送消息/接收消息”两个原语进行数据交换
    • 格式化的消息: 包含消息头消息体
      • 消息头:包括发送进程ID、接受进程ID、消息类型、消息长度等格式化的信息(计算机网络中发送的“报文”其实就是一种格式化的消息)
      • 消息体:进程间具体传递的信息
  • 两种方式:
    • 直接通信方式
      • 消息发送进程要指明接收进程的id, 消息直接挂到接收进程的消息缓冲队列上
    • 间接通信方式
      • 通过"信箱"间接地通信, 消息要先发送到中间实体(信箱)中, 因此又被称为"信箱通信方式"

4.5 管道通信

  • 管道”是指用于连接读写进程的一个共享文件,又名pipe文件,其实就是在内存中开辟一个大小固定的缓冲区

  • 数据在管道中以数据流的方式进行传输,遵循先进先出的原则

  • 管道通信

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

5. 线程的概念

5.1 什么是线程, 为什么要引入线程?

  • 进程是程序的一次执行过程, 传统的进程是程序执行流的最小单位, 有的进程可能需要"同时"做很多事, 而传统的进程只能串行地执行一系列程序。为此, 引入了"线程", 来增加并发度, 引入线程后, 线程成为了程序执行流的最小单位
    • 线程是一个基本的CPU执行单元, 引入线程之后, 不仅是进程之间可以并发, 进程内的各线程之间也可以并发, 从而进一步提升了系统的并发度, 使得一个进程内也可以并发处理各种任务
    • 引入线程后, 进程只作为除CPU之外的系统资源的分配单位

5.2 引入线程机制后, 有什么变化?

5.3 线程的属性

6. 线程的实现方式和多线程模型

6.1 线程的实现方式

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

    • 历史背景:早期的操作系统(如: 早期Unix)只支持进程,不支持线程。当时的“线程”是由线程库实现的,很多编程语言提供了强大的线程库,可以实现线程的创建、销毁、调度等功能
    • 用户级线程是“代码逻辑”的载体

    • 用户级线程的管理工作由谁来完成?
      • 用户级线程由应用程序通过线程库实现,所有的线程管理工作都由应用程序负责(包括线程切换),并不是由操作系统完成
    • 用户级线程切换是否需要CPU变态?
      • 用户级线程中,线程切换可以在用户态下即可完成,无需操作系统干预
    • 操作系统是否能意识到用户级线程的存在?
      • 在用户看来,是有多个线程。但是在操作系统内核看来,并意识不到线程的存在,操作系统只能看到包含着多个用户级线程的程序执行的进程
      • “用户级线程”就是“从用户视角看能看到的线程”
    • 优缺点
      • 优点: 用户级线程的切换在用户态即可完成,不需要切换到核心态,线程管理的系统开销小,效率高
      • 缺点: 当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高,多个线程不可在多核处理机上并行运行
  • 内核级线程(Kernel-Level Thread, KLT, 又称“内核支持的线程”)

    • 由操作系统支持的线程, 大多数现代操作系统都实现了内核级线程, 如Windows、Linux
    • 操作系统只“看得见”内核级线程,因此只有内核级线程才是处理机分配的单位
    • 内核级线程是“运行机会”的载体

    • 内核级线程的管理工作由谁来完成?
      • 内核级线程的管理工作由操作系统内核完成
    • 内核级线程切换是否需要CPU变态?
      • 线程调度、切换等工作都由内核负责,因此内核级线程的切换必然需要在核心态下才能完成
    • 操作系统是否能意识到内核级线程的存在?
      • 操作系统会为每个内核级线程建立相应的TCB(Thread Control Block,线程控制块),通过TCB对线程进行管理。“内核级线程”就是“从操作系统内核视角看能看到的线程”
    • 优缺点
      • 优点: 当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行
      • 缺点: 一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大

6.2 多线程模型

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

  • 一对一模型

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

    • 优点: 当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
    • 缺点: 一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大
  • 多对一模型

    • 多个用户级线程映射到一个内核级线程,且一个进程只被分配一个内核级线程
    • 退化为纯粹的用户级线程管理方式

    • 优点: 用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高
    • 缺点: 当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高,多个线程不可在多核处理机上并行运行
  • 多对多模型

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

    • 克服了多对一模型并发度不高的缺点(一个阻塞全体阻塞),又克服了一对一模型中一个用户进程占用太多内核级线程,开销太大的缺点, 内核级线程中可以运行任意一个有映射关系的用户级线程代码, 只有m个内核级线程中正在运行的代码逻辑都阻塞时, 这个进程才会阻塞

7. 线程的状态与转换

7.1 线程的状态与转换

7.2 线程的组织与控制


第二章 进程管理-2.1 进程与线程
http://binbo-zappy.github.io/2024/11/19/操作系统/2-1-进程与线程/
作者
Binbo
发布于
2024年11月19日
许可协议