Part2 链接:笔记_408_操作系统_02. 进程与线程(Part2)
文章目录
02. 进程与线程
2.1 进程与线程
2.1.1 + 2.1.3 进程概述
进程的组成:PCB + 程序段 + 数据段(只要有一个不同就是不同的进程)
PCB 是给操作系统用的,程序段、数据段是给进程自己用的
PCB是进程存在的唯一标志,当进程被创建时,OS 为其创建PCB,当进程结束时,会回收其PCB;
OS 需要对各个并发运行的进程进行管理,但凡管理时所需要的信息,都会被放在PCB中;
PCB 中通常包括:PID、进程状态、进程队列指针、程序和数据地址、进程优先级、CPU现场、保护区等(不包括进程地址空间大小)
-
进程
- 进程是程序的一次执行过程
- 进程是一个程序及其数据在处理机上顺序执行时所发生的活动
- 进程是具有独立功能的程序在一个数据集上运行的过程
- 进程是进程实体(=进程影像)的运行过程
2.1.3 进程的状态与转换
阻塞态:又称等待态
终止态:首先将该进程设置为终止态,然后进一步处理资源释放和回收等工作
只有就绪队列中无进程时,CPU 才可能处空闲状态
2.1.4 进程控制
-
进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能
-
通过原语实现
-
原语是一种特殊的程序
-
原语的执行具有原子性,即执行过程只能一气呵成,期间不允许被中断
可以用开 / 关中断(特权指令)来实现原子性
-
-
进程控制会导致进程状态的转换。无论哪个进程控制原语,要做的无非三类事情:
-
更新PCB中的信息
- 所有的进程控制原语一定都会修改进程状态标志
- 剥夺当前运行进程的CPU使用权必然需要保存其运行环境
- 某进程开始运行前必然要恢复其运行环境
-
将 PCB 插入合适的队列
-
分配 / 回收资源
-
1. 进程的控制
创建过程中,如果资源不足,则并不是创建失败,而是处于创建态,等待资源
设备分配是通过在系统中设置相应的数据结构实现的,不需要创建进程,属于 OS 的 I/O 核心子系统的内容
启动程序执行时典型的引起进程创建的事件
2. 进程的终止
3. 进程的阻塞和唤醒
4. 进程的切换
2.1.5 进程的通信
-
PV 操作是一种低级通信方式(原语),高级通信方式是指以较高的效率传输大量数据的通信方式
【注意】P、V不是系统调用
-
共享存储
-
注意:进程空间一般是独立的,进程运行期间一般不能访问其他进程的空间(必须通过特殊的系统调用实现),但进程内的线程是自然共享进程空间的
-
OS 只负责为通信进程提供可共享的存储空间和同步互斥工具(如P、V原语)
数据交换由用户自己安排读 / 写指令完成
-
-
消息传递
- 当前使用最广泛
- 在微内核 OS 中,微内核和服务器之间的通信就采用消息传递机制,该机制可以很好地支持多处理机系统、分布式系统和计算机网络,故成为这些领域最主要的通信工具
-
管道通信
-
数据在管道中先进先出的
-
当管道写满时,写进程将阻塞,直到读进程将管道中的数据取走,即可唤醒写进程
当管道读空时,读进程将阻塞,直到写进程往管道中写入数据,即可唤醒读进程
-
写进程往管道写数据,即便管道没被写满,只要管道没空,读进程就可以从管道读数据
读进程从管道读数据,即便管道没被读空,只要管道没满,写进程就可以往管道写数据
-
管道中的数据一旦被读出,就彻底消失。因此,当多个进程读同一个管道时,可能会错乱。对此,通常有两种解决方案:
- 一个管道允许多个写进程,一个读进程`(2014年408真题高教社官方答案)
- 允许有多个写进程,多个读进程,但系统会让各个读进程轮流从管道中读数据(Linux 的方案)
-
为了协调双方的通信,管道机制必须提供三方面的信息:互斥、同步和确定对方存在
-
2.1.6 线程和多线程模型
- 引入进程:并发、资源利用率和系统吞吐量
- 引入线程:减少并发执行过程中的时空开销,提高并发性能
1. 线程的概念与特点
-
线程是“轻量级进程”,是一个基本的CPU执行单位,是程序执行流的最小单位
由线程ID、程序计数器、寄存器集合和堆栈组成
-
不同线程可以执行相同的程序,即同一个服务程序被不同用户调用时,OS 把他们创建成不同的线程
2. 线程的组织和控制
-
线程控制块 TCB
- 线程标识符:TID,与 PID 类似
- 一组寄存器:包括程序计数器、状态寄存器和通用寄存器
- 线程运行状态:运行/就绪/阻塞
- 优先级:线程调度、资源分配的参考
- 线程专有存储区:线程切换时用于保存现场
- 堆栈指针:用于过程调用时保存局部变量及返回地址
线程切换时要保存 / 恢复:程序计数器 PC、其他寄存器、堆栈指针
同一进程的所有线程都完全共享进程的地址空间和全局变量(但各个线程有自己的栈空间)。各个线程都可以访问进程地址空间的每个单元,所以一个线程可以读、写甚至清除另一个线程的堆栈
- 进程的代码段、进程打开的文件、进程的全局变量等都是进程的资源,唯有进程中某线程的栈指针(包含在线程TCB中)是属于线程的
- 属于进程的资源可以共享,属于线程的栈指针是独享的,对其他线程透明
-
线程是具有生命期的,由创建而产生,由调度而执行,由终止而消亡
-
线程的创建:线程创建函数,执行完将返回一个线程标识符
-
线程的终止:线程线程的函数
- 有些线程(主要是系统线程)一旦被建立,便一直运行不会终止
- 通常,线程被终止后并不会立即释放它所占有的资源,只有当进程中的其他线程执行了分离函数后,被终止线程才与资源分离,此时的资源才能被其他线程利用
- 被终止但尚未释放资源的线程仍可被其他线程调用,以使被终止线程重新恢复运行
-
3. 线程的实现方式和多线程模型
1)实现方式
-
用户级线程 ULT
- 用户级线程不需要内核的支持,与系统平台无关,可以在任何 OS 中运行
-
**内核级线程 KLT **
- OS 中,无论是系统进程还是用户进程,都是在 OS 内核的支持下运行的,与内核紧密相关
-
组合方式
- 支持多个内核级线程的建立、调度和管理,同时允许用户程序建立、调度和管理用户级线程
- 一些内核级线程对应多个用户级线程,这是用户级线程通过时分多路复用内核级线程实现的
- 同一进程中的多个线程可以同时在多处理机上并行执行,且在阻塞一个线程时不需要将整个进程阻塞
- 能结合 KLT 和ULT 的优点,并且克服各自的不足
2)多线程模型
多线程指一个程序中可以定义多个线程并同时运行他们,每个线程可以运行不同的任务。
-
一对一模型(如上图)
-
多对一模型
-
多对多模型
适合多线程解决的业务场景:可以将处理逻辑拆分成多个互不相干的部分;计算量较大,需要高并发
- 适用例子:并行执行矩阵乘法运算;Web 服务器请求 HTTP 服务;基于 GUI 的debugger 处理用户的输入、机算、跟踪等操作
- 不适用例子:键盘驱动程序为每个正在运行的应用配备一个线程,用来跟踪相应的键盘输入(键盘:低速;CPU:高速,完全可以处理)
3)线程库
- 是为程序员提供创建和管理线程的 API
- 实现的主要方法
- 在用户空间提供一个没有内核支持的库
- 库内代码和数据结构都位于用户空间,故调库内的函数只导致用户空间的一个本地函数的调用
- 实现由 OS 直接支持的内核级的一个库
- 库内代码和数据结构位于内核空间。调用库中的一个 API 函数通常会导致对库内函数的调用
- 在用户空间提供一个没有内核支持的库
- 目前使用的三种主要的线程库
- POSIX Pthreads:POSIX 的扩展,可提供用户级和内核级的库
- Windows API:用于 Windows 系统的内核级线程库
- Java
- Java 线程 API 允许线程在 Java 程序中直接创建和管理
- Java 线程 API 通常采用宿主系统的线程库来实现:Windows 中用 Windows API;UNIX 系统中用 Pthreads
Notes
-
程序的封闭性:进程执行结果只取决于进程本身,不受外界影响
并发进程失去封闭性:并发进程共享变量,其执行结果与速度有关
-
C语言编写的程序在使用内存时一般分为三个段
- 正文段(代码和赋值数据段):二进制代码和常量、全局变量
- 数据堆段:动态分配的存储区
- 数据栈段:临时使用的变量、未赋值的局部变量和实参传递
-
进程 vs 程序
-
进程是程序的一次执行过程,具有生命周期,是暂时存在的
程序是一组代码的集合,是永久存在的,可以长期保存
-
静态的程序除了占用磁盘空间外,不需要其他系统的资源
动态的进程才需要分配内存、CPU等系统资源
-
程序是进程的核心内容。进程和程序相关,进程包含了程序,没有程序就没有进程
进程是程序执行的载体。进程不仅仅是程序,还包含了程序在执行过程中使用的全部资源
-
关系
- 一对一:执行一条命令或运行一个应用程序时
- 一对多:进程在执行过程中可以加载执行不同的程序
- 多对一:以不同的参数或数据执行同一应用程序
- 多对多:并发地执行不同的应用程序
-
-
父进程 vs 子进程:父进程创建子进程后,父进程和子进程并发执行
-
二者有一定的关系,但仍然是两个不同的进程,撤销一个并不一定会导致另一个也撤销
父进程撤销后,子进程可能有两种状态:① 子进程一并被终止 ② 子进程成为孤儿进程
-
父进程与子进程共享一部分资源,但不能共享虚拟地址空间,在创建子进程时,会为子进程分配资源,如虚拟地址空间等
主程序 vs 子程序:主程序调用子程序后,主程序暂停在调用点,子程序开始执行,直到子程序返回,主程序才开始执行
-
-
多线程 vs 多任务
- 多任务:针对 OS 而言地,代表 OS 可以同时执行的程序个数
- 多线程:针对一个程序而言,代表一个程序可以同时执行的线程个数,每个线程可以完成不同的任务
【2018.22】多任务操作系统
现代 OS 都是多任务的,允许用户把程序分为若干任务,使它们并发执行。在单 CPU 中,这些任务并发执行(宏观并行,微观分时交替执行);在多 CPU 中,这些任务是真正的并行执行。
引入中断之后才出现多任务操作系统(中断方式的的特点是 CPU 与外设并行工作 → 多任务 OS 具有并发和并行的特点)
-
为何进程之间的通信必须借助 OS 内核功能?
每个进程有自己独立的地址空间。在操作系统和硬件的地址保护机制下,进程无法访问其他进程的地址空间,必须借助于系统调用函数实现进程之间的通信。
2.2 处理机调度
2.2.1 调度的概念、层次
进程调度由调度算法决定 CPU 使用权,由 OS 实现,不需要硬件支持
“某种规则”→ 调度算法要研究的问题
作业:一个具体的任务;作业调度 → 好几个程序需要启动,要先启动哪一个
用户向系统提交一个作业 ≈ 用户让 OS 启动一个程序(来处理一个具体的任务)
- 挂起态和七状态模型
2.2.2 调度目标(调度算法的评价指标)
-
周转时间:从作业被提交给系统开始,到作业完成为止的这段时间间隔
- 包括四个部分:作业在外存后备队列上等待作业调度(高级调度)的时间、进程在就绪队列上等待进程调度(低级调度)的时间(就绪态)、进程在CPU上执行的时间(运行态)、进程等待I/O操作完成(阻塞态)的时间
- 后三项在一个作业的整个处理过程中,可能发生多次
-
等待时间:进程/作业处于等待处理机状态时间之和,等待时间越长,用户满意度越低
- 对于进程:等待时间就是指进程建立后等待被服务的时间之和,在等待I/O完成的期间其实进程也是在被服务的,所以不计入等待时间
- 对于作业:不仅要考虑建立进程后的等待时间,还要加上作业在外存后备队列中等待的时间
2.2.3 进程调度的实现
-
调度程序(调度器):调度和分派 CPU 的组件
-
组成
-
排队器:将系统内所有就绪进程按照一定策略排成一个或多个队列
-
分派器:从就绪队列中取出选中的进程,将 CPU 分配给进程
-
上下文切换器
-
-
触发事件:创建新进程;进程退出;运行进程阻塞;I/O 中断发生(可能唤醒某些阻塞进程)
-
不同调度策略
-
非抢占式:只有运行进程阻塞或退出才触发调度程序工作
-
抢占式:每个时钟中断或 k 个时钟中断会触发调度程序工作
-
-
对象
-
不支持内核级线程的 OS:进程
-
支持内核级线程的 OS:内核线程
-
-
-
闲逛程序 idle
-
线程切换时,如果系统中没有就绪进程,就会调度闲逛进程运行,如果没有其他进程就绪,该进程就一直运行,并在执行过程中测试中断。
-
优先级最低,没有就绪进程时才会运行闲逛进程,只要有进程就绪,就会立即让出处理机。
-
可以是 0 地址指令,占一个完整的指令周期(指令周期末尾例行检查中断)
-
不需要 CPU 之外的资源,不会被阻塞
-
-
两种线程的调度
- 用户级线程调度
- 内核并不知道线程的存在,还是和以前一样,选择一个进程,并给予时间控制
- 由进程中的调度程序决定哪个线程运行
- 用户级线程的线程切换在同一进程中进行,仅需少量的机器指令
- 内核级线程调度
- 内核选择一个特定线程运行,通常不用考虑该线程属于哪个进程
- 对被选择的线程赋予一个时间片,如果超过了时间片,就会强制挂起该线程
- 内核级线程的线程切换需要完整的上下文切换、修改内存映像、使高速缓存失效,这就导致了若干数量级的延迟
- 用户级线程调度
-
进程在操作系统内核程序临界区中不能进行调度与切换,但是进程在普通临界区中是可以进行调度、切换的
- 临界区:访问临界资源的那段代码
- 内核程序临界区一般是用来访问某种内核数据结构的,比如进程的就绪队列
-
“狭义的进程调度” 与 “进程切换” 的区别
-
狭义的进程调度
-
决策行为
-
从就绪队列中选中一个要运行的进程,是决定资源分配给哪个进程的行为。(这个进程可以是刚刚被暂停执行的进程,也可能是另一个进程,后一种情况就需要进程切换)
-
-
进程切换
- 执行行为
- 指一个进程让出处理机,由另一个进程占用处理机的过程,是实际分配的行为。
-
-
广义的进程调度:包含了选择一个进程和进程切换两个步骤
-
进程切换往往在调度后立刻发生的,现场切换时,OS 内核将原进程的现场信息推进当前进程的内核堆栈来保存,并更新堆栈指针
2.2.5 进程切换
-
内核支持下实现
-
上下文切换(只发生在内核态)
-
上下文:某一时刻 CPU 寄存器和程序计数器的内容
-
实质:处理机从一个进程的运行转到另一个进程上运行(运行环境发生实质性变化)
-
处理机切换时会发生两对切换操作
-
当前进程的上下文保存到其 PCB,装入分派程序的上下文信息,以便分派程序运行
-
移出分派程序的上下文,将新选进程的 CPU 现场信息装入处理机的各个寄存器
-
-
上下文切换时,需要执行大量
load
和store
指令,以保存寄存器的内容,会花费很多时间- 现已有硬件方法来减少上下文切换时间
- 两组寄存器(多组):一组给用户一组给内核,这样切换时只需要改变指针,指向当前寄存器组
-
-
上下文切换 vs 模式切换
- 模式切换:用户态和内核态的切换(CPU 逻辑上可能还在执行同一个程序)
2.2.4 调度算法
-
这三种算法一般适合用于早期的批处理系统
-
FCFS
有利于 CPU 繁忙型作业,不利于 I/O 繁忙型作业
-
SJF / SPF
适合 I/O 繁忙型(占用 CPU 的时间少,频繁放弃 CPU)
-
抢占式:最短剩余时间优先算法 SRTN
-
每当有进程加入就绪队列改变时就需要调度,如果新到达的进程剩余时间比当前运行的进程剩余时间更短,则由新进程抢占处理机,当前运行进程重新回到就绪队列
-
另外,当一个进程完成时也需要调度
-
-
-
HRRN
-
响应比 = 等待时间 + 要求服务时间 要求服务时间 响应比 = \frac{等待时间 + 要求服务时间}{要求服务时间} 响应比=要求服务时间等待时间+要求服务时间
- 这三种算法适合用于交互式系统
-
时间片轮转 RR
- 时间片太大——退化为FCFS
- 时间片太小——开销大
- 时间片长短的考虑因素:系统的响应时间、就绪队列的进程数目、系统的处理能力
-
优先级调度
- 优先级可否改变
- 静态优先级:创建进程时确定,之后一直不变
- 主要依据:进程类型、进程对资源的要求、用户要求
- 动态优先级:创建进程时有一个初始值,之后会根据情况动态地调整优先级
- 主要依据:占有CPU时间的长短、等待CPU时间的长短
- 静态优先级:创建进程时确定,之后一直不变
- 优先级设置原则
- 系统进程 > 用户进程
- 交互式进程 > 非交互式进程
- 前台进程 > 后台进程
- I/O 型进程 > 计算型进程(I/O设备和CPU可以并行工作)
- 优先级可否改变
-
多级反馈队列
-
算法规则
-
设置多级就绪队列,各级队列优先级从高到低,时间片从小到大
-
新进程到达时先进入第 1 级队列,按FCFS原则排队等待被分配时间片,若用完时间片进程还未结束,则进程进入下一级队列队尾。如果此时已经是在最下级的队列,则重新放回该队列队尾
-
只有第 k 级队列为空时,才会为 k+1 级队头的进程分配时间片
-
抢占式
在 k 级队列的进程运行过程中,若更上级的队列(1~k-1级)中进入了一个新进程,则由于新进程处于优先级更高的队列中,因此新进程会抢占处理机,原来运行的进程放回 k 级队列队尾
-
-
优势
- 终端型作业用户:短作业优先
- 短批处理作业用户:周转时间较短
- 长批处理作业用户:经过前面几个队列得到部分执行,不会长期得不到处理
-
-
多级队列调度算法
- 在系统中设置多个就绪队列,将不同类型或性质的进程固定分配到不同的就绪队列,每个队列可实施不同的调度算法
notes
- 分时系统中,响应时间与时间片和用户数成正比
- 高响应比优先、时间片轮转、多级反馈队列都适用于** **(都能保证每个任务在一定时间内分配到时间片)
- UNIX 属于分时操作系统