【408笔记】操作系统 第二章 进程和线程

文章目录

  • 2.1 进程与线程
    • 2.1.1 进程的概念和特征
      • 1. 进程的概念
      • 2. 进程的特征
    • 2.1.2 进程的状态与转换
    • 2.1.3 进程的组织
      • 1. 进程控制块(PCB
      • 2. 程序段
      • 3. 数据段
    • 2.1.4 进程控制
      • 1. 进程创建
      • 2. 进程的终止(就绪/阻塞/运行态 -> 终止态 -> 无
      • 3. 进程的阻塞(运行态 -> 阻塞态
      • 4. 进程的唤醒(阻塞态 -> 就绪态
      • 5. 进程的切换(一个进程 运行态->就绪态;另一个进程 就绪态->运行态
    • 2.1.5 进程的通信
      • 1. 共享存储
      • 2. 消息传递
      • 3. 管道通信
    • 2.1.6 线程和多线程模型
      • 1. 线程的基本概念
      • 2. 线程和进程的比较
      • 3. 线程的属性
      • 4. 线程的状态与转换
      • 5. 线程的组织与控制
        • 线程控制块
        • 线程创建
        • 线程终止
      • 6. 线程的实现方式
        • 用户级线程 ULT
        • 内核级线程 KLT
      • 7. 多线程模型
  • 2.2 处理机调度
    • 2.2.1 调度的概念
      • 1. 调度的基本概念
      • 2. 调度的层次
      • 3. 三级调度的联系
    • 2.2.2 调度的目标
    • 2.2.3 调度的实现
      • 1. 调度程序(调度器
      • 2. 进程调度的时机、切换与过程
      • 3. 进程调度的方式
      • 4. 闲逛进程
      • 5. 两种线程的调度
    • 2.2.4 调度算法
      • 1. 先来先服务(FCFS, First Come First Serve
      • 2. 短作业优先(SJF, Shortest Job First
      • 3. 优先级调度算法
      • 4. 高响应比优先(HRRN, Highest Response Ratio Next
      • 5. 时间片轮转调度算法(RR, Round-Robin
      • 6. 多级队列调度算法
      • 7. 多级反馈队列调度算法
      • 8. 比较
    • 2.2.5 进程切换
  • 2.3 同步、互斥
    • 2.3.1 同步和互斥的基本概念
      • 1. 临界资源
      • 2. 同步
      • 3. 互斥
      • 4. 可重入函数
    • 2.3.2 实现临界区互斥的基本方法
      • 1. 软件实现方法
        • 算法1:单标志法
        • 算法2:双标志先检查法
        • 算法3:双标志后检查法
        • 算法4:Peterson算法
      • 2.硬件实现方法
        • 1. 中断屏蔽方法
        • 2. 硬件指令方法
    • 2.3.3 互斥锁(mutex lock
    • 2.3.4 信号量机制
      • 1. 整型信号量
      • 2. 记录型信号量
      • 3. 用信号量机制实现进程同步
      • 4. 利用信号量实现进程互斥
      • 5. 利用信号量实现前驱关系
    • 2.3.5 管程
      • 1. 管程的定义和基本特征
    • 2.3.6 经典同步问题
      • 1. 生产者——消费者问题
        • 复杂化:多生产者——多消费者问题
      • 2. 读者——写者问题
      • 3. 哲学家进餐问题
      • 4. 吸烟者问题
  • 2.4 死锁
    • 2.4.1 死锁的概念
      • 1. 死锁的定义
      • 2. 死锁产生的原因
        • 系统资源的竞争
        • 进程推进顺序非法
        • 死锁产生的必要条件
      • 3. 死锁的处理策略
    • 2.4.2 死锁预防
      • 1. 破坏互斥条件
      • 2. 破坏不剥夺条件
      • 3. 破坏请求和保持条件
      • 4. 破坏循环等待条件
    • 2.4.3 避免死锁
      • 1. 系统安全状态
      • 2. 银行家算法
    • 2.4.4 死锁的检测和解除
      • 死锁的检测(死锁定理
      • 解除死锁
  • 408真题

2.1 进程与线程

2.1.1 进程的概念和特征

1. 进程的概念

  1. 程序:静态,是存放在磁盘里的可执行文件,就是一系列的指令集合
  2. 进程:动态,是程序的一次执行过程。(同一程序多次执行会对应多个进程

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


进程的组成

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

2. 进程的特征

  1. 动态性:最基本的特征,进程是程序的一次执行过程,动态产生、变化、消亡,具有一定的生命周期
  2. 并发性:内存中有多个进程实体,可以并发执行
  3. 独立性:进程是可以独立运行,独立获得资源,独立接受调度的基本单位
  4. 异步性:各进程按照各自独立的不可预知的速度向前推进,操作系统需要提供“进程同步机制”来解决异步问题
  5. 结构性:每个进程都会配置一个PCB,结构上看进程由程序段、数据段和PCB组成

2.1.2 进程的状态与转换

  1. 创建态:进程刚被创建时处于“创建态”,操作系统为进程分配资源、初始化PCB
  2. 就绪态进程创建完成后进入“就绪态”,此时进程已经具备运行条件,但由于没有空闲CPU,暂时不能运行
  3. 运行态正在CPU上运行的进程
  4. 阻塞态(等待态)进程运行的过程中可能请求某个事件发生(例如请求系统资源分配或者其他进程响应,在这个事件发生之前,进程无法继续执行,此时操作系统会让这个进程下CPU,进入“阻塞态”
  5. 终止态:操作系统让进程下CPU,回收内存空间等资源,回收PCB

⚠️区分就绪态和阻塞态

  1. 就绪态是指进程只缺少处理器,只要获得处理机资源就立即运行
  2. 等待态:进程需要其他资源(处理机以外),或者需要等待某一事件才能继续

进程状态转换

  1. 就绪态 -> 运行态:处于就绪态的进程被调度后,获得处理机资源(分派处理机时间片),进程从就绪态变为运行态
  2. 运行态 -> 就绪态:处于运行态的进程在时间片用完后,不得不让出处理机,进程由运行态转换为就绪态。在可剥夺的操作系统中,优先级更高的进程就绪时,调度程序将正在执行的进程转换为就绪态,让更高优先级的进程执行
  3. 运行态 -> 阻塞态:进程请求某一资源的使用(例如外设)或等待某一事件(例如IO操作)时,就从运行态转换为阻塞态。进程以系统调用方式请求操作系统提供服务,是一种特殊的、由运行用户态程序调用操作系统内核过程的形式(主动行为
  4. 阻塞态 -> 就绪态:进程等待的事件到来时,进程由阻塞态转换为就绪态(被动行为

【408笔记】操作系统 第二章 进程和线程_第1张图片

2.1.3 进程的组织

进程是一个独立的运行单位,是操作系统进行资源分配和调度的基本单位,由进程控制块PCB(核心部分)、程序段和数据段组成

1. 进程控制块(PCB

  1. 进程创建时,操作系统为其新建一个PCB,该结构之后常驻内存,任何时候都可以存取,并在进程结束时删除。PCB是进程实体的一部分,是进程存在的唯一标志
  2. 进程执行时,系统通过PCB了解该进程的现行状态信息,以便操作系统对其进行控制和管理
  3. 进程结束时,系统回收其PCB,进程随之消亡

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

【408笔记】操作系统 第二章 进程和线程_第2张图片


多个PCB组织方式

  1. 链接方式:将同一状态的PCB连接成一个队列
  2. 索引方式:将同一状态的进程组织在一个索引表中

2. 程序段

程序的代码(指令序列,可被调度到CPU执行

⚠️程序段可被多个进程共享

3. 数据段

存储原始数据、运行过程中产生的各种数据

  1. 下列C语言程序中的内容和相关数据结构位于:
    全局赋值变量-正文段;未赋值局部变量-栈段;函数调用实参传递值-栈段;用malloc要求动态分配的存储区-堆段;常量值-正文段;进程优先级-PCB

2.1.4 进程控制

  1. 进程控制的主要功能是对系统中所有进程实施有效管理,包括创建新进程、撤销已有进程、实现进程状态转换等功能
  2. os中把进程控制用的程序段称为原语,其特点是:执行期间不允许中断,是一个不可分割的基本单位

原语

  1. 原语的执行具有原子性,执行期间不允许被中断
  2. 可以用“关中断指令”(不再检查中断信号)和“开中断指令”两个特权指令实现原子性
  3. 原语运行在核心态

1. 进程创建

  1. 允许一个进程创建另一个进程(父进程创建子进程)
  2. 子进程可以继承父进程所拥有的资源
  3. 子进程被撤销时,应当将从父进程获得的资源归还
  4. 撤销父进程时,同时也会撤销其所有的子进程

操作系统创建新进程:

  1. 为新进程分配一个唯一的进程标识号(PID),并申请一个空白PCB(PCB是有限的);若PCB申请失败,则创建进程失败
  2. 为进程分配其运行所需的资源,例如内存、文件、IO设备和CPU时间等(在PCB中实现)。这些资源从操作系统、或者父进程获得。如果资源不足,进程处于创建态,等待内存资源,而不是创建失败
  3. 初始化PCB,包括初始化标志信息、初始处理机状态信息和初始化处理机控制信息,以及设置进程优先级
  4. 若进程就绪队列能接纳新进程,则新进程插入就绪队列,等待被调度

引起进程创建的事件

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

2. 进程的终止(就绪/阻塞/运行态 -> 终止态 -> 无

进程终止:(终止原语)

  1. 根据被终止进程标识符,检索进程PCB,读取该进程状态
  2. 若被终止进程处于执行状态,立即终止该进程执行,将处理机资源分配给其他进程
  3. 若该进程还有子孙进程,则全部终止
  4. 将该进程所拥有的全部资源归还给操作系统或父进程
  5. 将PCB删除

引起进程终止的事件

  1. 正常结束
  2. 异常结束
  3. 外界干预

3. 进程的阻塞(运行态 -> 阻塞态

  1. 阻塞原语(Block
    1. 找到要阻塞的进程对应的PCB
    2. 保护进程运行现场,PCB状态信息设置为阻塞态,暂时停止进程运行
    3. PCB插入相应事件的等待队列
  2. 引起进程阻塞的事件
    1. 需要等待系统分配资源
    2. 等待其他相互合作的进程完成工作

4. 进程的唤醒(阻塞态 -> 就绪态

  1. 唤醒原语(Wakeup)
    1. 在事件等待队列中找到PCB
    2. PCB设置为就绪态,移除等待队列
    3. PCB插入就绪队列,等待被调度
  2. 引起进程唤醒的事件
    1. 等待的事件发生:进程因为什么事件被阻塞,就应该由什么事件唤醒

5. 进程的切换(一个进程 运行态->就绪态;另一个进程 就绪态->运行态

  1. 切换原语
    1. 运行环境存入PCB
    2. PCB移入相应队列
    3. 选择另一个进程执行,更新PCB
    4. 根据PCB恢复新进程运行环境
  2. 引起进程切换的事件
    1. 当前进程时间片到
    2. 更高优先级的进程到达
    3. 当前进程主动阻塞
    4. 当前进程终止

2.1.5 进程的通信

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

每个进程拥有各自的内存地址空间。为了保证安全,一个进程不能直接访问另一个进程的地址空间

PV操作是低级通信方式,高级通信方式主要有以下三种:共享存储、消息传递、管道通信

在这里插入图片描述
【408笔记】操作系统 第二章 进程和线程_第3张图片

1. 共享存储

设置一个共享空间,两个进程对共享空间的访问是互斥的(互斥访问通过os提供的工具实现,例如PV操作

  • 基于数据结构的共享:例如共享空间内只放一个长度为10的数组,这种共享方式速度慢,限制多,是低级通信方式
  • 基于存储区的共享:在内存中划出一块共享存储区,由进程控制(而不是os),这种方式速度快,是高级通信方式

2. 消息传递

数据以格式化的消息为单位,进程通过os提供的原语进行数据交换

  • 直接通信方式:发送进程直接把消息发送给接受进程,挂在接受进程消息缓冲队列上,接受进程从消息缓冲队列中取得消息
  • 间接通信方式:发送进程将消息发送到某个中间实体(称为信箱),接受进程从信箱取得消息
  1. 用信箱实现进程间相互通信需要有两个通信原语:发送原语和接收原语

3. 管道通信

管道是指用于连接读写进程的一个共享文件(pine文件),实质是在内存中开辟一个固定大小的缓冲区

  1. 管道只能半双工通信
  2. 各个进程要互斥访问管道
  3. 管道写满时,写进程的系统调用被阻塞;管道空时,读进程的系统调用被阻塞
  4. 管道没写满,不允许读;管道没读空,不允许写
  5. 数据一旦被读出,就离开管道,这意味着读进程最多只能有一个

2.1.6 线程和多线程模型

1. 线程的基本概念

引入线程目的:减少程序在并发执行时所付出的时空开销,提高操作系统并发性能

  1. 线程最直接的理解:轻量级进程
  2. 线程是进程中的一个实体,是被系统独立调度和分配的基本单位。一个进程可以有多个线程
  3. 线程只拥有一丁点运行必不可少的资源,不拥有系统资源
  4. 线程可以与同属一个进程的其他线程共享进程所拥有的所有资源
  5. 一个线程可以创建和撤销另一个线程
  6. 同一个进程中的多个线程之间可以并发执行
  7. 线程在运行中呈现出间断性,线程也有就绪、阻塞和运行三种基本状态

⚠️:引入线程后,进程只作为除了CPU之外的系统资源的分配单位,而线程作为处理机的分配单元

由于一个进程内有多个线程,若线程的切换发生在同一个进程内部,则只需要很小的时空开销

2. 线程和进程的比较

传统操作系统 引入线程的操作系统
调度 进程是拥有资源,独立调度的基本单位。每次调度需要上下文切换,开销大 线程是独立调度的基本单位,线程切换开销远小于进程切换。同一个进程内线程切换不会引起进程切换
并发性 进程可以并发执行 进程可以并发执行,线程也可以并发执行。提高系统资源利用率和系统吞吐量
拥有资源 进程是系统中拥有资源的基本单位 线程不拥有系统资源,只有一点运行必不可少的资源。线程可以访问其隶属进程的所有资源
独立性 每个进程都有独立的地址空间和资源,除了共享全局变量,不允许其他进程访问 某进程中的线程对其他进程不可见。同一进程中的不同线程共享进程的地址空间和资源
系统开销 创建、撤销进程,上下文切换开销大 线程切换开销小,线程之间同步和通信容易实现
支持多处理机系统 进程只能在一个处理机上运行 可以将进程中的多个线程分配到多个处理机上执行

3. 线程的属性

  1. 线程是一个轻型实体,不拥有系统资源,但有一个唯一的标识符和一个线程控制块(TCB),TCB记录了线程执行的寄存器和栈等状态
  2. 不同线程可以执行相同程序,即同一个服务程序被不同用户调用时,操作系统将其创建成不同的线程
  3. 同一进程中的各个线程共享该进程拥有的资源
  4. 线程是处理机的独立调度单位,多个线程可以并发执行。在单CPU的计算机系统中,各线程可交替占用CPU;多CPU系统中,各线程可以同时占用不同的CPU。各个CPU为同一个进程内的线程同时服务,可以缩短进程处理时间
  5. 线程的生命周期中会经历阻塞态、就绪态和运行态等各种状态

4. 线程的状态与转换

  1. 执行状态:线程获得处理机,正在运行
  2. 就绪状态:线程已具备运行条件,只需要获得CPU便可立即执行
  3. 阻塞状态:线程执行中因为某些事件受阻,处于暂停状态

线程状态之间的转换和进程状态转换一致

5. 线程的组织与控制

线程控制块
  1. 与进程类似,系统为每个线程配置一个线程控制块TCB,用于记录和管理线程的信息
  2. TCB通常包括:线程标识符、一组寄存器、线程运行状态、优先级、线程专有存储区、堆栈指针
  3. 同一进程中的所有线程完全共享进程的地址空间和全局变量,各个线程都可以访问进程地址空间的每个单元。一个线程可以读、写、甚至清除另一个线程的堆栈
线程创建

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

创建新线程时,需要用一个线程创建函数,提供相应参数

线程终止
  1. 一个线程完成任务后,或者运行中出现异常被强制终止时,终止线程调用相应函数执行终止操作
  2. 某些线程(主要是系统线程)一旦被创建就不会被终止
  3. 通常线程被终止后不会立即释放占有的资源,只有当其他线程执行分离函数后,被终止线程才和资源分离
  4. 被终止但尚未释放资源的线程仍可被其他线程调用,以便被终止线程重新恢复运行

6. 线程的实现方式

线程的实现分为用户级线程(User-Level Thread, ULT)和内核级线程(Kernel-Level Thread)(内核支持的线程)

【408笔记】操作系统 第二章 进程和线程_第4张图片

用户级线程 ULT
  1. ULT中,有关线程管理的所有工作(创建、撤销、切换等)都由应用程序在用户空间中完成内核意识不到线程的存在
  2. 应用程序可以使用线程库设计成多线程程序。用户级线程管理工作由应用程序通过线程库实现
  3. 使用ULT的系统,调度仍然以进程为单位,内核每次分配给一个进程一个CPU,进程内只有一个线程可以执行,不能发挥多处理机的优势。线程被阻塞时,同一进程内的所有线程都被阻塞
  4. 用户级线程切换不需要CPU从用户态转变为内核态。线程切换不需要切换CPU状态,开销减少
内核级线程 KLT
  1. KLT管理工作由操作系统内核完成
  2. KLT的切换需要在内核态才能完成
  3. 操作系统为每个KLT建立相应的TCB,通过TCB对KLT进行管理,因此内核可以看到线程
  4. 优缺点:
    1. 一个线程被阻塞,其他的还可以继续执行,并发能力强
    2. 可以在多核处理机上运行
    3. 线程管理成本高,开销大

7. 多线程模型

某些系统同时支持ULT和KLT,由链接方式的不同可以分为多对一、一对一和多对多模型

【408笔记】操作系统 第二章 进程和线程_第5张图片
【408笔记】操作系统 第二章 进程和线程_第6张图片

【408笔记】操作系统 第二章 进程和线程_第7张图片

1 . 系统动态DLL库中的系统线程,被不同进程调用,属于相同的线程

2.2 处理机调度

2.2.1 调度的概念

1. 调度的基本概念

处理机调度:对处理机进行分配,从就绪队列中按照一定算法,选择一个进程将处理机分配给它运行,以实现进程并发执行

2. 调度的层次

调度有三个层次:高级调度、中级调度、低级调度

【408笔记】操作系统 第二章 进程和线程_第8张图片

  1. 高级调度:按照一定的原则从外存的作业后备队列中挑选一个作业调入内存,并创建进程。每个作业只调入一次(创建PCB),调出一次(撤销PCB
  2. 中级调度(内存调度):挂起:当内存不足时,可将某些进程的数据调出外存,等内存空闲或进程需要运行时再重新调入内存。暂时调到外存等待的进程状态为挂起状态,PCB会被组织成挂起队列。中级调度:按照某种策略决定将哪个处于挂起状态的进程重新调入内存
  3. 低级调度(进程/处理机调度):按照某种策略,从就绪队列中选取一个进程,将处理机分配给它。进程调度是操作系统最基本的一种调度,进程调度的频率很高
  • 挂起态又可以分为就绪挂起阻塞挂起状态

3. 三级调度的联系

作业调度从外存的后备队列中选择一批作业进入内存,为其建立进程,这些进程被送入就绪队列。进程调度从就绪队列中选择一个进程,将其状态改为运行态,将CPU分配给它。中级调度是为了提高内存利用率,将暂时不能运行的进程挂起

2.2.2 调度的目标

评价不同调度算法的标准

【408笔记】操作系统 第二章 进程和线程_第9张图片

2.2.3 调度的实现

1. 调度程序(调度器

操作系统中,用于调度和分派CPU的组件称为调度程序,主要由以下三部分组成

【408笔记】操作系统 第二章 进程和线程_第10张图片

  1. 排队器:将所有进程按照一定策略拍成一个或多个队列
  2. 分派器:依据调度程序选择的进程,将其从就绪队列中取出,将CPU分配给新进程
  3. 上下文切换器:处理机进行切换时,需要进行上下文切换:当前进程上下文保存到PCB中,装入分派程序的上下文;移出分派程序的上下文,将新选进程的CPU现场装入处理机的各个相应寄存器

2. 进程调度的时机、切换与过程

调度程序是操作系统内核程序,请求调度的事件发生后才可能运行调度程序,调度新的就绪程序后才会引起进程切换

需要进行进程调度与切换的情况

  1. 当前运行的进程主动放弃处理机
    1. 进程正常终止
    2. 进程过程发生异常而终止
    3. 进程主动请求阻塞(例如等待IO
  2. 当前运行的进程被动放弃处理机
    1. 时间片用完
    2. 有更紧急的事件需要处理(例如IO中断
    3. 更高优先级的进程进入就绪队列

不能进行进程调度与切换的情况

  1. 处理中断时
  2. 进程位于操作系统内核程序临界区中
  3. 原子操作过程(原语)中

临界资源:一个时间段只允许一个进程使用的资源,各个进程需要互斥地访问临界资源
临界区:访问临界资源的代码
内核程序临界区:用于访问某种内核数据结构,例如进程的就绪队列

3. 进程调度的方式

  1. 非剥夺调度方式、又称非抢占方式只允许进程主动放弃处理机
  2. 剥夺调度方式,又称抢占方式允许剥夺处理机

4. 闲逛进程

  1. 进程切换时,如果系统中没有就绪进程,则会调度闲逛进程(idle)运行
  2. 如果没有其他进程就绪,闲逛进程就会一直进行
  3. 闲逛进程优先级最低,只要有进程就绪,就会立即让出处理机

5. 两种线程的调度

  1. 用户级线程调度:内核不知道线程的存在;内核选择一个进程,给予时间控制,由进程中的调度程序决定哪个线程运行
  2. 内核级线程调度:内核选择一个特定线程运行,通常无需考虑线程属于哪个进程

上下文切换:切换CPU到另一个进程需要保存当前进程的状态并恢复另一个进程的状态,这个任务称为上下文切换。上下文切换一定发生在内核态

模式切换:用户态和内核态之间的切换。模式切换时可能没有发生进程切换

2.2.4 调度算法

操作系统存在多种调度算法,有些适用于作业调度,有些适用于进程调度,有的两者都适用

调度算法包括:

  • 先来先服务(FCFS)短作业优先(SJF)高响应比优先(HRRN)(交互性差)
  • 时间片轮转调度算法(RR)优先级调度算法多级反馈队列调度算法

饥饿:某个进程/作业长期得不到服务

1. 先来先服务(FCFS, First Come First Serve

FCFS调度算法是一种最简单的调度算法,可用于作业调度和进程调度

【408笔记】操作系统 第二章 进程和线程_第11张图片

  1. 按照作业/进程到达的先后顺序进行服务
  2. 属于不可剥夺算法
  3. 对长作业有利,对短作业不利
  4. 不会导致饥饿

2. 短作业优先(SJF, Shortest Job First

【408笔记】操作系统 第二章 进程和线程_第12张图片

  1. 追求最少的平均等待时间、平均周转时间、平均带权周转时间
  2. 服务时间最短的作业/进程优先得到服务
  3. SJF和SPF是非抢占式的算法,但也有抢占式的版本——最短剩余时间优先算法:SRTN(未说明的话,默认非抢占式
  4. 对短作业有利,对长作业不利;并不一定能做到真正的短作业优先
  5. 未考虑作业的紧迫程度
  6. 可能导致长作业饥饿,甚至饿死

3. 优先级调度算法

  1. 每个作业/进程有各自优先级,调度时选择优先级最高的作业/进程
  2. 可用于作业调度、进程调度(甚至可以用于IO调度
  3. 分为非抢占式和抢占式
  4. 可以灵活的调整优先级,适用于实时操作系统
  5. 如果源源不断地有高优先级进程到来,可能发生饥饿
  • 静态优先级:创建进程时确定,之后一直不变
  • 动态优先级:创建进程时有初始值,之后根据情况动态地调整

通常

  1. 系统进程优先级高于用户进程
  2. 前台进程优先级高于后台进程
  3. 操作系统更偏好I/O型进程(又称I/O繁忙型进程)(与I/O型进程相对的是计算型进程,又称CPU繁忙型进程)
    1. 原因:IO设备和CPU可以并行工作,偏好IO型进程可以尽早让IO设备投入工作,提高资源利用率、系统吞吐量
  4. 如果进程在就绪队列中等待了很长时间,可以适当提升其优先级;如果进程占用处理机运行了很长时间,可以适当降低优先级

4. 高响应比优先(HRRN, Highest Response Ratio Next

主要用于作业调度,是FCFS和SJF调度算法的综合平衡

  1. 在每次调度时计算作业/进程的响应比,选择最高的一个
    1. 响 应 比 R p = 等 待 时 间 + 要 求 服 务 时 间 要 求 服 务 时 间 响应比R_p=\frac{等待时间 + 要求服务时间}{要求服务时间} Rp=+
    2. 作业的等待时间相同时,要求服务时间越短,响应比越高,越有利于段作业,类似SJF
    3. 要求服务时间相同时,等待时间越长,响应比越高,类似FCFS
    4. 对于长作业,响应比随等待时间增加提高,当等待时间足够长时也可以获得处理机,克服了饥饿现象
  2. 非抢占式

5. 时间片轮转调度算法(RR, Round-Robin

适用于分时操作系统

  1. 按照各个进程到达就绪队列的顺序,轮流让各个进程执行一个时间片,如果未在一个时间片内执行完,则剥夺处理机,进程放到就绪队列队尾重新排队
  2. 用于进程调度,只有作业放入内存建立相应进程后,才能被分配处理机时间片
  3. 是抢占式的算法,由时钟装置发出时钟中断来通知CPU时间片到
  4. 如果时间片太大,则RR退化为FCFS算法;如果时间片太小,会导致进程切换太频繁,导致较大开销
  5. 不区分任务的紧急程度
  6. 不会导致饥饿

6. 多级队列调度算法

在系统中设置多个就绪队列,将不同类型或性质的进程固定分配到不同队列,每个队列可以实施不同的调度算法

7. 多级反馈队列调度算法

【408笔记】操作系统 第二章 进程和线程_第13张图片

  1. 设置多个就绪队列,每个队列赋予不同优先级。第一级队列优先级最高,往下依次递减
  2. 赋予每个队列的进程运行时间片大小不相同,优先级越高的队列,每个进程的时间片就越小
  3. 每个队列都采用FCFS算法。新进程进入内存,首先放入第一级队列末尾,进程执行一个时间片后,如果还未完成,则插入第二级队列末尾…
  4. 按队列优先级调度

8. 比较

【408笔记】操作系统 第二章 进程和线程_第14张图片

2.2.5 进程切换

【408笔记】操作系统 第二章 进程和线程_第15张图片

2.3 同步、互斥

2.3.1 同步和互斥的基本概念

1. 临界资源

对临界资源的互斥访问,在逻辑上可以分为四个部分

  1. 进入区:检查是否可以进入临界区,若可进入,则设置“正在访问临界资源”的标志,(相当于上锁)阻止其他进程同时进入临界区
  2. 临界区:访问临界资源的代码段,又称临界段
  3. 退出区:解除“正在访问临界资源”的标志(相当于解锁)
  4. 剩余区:做其他处理

临界区是进程中访问临界资源的代码段,进入区和退出区是负责实现互斥的代码段

  1. 临界区是指并发进程中访问共享变量段的代码程序
  2. 一个系统中有5个并发进程涉及某个相同变量A,变量A的相关临界区是由5个临界区组成的

2. 同步

进程同步:同步也称作直接制约关系,是指为了完成某种任务而建立的两个或多个进程,因为在需要某些位置上协调他们的工作次序而产生的制约关系

  1. 必须对并发进程进行同步的原因:并发进程是异步的

3. 互斥

进程互斥:又称间接制约关系当一个进程进入临界区使用临界资源时,另一个进程必须等待。当占用临界资源的进程退出临界区后,另一进程才可以访问该临界资源

为了实现对临界资源的互斥访问,同时保证整体性能,需要遵循以下原则

  1. 空闲让进:临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区
  2. 忙则等待:当已有进程进入临界区时,其他试图进入临界区的进程必须等待
  3. 有限等待:对请求访问的进程,应保证能在有限时间内进入临界区(保证不会饥饿
  4. 让权等待:进程不能进入临界区时,应立即释放处理机,防止进程忙等待
  1. (21 408) 其中1,2,3必须满足,4可以不满足(例如Peterson算法

4. 可重入函数

可以在执行函数时被os中断,去处理其他任务的函数

  1. 一个进程映像由程序、数据以及PCB组成,其中共享程序段必须用可重入编码编写

2.3.2 实现临界区互斥的基本方法

1. 软件实现方法

在进入区设置并检查一些标志来表明是否有进程在临界区中,若已经有,则在进入区通过循环检查进行等待,进程离开临界区后则在退出区修改标志

算法1:单标志法

思想:设置一个公共变量turnturn==0时允许进程P0访问,turn==1允许进程P1访问…
在这里插入图片描述
问题:必须交替访问,如果turn==0而P0不进入临界区,那么turn将无法被修改,P1也就永远无法进行。违反了“空闲让进”的原则

算法2:双标志先检查法

思想:设置一个数组flag,标记各个进程是否想要进入临界区。每个进程在进入临界区之前,先检查当前是否有其他进程想进入临界区,如果没有,则自身标志设置为true然后开始访问

在这里插入图片描述
在这里插入图片描述

存在的问题:如果进程P1确定将访问临界区,但还没置flag,此时进程切换P2,P2也将可以访问临界区。违反了“忙则等待”的原则

算法3:双标志后检查法

思想:对算法2的改进,先上锁,再进入临界区
【408笔记】操作系统 第二章 进程和线程_第16张图片

问题:虽然解决了“忙则等待”的问题,但可能双方都置flag,违反了“空闲让进”和“有限等待”的原则,可能会出现饥饿现象

算法4:Peterson算法

思想:为了防止两个进程为了进入临界区无限等待,设置变量turn,标识临界区是否有进程在使用。

在这里插入图片描述
问题:没有遵循”让权等待“的原则

2.硬件实现方法

计算机提供特殊的硬件指令,允许对一个字中的内容进行修正等通过硬件支持实现临界段问题的方法称为低级方法,或称元方法

1. 中断屏蔽方法

思想:利用“开/关中断指令”实现,不允许中断也就意味着不能发生进程切换,也就不可能发生同时访问临界区的情况

  • 优点:简单高效
  • 缺点:不适用于多处理机,只适用于操作系统内核进程,不适用于用户进程
2. 硬件指令方法

TestAndSet,简称TS指令,又称TSL指令(TestAndSetLock)

TS指令用硬件实现,执行的过程不允许被中断

// lock表示临界区是否被锁
// 检查并上锁
bool TestAndSet(bool *lock){
    bool old = *lock;
    *lock = true;
    return old;
}
// TSL实现互斥
while(TestAndSet(&lock)){...};
// 临界区
lock = false;
剩余区

Swap指令,又称Exchange指令、XCHG指令。用硬件实现,不允许被中断

Swap(bool* a, bool* b){
    bool temp = *a;
    *a = *b;
    *b = temp;
}
// 实现互斥
// lock表示临界区是否上锁
bool old = true;
while(old)  swap(&lock, &old);
// 临界区
lock = false;
// 剩余区

2.3.3 互斥锁(mutex lock

一个进程在进入临界区时获得锁(acquire),在退出临界区时释放锁(release)

每个互斥锁有一个bool变量avaiable,表明锁是否可用,进程试图获取不可用的锁时会被阻塞,直到锁被释放

acquire(){
    while(!available){...}
    available = false;
}
release(){
    available = true;
}

互斥锁的缺点是没有遵循忙等待,浪费CPU周期

2.3.4 信号量机制

信号量只能被两个标准原语wait和signal访问,又称P操作和V操作

1. 整型信号量

用一个整数型的变量作为信号量,用来表示系统中某种资源的数量

【408笔记】操作系统 第二章 进程和线程_第17张图片

整型信号量存在的问题:只要S<=0,就会不断测试。不满足“让权等待”的原则,会发生忙等

2. 记录型信号量

记录型信号量是不存在忙等现象的进程同步机制。除了表示资源数量的整型变量value,再增加一个进程链表L,用于链接所有等待该资源的进程
【408笔记】操作系统 第二章 进程和线程_第18张图片

优点:遵循了“让权等待”的原则,不会出现”忙等“现象

tips:默认S为记录型信号量

  1. PV操作是一种低级进程通信原语
  2. PV操作是由两个不可被中断的过程组成的

3. 用信号量机制实现进程同步

【408笔记】操作系统 第二章 进程和线程_第19张图片

  1. 需要使用某种资源时,先P一下
  2. 提供某种资源时,V一下
  1. PV操作实现进程同步,信号量的初始值由用户确定

4. 利用信号量实现进程互斥

【408笔记】操作系统 第二章 进程和线程_第20张图片

  1. 对临界区的访问紧夹在PV操作之间
  1. 一个进程在互斥信号量mutex上执行V(mutex)操作导致唤醒另一个进程时,执行V操作后mutex的值为:小于等于0

5. 利用信号量实现前驱关系

【408笔记】操作系统 第二章 进程和线程_第21张图片
【408笔记】操作系统 第二章 进程和线程_第22张图片

每一对前驱关系都是一个进程同步问题(需要保证一前一后的关系,设置多个信号量

2.3.5 管程

  1. 信号量机制存在问题:编写程序困难,容易出错
  2. 需要设计一种机制,让程序员写程序无需关注复杂的PV操作

1. 管程的定义和基本特征

管程是一种特殊的软件模块(有点像),由以下部分组成

  1. 局部于管程的共享数据结构说明
  2. 对该数据结构进行操作的一组过程(函数
  3. 对局部于管程的共享数据设置初始值的语句
  4. 管程有一个名字

管程的基本特征:

  1. 局部于管程的数据只可以被局部于管程的过程访问
  2. 一个进程只有通过调用管程内的过程才能进入管程访问共享数据
  3. 每次只允许一个进程在管程内执行某个内部过程(互斥
  4. 管程有很多“入口”,但每次只能开放其中一个入口,并且只有一个进程或线程可以进入。这种互斥特性是由编译器负责实现的
  5. 可以在管程中设置条件变量及等待/唤醒操作来解决同步问题
  1. 管程是进程同步工具,解决信号量机制大量同步操作分散的问题
  2. 管程每次只允许一个进程进入管程
  3. 管程中signal操作的作用和信号量机制中的V操作不同
  4. 管程是被进程调用的,管程是语法范围,无法创建和撤销
  5. 管程执行x.wait()时所做的工作:阻塞该进程,并将其插入x的阻塞队列中

2.3.6 经典同步问题

1. 生产者——消费者问题

系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区取出一个产品使用(产品理解为一种数据
生产者和消费者共享一个初始为空,大小为n的缓冲区
只有缓冲区没有满,生产者才能把产品放入缓冲区,否则必须等待
缓冲区不空时,消费者才能从中取出产品,否则必须等待
缓冲区是临界资源,进程必须互斥访问

semaphore mutex = 1;    // 互斥信号量
semaphore empty = n;    // 同步信号量,表示空闲缓冲区的数量
semaphore full = 0;     // 同步信号量,表示产品的数量

producer(){
    while(1){
        生产一个产品;
        P(empty);
        P(mutex);
        产品放入缓冲区;
        V(mutex);
        V(full);
    }
}

consumer(){
    while(1){
        P(full);
        P(mutex);
        从缓冲区使用取出一个产品;
        V(mutex);
        V(empty);
        使用产品;
    }
}
  • 实现互斥的P操作一定要在实现同步的P操作之后,否则会发生死锁(两个进程互相等待被对方唤醒
  • V操作不会导致进程堵塞,因此可以交换顺序
复杂化:多生产者——多消费者问题

桌子上有一只盘子,每次只能向其中放入一个水果。爸爸只放苹果,妈妈只放橘子;儿子只吃橘子,女儿只吃苹果。
盘子空时,爸妈才能向盘子中放水果;盘子中有自己想吃的水果时,儿女才能从盘子中取出水果

  1. 互斥关系:对缓冲区(盘子)的访问必须互斥进行
  2. 同步关系:父亲将苹果放入盘子后,女儿才能取出苹果
  3. 同步关系:母亲将橘子放入盘子后,儿子才能取出橘子
semaphore apple = 0;    // 苹果
semaphore orange = 0;   // 橘子
semaphore plate = 1;    // 盘子

dad(){
    while(1){
        准备一个苹果;
        P(plate);
        苹果放入盘子;
        V(apple);
    }
}

mom(){
    while(1){
        准备一个橘子;
        P(plate);
        橘子放入盘子;
        V(orange);
    }
}

daughter(){
    while(1){
        P(apple);
        从盘中取出苹果;
        V(plate);
        吃掉苹果;
    }
}

son(){
    while(1){
        P(orange);
        从盘中取出橘子;
        V(plate);
        吃掉橘子;
    }
}
  • 实现互斥的P操作一定要在实现同步的P操作之后,否则可能引起死锁
  1. 九个生产者、六个消费者共享容量为8的缓冲器的生产者-消费者问题中,互斥使用缓冲器的信号量初始值为:1

2. 读者——写者问题

有读者和写者两组并发进程,共享一个文件,要求如下:

  1. 允许多个读者同时对文件执行读操作
  2. 只允许一个写者往文件中写信息
  3. 任意写者在完成写操作之前不允许其他读者或写者工作
  4. 写者执行写操作前,必须让已有的读者和写者全部退出
  1. 互斥关系:写进程之间互斥,读进程之间没有互斥关系
semaphore rw = 1;   // 实现读写互斥
int count = 0;      // 记录当前有几个读进程在访问文件
semaphore mutex = 1;// 保证对count变量的互斥访问    
// semaphore w = 1; // 实现写优先

writer(){
    while(1){
        // P(w);
        P(rw);
        写文件;
        V(rw);
        // V(w);
    }
}

reader(){
    // P(w);
    P(mutex);
    if(count == 0)  // 第一个读进程来上锁
        P(rw);
    ++ count;
    V(mutex);
    // V(w);
    读文件;
    P(mutex);
    -- count;
    if(count == 0)  // 最后一个读进程来解锁
        V(rw);
    V(mutex);
}
  1. 潜在问题:可能导致写进程饿死
  2. 解决方法:增加一个w信号量,实现写优先
  3. 精髓在于计数器count遇到不太好解决的同步-互斥问题,需要考虑能否通过互斥访问的计数器count解决

3. 哲学家进餐问题

一张圆桌上有五名哲学家,每两个哲学家之间的桌子上摆一根筷子,桌子中间是食物
哲学家思考时,不影响其他人。哲学家饥饿时,会试图拿起左右两根筷子(一根一根拿起),如果筷子在其他人手上,则需要等待。饥饿的哲学家只有同时拿着两根筷子才可以开始进餐。进餐完毕后,放下筷子继续思考

  1. 五位哲学家与左右邻居对其间筷子的访问是互斥关系
  2. 设置信号量数组,编号0-4,左边的筷子是i,右边的筷子是(i+1)%5
  3. 如何预防死锁?
    1. 添加一些限制,比如最多只允许四个哲学家同时进餐,可以保证至少有一个哲学家可以拿到左右两只筷子
    2. 要求奇数编号哲学家先拿左边筷子,偶数哲学家则相反
    3. 仅当一个哲学家左右两边都有筷子,才允许吃饭

【408笔记】操作系统 第二章 进程和线程_第23张图片

4. 吸烟者问题

一个系统有三个抽烟者进程和一个供应者进程
每个抽烟者需要用三种材料卷烟并抽掉。第一个拥有烟草,第二个拥有纸,第三个拥有胶水
供应者无限地供应三种材料,每次将两种材料放在桌子上,拥有第三种材料的抽烟者就会使用它们卷烟并抽掉,然后给供应者一个信号告诉完成了,供应者就会放另外两种材料,这个过程一直重复(让三个抽烟者轮流抽烟

  1. 这道题本质上也是消费者——生产者问题
  2. 互斥关系:桌子抽象为缓冲区,容量为1(所以可以不设置互斥量
  3. 同步关系:三种组合才能发生对应的抽烟
  4. 同步关系:抽烟者发出完成信号->供应者将下一个组合放到桌子上
semaphore offer1 = 0;
semaphore offer2 = 0;
semaphore offer3 = 0;
semaphore finish = 0;
int i = 0;  // 用于实现三个抽烟者轮流抽烟

provider(){
    while(1){
        if(i == 0){
            组合1放在桌子上;
            V(offer1);
        }else if(i == 1){
            组合2放在桌子上;
            V(offer2);
        }else{
            组合3放在桌子上
            V(offer3);
        }
        i = (i+1)%3;
        P(finish);
    }
}

smoker1(){
    while(1){
        P(offer1);
        拿走组合1,卷烟抽掉;
        V(finish);
    }
}

smoker2(){
    while(1){
        P(offer2);
        拿走组合2,卷烟抽掉;
        V(finish);
    }
}

smoker3(){
    while(1){
        P(offer3);
        拿走组合3,卷烟抽掉;
        V(finish);
    }
}

2.4 死锁

2.4.1 死锁的概念

1. 死锁的定义

在并发环境下,各进程竞争资源导致的一种互相等待对方手中的资源,导致各个进程都被阻塞,都无法向前推进的现象 。如果没有外力推动,进程将无法向前推进

例如:某计算机系统只有一台打印机和一个输入设备,P1占有打印机,同时请求输入设备;P2占有输入设备,同时请求打印机。此时两个进程陷入死锁

2. 死锁产生的原因

系统资源的竞争

系统中不可剥夺资源的数量不能满足多个进程运行的需要

进程推进顺序非法

进程在运行过程中,请求和释放资源的顺序不当;信号量使用不当也会产生死锁

死锁产生的必要条件

只要以下任意一个条件不满足,死锁就不会发生

  1. 互斥条件:进程要求对所分配的资源进行排他性使用
  2. 不剥夺条件:进程所获得的资源在未使用完之前,不能被其他进程强行夺走,只能自己主动释放
  3. 请求并保持条件:进程已经保持了至少一个资源,提出新的资源请求,而该资源被其他进程占有,此时进程被阻塞,但对自己获得的资源保持不放
  4. 循环等待条件:存在一种进程资源的循环等待链,链中每个进程已获得资源同时被链中的下一个进程所请求

3. 死锁的处理策略

为了系统不发生死锁,必须设法破坏四个必要条件之一

  1. 预防死锁,破坏四个产生条件的一个或几个
  2. 避免死锁,用某种方法防止系统进入不安全状态(银行家算法
  3. 死锁的检测和解除,允许死锁发生,但操作系统负责监测处死锁的发生,然后解除死锁

【408笔记】操作系统 第二章 进程和线程_第24张图片

2.4.2 死锁预防

破坏导致死锁的必要条件

1. 破坏互斥条件

  1. 将资源改造为允许共享使用。例如SPOOLing技术
  2. 缺点:很多时候系统必须保护资源的互斥性

2. 破坏不剥夺条件

  1. 当某个进程请求新资源无法满足时,必须释放保持的所有资源,需要时再重新申请
  2. 可以由操作系统协助,强行剥夺。这种方式一般需要考虑进程优先级
  3. 缺点:实现起来复杂;释放资源可能造成前一阶段工作失效;反复申请释放资源会增加系统开销;可能导致饥饿

3. 破坏请求和保持条件

  1. 进程在运行前一次性申请完他所需要的所有资源。在资源未满足不会投入运行
  2. 缺点:资源利用率低;可能导致饥饿

4. 破坏循环等待条件

  1. 采用顺序资源分配法。给系统中的资源编号,规定每个进程必须按照编号递增的顺序请求资源
  2. 缺点:不方便增加新设备;进程使用资源的顺序可能不是递增,导致资源浪费;用户编程麻烦

2.4.3 避免死锁

避免死锁属于事先预防策略

1. 系统安全状态

安全序列:如果系统按照这种序列分配资源,可以满足每个进程对资源的最大需求,则每个进程都可以顺利完成
只要找出一个安全序列,系统就是安全状态。安全序列可能会有多个
系统处于安全状态时,一定不会发生死锁;进入不安全状态,有可能发生死锁;发生了死锁一定在不安全状态

2. 银行家算法

银行家算法核心思想:在进程提出资源申请时,先预判这次分配是否会导致系统进入不安全状态,如果会,则暂时阻塞该进程

银行家算法过程-解题

2.4.4 死锁的检测和解除

死锁的检测(死锁定理

  1. 用某种数据结构保存资源的请求和分配信息
    1. 进程节点、资源节点:圆圈表示进程,矩形框表示资源
    2. 请求边、分配边
  2. 提供算法检测是否进入死锁

【408笔记】操作系统 第二章 进程和线程_第25张图片
【408笔记】操作系统 第二章 进程和线程_第26张图片

解除死锁

  1. 资源剥夺法:挂起某些死锁进程,抢占资源
  2. 撤销进程法:强制撤销部分死锁进程
  3. 进程回退法:需要系统记录进程进程的历史信息,设置还原点
1. **若系统中有n个进程,每个进程需要使用某种临界资源m个,则不会发生死锁的该类资源的总数至少为:m+(n-1)(m-1)**
2. 区分死锁预防和死锁避免
3. 产生死锁的根本原因在于系统资源分配不足和进程推进顺序非法

408真题

(17 408)

  • 互斥关系的判断:共享变量、一读一写、都写
    【408笔记】操作系统 第二章 进程和线程_第27张图片
    (19 408)

  • 哲学家问题改进

【408笔记】操作系统 第二章 进程和线程_第28张图片

你可能感兴趣的:(操作系统,其他)