Download presentation
Presentation is loading. Please wait.
1
第二章 进程的描述与控制管理
2
本章要点(1/3) 目标:建立起进程的概念,掌握好进程控制、进程同步和进程间通信的基本概念。 进程的基本概念 进程同步的基本概念
为什么要引入进程 进程的基本特征 进程的状态 进程控制块 进程同步的基本概念 临界资源 临界区 同步机制应遵循的准则
3
本章要点(2/3) 信号量机制及其应用 经典进程的同步问题 信号量的含义 信号量的物理意义 用信号量实现互斥 用信号量实现前趋关系
生产者-消费问题 哲学家进餐问题 读者-写者问题 这些问题用于解决什么问题 如何实现进程互斥 如何实现进程同步
4
本章要点(3/3) 消息传递通信机制 线程的基本概念 内核支持线程和用户级线程 什么是消息传递通信机制 消息传递通信机制的实现方式
如何协调发送进程和接收进程 消息缓冲队列通信机制 线程的基本概念 为什么要引入线程 线程的特征 创建和终止线程 内核支持线程和用户级线程 什么是内核支持线程 什么是用户级线程
5
本章内容 2.1 前趋图和程序执行 2.2 进程的描述 2.3 进程控制 2.4 进程同步 2.5 经典进程的同步问题 2.6 进程通信 2.7 线程的基本概念 2.8 线程的实现
6
2.1 前趋图和程序执行
7
2.1.1 前趋图 前趋图是一个有向无循环图,用于描述程序段或进程之间执行的先后顺序。图中的结点用于表示一个程序段或一个进程,结点间的有向边用于表示两个结点之间存在的偏序或前趋关系“→”。 →={(Pi, Pj)|Pi must complete before Pj may start}, 如果(Pi, Pj)∈→,可写成Pi→Pj,称Pi是Pj的直接前趋,而称Pj是Pi的直接后继。在前趋图中,把没有前趋的结点称为初始结点,把没有后继的结点称为终止结点。
8
图 2-1 前趋图 每个结点还具有一个重量,用于表示该结点所含有的程序量或结点的执行时间。 P2 P3 P4 P5 S1 S2 S3 P8
(a)具有九个结点的前趋图 (b)具有循环的图 图 2-1 前趋图
9
2.1.2 程序顺序执行 1、程序的顺序执行 I : 输入操作 C : 计算操作 P : 打印操作 S1 : a : = x + y;
S2 : b : = a - 5; S3 : c : = b + 1; I1 C1 P1 I2 C2 P2 S1 S2 S3 (a) 程序的顺序执行 (b) 三条语句的顺序执行 图 2-2 程序的顺序执行
10
2、程序顺序执行的特征 顺序性:处理机的操作必须严格按照程序所规定的顺序执行。
封闭性:程序在执行时独占系统的全部资源,机器资源状态的改变只与执行的程序有关,而与外界环境无关。 可再现性:只要初始条件相同,一个程序的多次重复执行,将得到相同的结果。
11
2.1.3 程序并发执行 1、程序的并发执行 图 2-3 并发执行时的前趋图 I1 I2 I3 I4 C1 C2 C3 C4 P1 P2
程序并发执行 1、程序的并发执行 I1 I2 I3 I4 C1 C2 C3 C4 P1 P2 P3 P4 图 2-3 并发执行时的前趋图
12
1、程序的并发执行 图 2-4 四条语句的前趋关系 例子: 对于具有下述四条语句的程序段 S1: a∶=x+2 S2: b∶=y+4
S3: c∶=a+b S4: d∶=c+b 图 2-4 四条语句的前趋关系
13
2、程序并发执行时的特征 间断性:由于资源共享和相互合作,并发执行的程 序间形成了相互制约关系,导致程序的运行过程出 现“执行-暂停-执行”的现象。 失去封闭性:程序在执行时与其他并发执行的程序 共享系统的资源,因此,资源状态的改变还与其他 程序有关,即程序本身的执行环境要受到外界程序 的影响。 不可再现性:同样的初始条件,一个程序的多次重 复执行,可得到不同的结果。
14
2、程序并发执行时的特征 例如,有两个循环程序A和B,它们共享一个变量N。程序A每执行一次时,都要做N∶=N+1操作;程序B每执行一次时,都要执行Print(N)操作,然后再将N置成”0”。程序A和B以不同的速度运行。 (1) N∶=N+1在Print(N)和N∶=0之前,此时得到的N值分别为n+1, n+1, 0。 (2) N∶=N+1在Print(N)和N∶=0之后,此时得到的N值分别为n, 0, 1。 (3) N∶=N+1在Print(N)和N∶=0之间,此时得到的N值分别为n, n+1, 0。
15
2.2 进程的描述
16
2.2.1 进程的定义和特征 1、进程的定义 引入进程的目的:为了能使程序并发执行,并对并发执行的程序加以描述和控制。
进程的定义和特征 1、进程的定义 引入进程的目的:为了能使程序并发执行,并对并发执行的程序加以描述和控制。 进程实体 = 程序段 + 数据段 + PCB PCB是用于描述进程的基本情况和活动过程的数据结构 系统利用PCB控制和管理进程
17
1、进程的定义 进程的典型定义: 进程是程序一次执行 进程是一个程序及其数据在处理机上顺序执行时所发生的活动
进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。 传统OS中进程的定义:进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。
18
2、进程的特征 动态性 并发性 独立性 异步性 进程的并发执行结果是可再现的。 进程的实质是进程实体的一次执行过程
进程具有一定的生命周期:由创建而产生,由调度而执行,由撤消而消亡。 并发性 多个进程实体同存于内存中,且能在一段时间内同时运行。 独立性 进程实体是一个能独立运行、独立分配资源和独立接受调度的基本单位。 异步性 进程可按各自独立的、不可预知的速度向前推进。 进程的并发执行结果是可再现的。
19
2.2.2 进程的基本状态及转换 1、进程的三种基本状态 就绪(Ready)状态 执行(Running)状态 阻塞(Block)状态
进程的基本状态及转换 1、进程的三种基本状态 就绪(Ready)状态 执行(Running)状态 阻塞(Block)状态 对单个进程: 任何时刻,进程只能处理三种基本状态之一; 随着进程自身的推进和外界条件的变化,进程的状态可以动态地转换 对整个系统: 每个时刻允许同时有多个进程处于就绪/阻塞状态; 对于执行状态的进程,每个处理机最多只允许有一个。
20
2、三种基本状态的转换 就绪 时间片完 进程调度 I/O完成 阻塞 执行 终止 I/O请求 图 2-5 进程的三种基本状态及其转换
21
3、创建状态和终止状态 为了满足PCB对数据及操作系统的完整性,增加管理 的灵活性,又引入了创建状态与终止状态: 创建进程的工作
为进程分配运行时所必须的资源; 把该进程转入就绪状态并插入就绪队列之中。 引入创建状态的目的 保证进程的调度必须在创建工作完成后进行,确保进程控制块操作的完整性。 增加管理的灵活性,操作系统可以根据系统性能或主存容量的限制,推迟创建状态进程的提交。 21
22
3、创建状态和终止状态 终止进程的工作 引起进程终止的事件 等待操作系统进行善后处理; 将进程PCB清零,并将PCB空间返还系统 自然结束;
出现无法克服的错误; 被操作系统终结,或其他有终止权的进程所终结。 22
23
3、创建状态和终止状态 许可 创建 就绪 时间片完 进程调度 I/O完成 阻塞 执行 释放 终止 I/O请求
图 2-6 进程的五种基本状态及转换 23
24
2.2.3 挂起操作和进程状态的转换 1、挂起操作的引入 挂起:实质是使进程不能继续执行 引起挂起的原因: 被挂起的进程处于静止状态;
挂起操作和进程状态的转换 挂起:实质是使进程不能继续执行 被挂起的进程处于静止状态; 没被挂起的进程处于活动状态。 引起挂起的原因: 终端用户的请求; 父进程请求; 负荷调节的需要; 操作系统的需要; 1、挂起操作的引入
25
2、引入挂起原语操作后三个进程状态的转换 活动就绪(Readya) 静止就绪(Readys) 活动阻塞(Blockeda)
Suspend 活动就绪(Readya) 静止就绪(Readys) 活动阻塞(Blockeda) Suspend 静止阻塞(Blockeds) 静止就绪(Readys) Active 活动就绪(Readya) Active 静止阻塞(Blockeds) 活动阻塞(Blockeda) 终止 执行 进程调度 请求I/O 挂起 时间片完 图 2-7 具有挂起状态的进程状态图 活动 就绪 激活 静止 就绪 唤醒 挂起 激活 活动 阻塞 静止 阻塞 唤醒 挂起
26
3、引入挂起操作后五进程状态的转换 创建 许可 释放 许可 终止 执行 进程调度 请求I/O 挂起 激活 唤醒 挂起 激活 唤醒 挂起
时间片完 活动 就绪 激活 静止 就绪 唤醒 挂起 激活 唤醒 活动 阻塞 静止 阻塞 挂起 图 2-8 具有创建、终止和挂起状态的进程状态图
27
2.2.4 进程管理中的数据结构 1、操作系统中用于管理控制的数据结构 内存表 内存 设备 设备表 进程实体及所用资源列表 文件 文件表
进程管理中的数据结构 1、操作系统中用于管理控制的数据结构 内存 内存表 设备 设备表 进程实体及所用资源列表 文件 文件表 进程1 进程 进程1 进程2 … 进程3 进程实体及所用资源列表 … 进程n 进程n 图2-9 操作系统控制表的一般结构
28
2、进程控制块PCB的作用 进程控制块PCB(Process Control Block):
是进程实体的一部分 是操作系统中最重要的记录型数据结构 记录了操作系统所需的、用于描述进程当前情况以及控 制进程运行的全部信息 PCB的作用:使一个在多道程序环境下不能独立运行的程序成为一个能独立运行的基本单位,一个能与其他进程并发执行的进程 PCB必须长驻内存
29
2、进程控制块的作用 PCB的作用 作为独立运行单位的标志 PCB是进程存在的唯一标志 能实现间断性运行方式 具有保存与恢复CPU现场的手段
提供进程管理所需要的信息 在进程的生命期中,OS总是通过PCB实施对进程的 控制和管理 提供进程调度所需要的信息 进程状态、优先级等 实现与其他进程的同步与通信 在PCB中还具有实现进程通信的区域或通信队列指针
30
3、进程控制块中的信息 进程标识符 处理机状态 进程调度信息 进程控制信息
31
进程标识符 1)进程标识符 进程标识符用于唯一地标识一个进程。
(1)内部标识符。在所有的操作系统中,都为每一个进程赋予一个唯一的数字标识符,它通常是一个进程的序号。 进程标识符 (2)外部标识符 由创建者提供,通常是由用户在访问该进程时使用。
32
2)处理机状态 处理机状态信息主要是由处理机的各种寄存器中的内容组成的。处理机运行时,许多信息都存放在寄存器中。当处理机被中断时,所有这些信息都必须保存在PCB中,以便该进程重新执行时,能从断点继续执行。这些寄存器包括: 通用寄存器:用户可视寄存器,用于暂存信息,用户程序可以访问; 指令计数器:存放了要访问的下一条指令的地址; 程序状态字PSW:其中包含了状态信息,如条件码、执行方式、终端屏蔽标志等; 用户栈指针:每个用户进程都有一个或若干个与之相关的系统栈,用于存放过程和系统调用参数及调用地址。栈指针指向栈顶。
33
3)进程调度信息 在PCB中还存放一些与进程调度和进程对换有关的信息,包括: 进程状态 指明进程的当前状态,作为进程调度和对换时的依据;
进程状态 指明进程的当前状态,作为进程调度和对换时的依据; 进程优先级 优先级高的进程应先获得处理机 进程调度所需的其它信息 与所采用的进程调度算法有关,比如,进程已等待CPU的时间总和、进程已执行的时间总和等; 事件 即阻塞原因,指进程由执行状态转变为阻塞状态所等待发生的事件。
34
4)进程控制信息 程序和数据的地址:指进程的程序和数据所在的内存或外存地址,以便再调度到该程序执行时,能从PCB中找到其程序和数据;
进程同步和通信机制:指实现进程同步和通信必需的机制,如消息队列指针、信号量等; 资源清单:是一张列出了使用CPU以外的、进程所需的全部资源及已经分配到该进程的资源的清单; 链接指针:它给出了本进程(PCB)所在队列中的下一个进程的PCB的首地址。
35
4、进程控制块的组织方式 线性方式:将系统中所有PCB都组织在一张线性表中,将该表的首地址存放在内存的一个专用区域中
特点:实现简单、开销小,每次查找都需要扫描整张表,适合进程数目不多的系统 PCB1 PCB2 PCB3 … PCBn 图2-10 PCB线性表示意图
36
4、进程控制块的组织方式 链接方式:把具有相同状态进程的PCB,用其中的链接字链接成一个队列 PCB1 4 执行指针 PCB2 3 PCB3
就绪队列指针 PCB4 8 PCB5 阻塞队列指针 PCB6 7 PCB7 9 空闲队列指针 PCB8 P5正在执行; 就绪进程有:P1,P4,P8 阻塞进程有:P2,P3 其余为空闲进程控制块PCB PCB9 15 … 图2-11 PCB链接队列示意图
37
4、进程控制块的组织方式 索引方式:系统根据所有进程的状态建立几张索引表,并把各索引表在内存的首地址记录在内存的一些专用单元中。在每个索引表的表目中,记录具有相应状态的某个PCB在PCB表中的地址。 PCB1 执行指针 就绪索引表 PCB2 PCB3 就绪表指针 PCB4 PCB5 阻塞索引表 PCB6 阻塞表指针 PCB7
38
2.3 进程控制
39
2.3 进程控制 进程控制是进程管理中最基本的功能: 进程控制是操作系统的内核通过原语来实现的。
2.3 进程控制 进程控制是进程管理中最基本的功能: 创建和撤消进程 对进程在整个生命期中各种状态之间的转换进行有效控制。 进程控制是操作系统的内核通过原语来实现的。 原语:是指由若干条指令组成,用来实现某个特定操作的一个过程。 原语的执行具有原子性; 原语常驻内存,且在系统态下执行。
40
操作系统内核 1、OS内核 OS内核:将一些与硬件紧密相关的模块、各种常 用设备的驱动程序以及运行频率较高的模块安排 在紧靠硬件的软件层次中,常驻内存 OS划分层次的目的是: 便于对这些软件进行保护,防止遭受其他应用程序的破 坏 提高OS的运行效率
41
系统态和用户态是处理机的两种执行状态,用于防止用户程序破坏操作系统内核代码和关键数据。
2、处理机的执行状态 系统态和用户态是处理机的两种执行状态,用于防止用户程序破坏操作系统内核代码和关键数据。 系统态:也叫管态或内核态,它具有较高的特权,能执行一切指令,访问所有寄存器和存储区。 通常操作系统内核就运行在系统状态。 用户状态:也叫目态,是一种具有较低特权的执行状态,它只能执行规定的指令、访问指定的寄存器和存储区。 通常用户程序都是运行在用户态。
42
3、内核包含的功能 支撑功能:为OS其他众多模块提供所需要的一些 基本功能 资源管理功能 中断处理 时钟管理 原语操作 进程管理 存储器管理
设备管理
43
2.3.2 进程的创建 1、进程图(Process Graph) 进程图:用于描述一个进程的家庭关系的有向树。 父进程 子进程 祖先进程
进程的创建 1、进程图(Process Graph) 进程图:用于描述一个进程的家庭关系的有向树。 父进程 子进程 祖先进程 祖先 子进程可以继承父进程所拥有的资源 注:Windows中不存在任何进程层次结构 A B C D E F G H I J K L M 图 2-9 进程树
44
2、引起创建进程的事件 用户登录。 (2) 作业调度。 (3) 提供服务。 (4) 应用请求。 由系统内核创建新进程 由应用程序创建新进程
45
3、进程的创建(Creation of Progress)
功能:创建进程控制块PCB 入口信息:进程标识符、优先 级、进程开始地址、初始CPU 状态、资源清单等 PCB的初始化: 初始化标识信息:系统分配的标 识符与父进程标识符; 初始化处理机状态信息:PC指向 程序入口地址,栈指针指向栈顶; 初始化处理机控制信息:进程状 态设置为就绪,设置优先级; 开始 申请空白PCB 为新进程分配资源 初始化进程控制块 将新进程插入就绪队列
46
2.3.3 进程的终止 1、引起进程终止(Termination of Process)的事件 1) 正常结束 3)外界干预 2) 异常结束
进程的终止 1、引起进程终止(Termination of Process)的事件 1) 正常结束 2) 异常结束 越界错误 保护错 非法指令 特权指令错 运行超时 等待超时 算术运算错 I/O故障 3)外界干预 操作员或操作系统干预 父进程请求 父进程终止
47
2、进程的终止过程 终止原语: 功能:终止一个指定的进程 入口信息:被终止进程的标识符 实现过程:
根据被终止进程的标识符,从PCB集合中检索进程的 PCB,读出该进程的状态; 若被终止进程正在执行,则终止它的执行,并置重新调 度标志; 若进程还有子孙进程,终止属于该进程的所有子孙进程; 将被终止进程所拥有的全部资源归还给其父进程或系统; 将终止进程移出它所在队列,等待其它程序搜集信息。
48
2.3.4 进程的阻塞与唤醒 1、引起进程阻塞和唤醒的事件 向系统请求共享资源失败 等待某种操作的完成 新数据尚未到达 等待新任务的到达
进程的阻塞与唤醒 1、引起进程阻塞和唤醒的事件 向系统请求共享资源失败 主动阻塞, 被释放资源的其他进程唤醒 等待某种操作的完成 启动I/O后主动阻塞,I/O完成后由中断处理程序唤醒 新数据尚未到达 等待新任务的到达
49
2、进程的阻塞过程 阻塞原语(block()): 功能:将进程从执行状态转换成阻塞状态 入口信息:可省略 实现过程:
停止进程执行,将其状态改为阻塞状态; 将其PCB插入相应的阻塞队列; 转调度程度进行重新调度,进行上下文切换。 进程的阻塞是进程的主动行为
50
3、进程唤醒过程 唤醒原语(wakeup()): 功能:当被阻塞进程所等待的事件完成时,唤醒 某一处于等待队列当中的进程。
入口信息:被唤醒进程的标识符 实现过程: 将被阻塞的进程从等待该事件的阻塞队列中移出; 将其PCB中的状态由阻塞改为就绪; 将该PCB插入到就绪队列中。
51
2.3.5 进程的挂起与激活 1、进程的挂起 挂起原语(suspend()): 功能:将指定进程挂起 实现过程:
进程的挂起与激活 1、进程的挂起 挂起原语(suspend()): 功能:将指定进程挂起 实现过程: 挂起进程处于活动就绪状态,将其转换为静止就绪; 挂起进程处于活动阻塞状态,将其转换为静止阻塞; 把被挂起的进程的PCB复制到某指定的内存区域; 若被挂起的进程正在执行,则转向调度程序重新调度。 如果挂起是为了对换,则在挂起进程时还必须将它换出 到外存
52
2、进程的激活 激活原语(active()): 功能:使处于静止状态的进程变为活动 执行过程:
若进程处于静止阻塞状态,则将它转换成活动阻塞状态; 若进程为静止就绪状态,则将它转换为活动就绪状态; 若进程转换成活动就绪状态,而系统又采用抢占调度策 略,则应检查该进程是否有权抢占CPU,若有则应进行 进程调度; 如果挂起是为了对换,则在激活被挂起的进程时还必须 将它调入内存。
53
2.4 进程同步
54
2.4 进程同步 进程同步问题的提出 进程同步目标 进程异步推进可能造成混乱 混乱可能导致不可再现 进程执行异步(断续) 维持进程并发性
2.4 进程同步 进程同步问题的提出 进程异步推进可能造成混乱 混乱可能导致不可再现 进程同步目标 进程执行异步(断续) 维持进程并发性 以提高系统效率 结果不可再现 资源的非封闭(共享) 进程间相互合作 进程同步 结果可再现 资源有效共享
55
2.4.1 进程同步的基本概念 1、两种形式的制约关系 间接相互制约关系(进程互斥):这种制约源于资源共享。
进程同步的基本概念 1、两种形式的制约关系 间接相互制约关系(进程互斥):这种制约源于资源共享。 直接相互制约关系(进程同步):这种制约源于进程间合作。 同 步 互 斥 进程-进程 进程-资源-进程 时间次序上受到某种限制 未竞争到物理资源的进程处于阻塞状态 相互清楚对方的存在及作用,交换信息 不一定清楚其他进程情况 往往指有几个进程共同完成一个任务 往往指多个任务多个进程间通讯制约 例:生产与消费之间,发送与接受之间,作者与读者之间,供者与用者之间 例:交通十字路口,单轨火车的拨道岔口
56
2、临界资源(Critical Resource)
临界资源:在一段时间内只允许一个进程访问的资源。临界资源的访问要求互斥的访问。 物理设备,如输入机、打印机、磁带机 软件资源,如公用变量、数据、表格、队列等 为了使多个进程有效地共享临界资源,并使程序的执行具有可再现性,系统必须保证它们互斥地使用临界资源。
57
… 3、生产者—消费者问题 并发 生产者进程 消费者进程 放产品 取产品 一次只能放一个产品; 一次只能取走一个产品;
缓冲区满则生产者不能放产品; 缓冲区空则消费者不能取产品;
58
3、生产者—消费者问题 1 2 3 4 5 … n-1 缓冲池 counter 生产者 消费者 in out
1 2 3 4 5 … n-1 消费者 in out 数据结构:int n; /*具有n个缓冲区的缓冲池*/ item buffer[n]; int in=0,out=0; /*指向缓冲区的指针*/ int counter=0; /*缓冲区中的消息数*/ /*生产者进程中的局部变量,用于暂时存放每次刚生产的消息*/ item nextp; /* 消费者进程中的局部变量,用于存放每次要消费的消息*/ item nextc;
59
3、生产者—消费者问题 register2 =counter; register2 =register2-1;
in out 1 2 3 4 5 6 7 3、生产者—消费者问题 void producer() { while(1) { produce an item in nextp … while (counter == n); buffer[in] = nextp; in = (in + 1) % n; counter++; } }; void consumer() { while (1) { while (counter==0); nextc = buffer[out]; out = (out+1) % n; counter--; consume the item in nextc; … } }; register2 =counter; register2 =register2-1; counter = register2; register1= counter; register1 = register1+1; counter = register1;
60
? 3、生产者—消费者问题 按什么顺序执行时,counter的值是6? 假设counter的当前值为5
register 1=counter; register 1=register+1; counter=register 1; register 2=counter; register 2 =register 2 -1; counter= register 2 ? 按什么顺序执行时,counter的值是6? register 1=counter; register 1=register 1+1; register 2=counter; register 2=register 2-1; counter=register 2; counter=register 1; register 1=5 register 1=6 register 2=5 register 2=4 counter=4 counter=6 为了解决以上问题,可将counter作为临界资源
61
4、临界区(critical section)
不论是硬件临界资源,还是软件临界资源,多个进程 必须互斥地对它进行访问。 临界区定义:每个进程中访问临界资源的那段代码; P1 P2 P3 临界区 临界区 临界区
62
将临界区正被访问的标志恢复为未被访问的标志
4、临界区(critical section) 用于对临界资源进行检查 while (TRUE) { entry section critical section exit section 将临界区正被访问的标志恢复为未被访问的标志 remainder section; }
63
5、同步机制应遵循的规则 空闲让进 (2) 忙则等待 (3) 有限等待 (4) 让权等待
64
2.4.2 硬件同步机制 利用特殊的硬件指令解决临界区问题 1、关中断 在进入锁测试之前关闭中断,直到完成锁测试并上锁之后才能打开中断。
硬件同步机制 利用特殊的硬件指令解决临界区问题 在进入锁测试之前关闭中断,直到完成锁测试并上锁之后才能打开中断。 互斥的保证:通过对锁测试和关锁操作连续性和完整性的保证 1、关中断 while (TRUE) { /* disable interrupts */ /* critical section */ /* enable interrupts */ /* remainder */ } 64
65
1、关中断 关中断方法的特点: 简单,高效 滥用关中断权力可能导致严重后果 关中断时间过长,会影响系统效率,限制CPU并发能力
66
2、利用Test-and-Set指令实现互斥
“测试并设置”(TS)指令 硬件指令TS是一条原语 lock=FALSE时表示该资源空闲 lock=TRUE时表示该资源正在被使用 /* TS指令功能描述 */ enter_region: TSL REGISTER, LOCK |copy lock to register and set lock 1 CMP REGISTER, # |was lock zero? JNE enter_region |if it was nonzero, lock was set, so loop RET |return to caller; critical region entered leave_region: MOVE LOCK, # |store a 0 in lock RET |return to caller /* 利用TS指令实现互斥 */ do{ … while TS(&lock); /*do skip*/ critical section; lock = FALSE; remainder section; } while(TRUE) /* TS指令的一般性描述 */ boolean TS(boolean *lock) { boolean old; old = *lock; *lock = TRUE; return old; }
67
3、利用Swap指令实现进程互斥 利用硬件指令实现进程互斥的缺点: /* XCHG指令功能描述 */ 存在“忙等”问题
enter_region: TSL REGISTER, # |put a 1 in the register XCHG REGISTER, LOCK |swap the contents of the register and lock variable CMP REGISTER, # |was lock zero? JNE enter_region |if it was non zero, lock was set, so loop RET |return to caller; critical region entered leave_region: MOVE LOCK, # |store a 0 in lock RET |return to caller /* 利用swap指令实现互斥 */ Do { key = TRUE do { swap(&lock, &key); } while (key != FALSE); critical section; lock = FALSE; remainder section; } while(TRUE) /* swap指令的一般性描述 */ void swap(boolean *a, boolean *b) { boolean temp; temp = *a; *a = *b; *b = temp; } 利用硬件指令实现进程互斥的缺点: 存在“忙等”问题 难于应用于复杂的进程同步问题
68
2.4.3 信号量机制 信号量机制的基本概念 信号量(Semaphores) 信号量机制是进程同步工具 信号量是对具体物理资源的抽象
信号量机制 信号量机制的基本概念 信号量(Semaphores) 信号量机制是进程同步工具 信号量是对具体物理资源的抽象 不同类的资源用不同名称的信号量代表 同类资源的个数用> 0的信号量值表示 信号量值为 0 或 1 的信号量表示临界资源
69
1、整型信号量 信号量:Dijkstra把整型信号量定义为一个整型量S。
特性:除初始化外,仅能通过两个标准的原子操作 wait(s)(P操作)和signal(s)(V操作)来访问。描述 如下: wait(S){ while (S≤0); /*do no-op*/ S--; } signal(S){ S++; 存在问题:只要S<=0,wait操作就会不断地测试, 没能做到“让权等待”
70
2、记录型信号量 引入进程阻塞机制,解决了“忙等”问题。 在信号量里增加对阻塞进程的记录
在信号量机制中,除了需要一个用于代表资源数目的整型 变量value外,还增加一个等待队列指针L,记录型的数据 结构描述如下: typedef struct { int value; /*代表资源数目*/ struct process_control_block *list;/*等待进程链表*/ } semaphore;
71
2、记录型信号量 wait(S)和signal(S)操作描述: wait(semaphore *S) { S->value--;
if (S->value < 0) block(S->list); } signal(semaphore *S) S->value++; if (S->value <= 0) wakeup(S->list);
72
2、记录型信号量 signal( S ) :归还一个单位的资源 wait( S ): 申请一个单位的资源 S->value++
将S->list中 第一个进程 唤醒, Y 本进程进入 S->list队列, 进入阻塞状态 Y N 本进程获得 一个资源 N 唤醒后的进程从这里开始 临界区/资源访问区
73
2、记录型信号量—举例 semaphore mutex; mutex.value=3; Printer(){ while (TRUE) {
wait(mutex); print the document on the paper; signal(mutex); } P1 P2 P3 2 1 -1 mutex P4 阻塞 唤醒 P1
74
2、记录型信号量 记录型信号量机制特点: S.value的初值为1表示什么呢? S.value的含义 是否遵循让权等待?
大于0 等于0 小于0 是否遵循让权等待? 采用阻塞队列,阻塞机制 主动阻塞与被动唤醒 进程对资源访问的过程: 原语保证 wait(S),signal(S)操作都是原语 保证不出现“锁不住”资源的现象 S.value的初值为1表示什么呢? ......wait( S ) 进入临界区 signal( S )...... S<0时进程会进入阻塞状态
75
3、AND型信号量 wait1(S1); S1 wait1(S2); S2 wait2(S2); …… signal(S2);
进程1 wait1(S1); S1 进程2 wait1(S2); S2 wait2(S2); …… signal(S2); signal(S1); wait2(S1); …… signal(S1); signal(S2); 系统推进过程为 wait1(S1); wait2(S2); wait2(S1); wait1(S2); 进程1和进程2都无法继续推进,两个进程进入死锁状态 进程2 阻塞 进程1 阻塞
76
3、AND型信号量 基本思想 将多次对多个信号量的申请改为一次,用一个原子操作完成 进程要么一次获得所有的资源,要么一个也申请不到
不会存在互相等待的局面 AND型信号量 Swait(S1,S2,S3……Sn)
77
3、AND型信号量 Swait(Simultaneous wait)操作描述: Swait(S1, S2, … ,Sn) {
while(TRUE) { if (S1>=1 && … && Sn>=1){ for (i=1; i<=n;i++) Si--; break; } else { place the process in the waiting queue associated with the first Si found with Si<1, and set the program count of this process to the beginning of Swait operation
78
3、AND型信号量 Ssignal(Simultaneous signal)操作描述: Ssignal(S1, S2, … ,Sn)
while(TRUE) { for (i=1; i<=n;i++) { Si++; remove all the process waiting in the queue associated with Si into the ready queue }
79
3、AND型信号量 AND型信号量流程 Swait(S1,S2,S3……Sn) N S1>=1? N S2>=1? 。。。
将正在执行的进程移入第一个满足Si<1的阻塞队列中; 并将进程的程序指针设至Swait操作开始处 S1=S1-1; S2=S2-1; …… Sn=Sn-1;
80
3、AND型信号量 AND型信号量流程 Ssignal(S1,S2,S3……Sn) S1=S1+1; S2=S2+1; ……
Sn=Sn+1; 将Si队列中所有等待进程 唤醒,移至就绪队列中
81
3、AND型信号量 AND型信号量流程 进程1 进程2 wait1(S1); wait2(S2); Swait1(S1,S2)
系统推进 Swait1(S1,S2) Swait2(S1,S2) 或 Swait2(S1,S2) Swait1(S1,S2)
82
3、AND型信号量—应用 A B C 3 2 2 到达顺序:P1,P2,P3,P4
P1到达,执行Swait(S1,S2,S3)操作 2 1 1 P2到达,执行Swait(S1,S2)操作 1 1 P3到达,执行Swait(S1, S3)操作 阻塞 P4到达,执行Swait(S2,S3)操作 P4会排在S2所对应的等待队列中 P1执行完成,执行Ssignal(S1,S2,S3)操作 1 1 1 P4会被唤醒,执行Swait(S2,S3)操作 1
83
4、信号量集 Swait(S1,t1,d1,……Sn,tn,dn) 引入原因: 基本思想: Si:各信号量 ti:申请下限
① 一次需要N个某类资源时,需进行N次wait(S)操作,这是低效的。 ② 某些情况下,当资源数量低于某一下限值时,便不予分配。 基本思想: Si:各信号量 ti:申请下限 ti > 0时,可进行资源预留 di:申请个数 一次可申请一种资源的多个
84
4、信号量集 信号量集流程 Swait(S1,t1,d1……Sn ,tn,dn ) N S1>=t1? N S2>=t2? ……
1)将正在执行的进程移入第一个满足Si<ti的阻塞队列中; 2)将程序指针指向该进程中Swait操作开始处 S1=S1-d1; S2=S2-d2; …… Sn=Sn-dn;
85
4、信号量集 信号量集流程 Ssignal(S1,d1……Sn ,dn ) S1=S1+d1; S2=S2+d2; …… Sn=Sn+dn;
中的所有进程移到就绪 队列
86
4、信号量集—应用 A B C 61 52 41 到达顺序:P1,P2,P3,P4
四个进程对应的代码段为: P2(){ Swait(S1,1,2,S2,2,2,S3,1,1); use the critical resource; Ssignal(S1,2,S2,2,S3,1); } semaphore S1,S2,S3 S1.value=6; S2.value=5; S3.value=4; P1(){ Swait(S1,1,3,S2,2,2,S3,1,1); use the critical resource; Ssignal(S1,3,S2,2,S3,1); } P3(){ Swait(S1,1,1,S2,2,1,S3,1,1); use the critical resource; Ssignal(S1,1,S2,1,S3,1); }
87
4、信号量集—应用 A B C 61 52 41 到达顺序:P1,P2,P3,P4
Swait(S1,1,1,S2,2,1,S3,1,1); use the critical resource; Ssignal(S1,1,S2,2,S3,1); } P1到达 3 3 3 P2到达 1 1 2 阻塞 P3到达, S2<t2 阻塞 P4到达, S2<t2 P1执行完成,释放资源 4 3 3 P3 P4都被唤醒 3 2 2 P3 申请到资源 P4 申请到资源 2 1 1
88
4、信号量集—特殊情况 (1) Swait(S, d, d)。 此时在信号量集中只有一个信号量S, 但允许它每次申请d个资源,当现有资源数少于d时,不予分配。 (2) Swait(S, 1, 1)。 此时的信号量集已蜕化为一般的记录 型信号量(S>1时)或互斥信号量(S=1时)。 (3) Swait(S, 1, 0)。这是一种很特殊且很有用的信号量操作。 当S≥1时,允许多个进程进入某特定区;当S变为0后,将阻止 任何进程进入特定区。换言之,它相当于一个可控开关。 S=2 Swait(S,1,0) S=0 Swait(S,1,0) P1 … P3 P2 Pn 临界区 临界区 P1 P2 P3 … Pn
89
2.4.4 信号量的应用 1、利用信号量实现进程互斥 状态: 状态: 信号量初值为1 进程1 进程2 wait(s) … … wait(s)
信号量的应用 1、利用信号量实现进程互斥 对竞争资源的互斥访问过程 信号量初值为1 进程1 进程2 wait(s) … … wait(s) wait(s) 访问资源 临界区 临界区 signal(s) signal(s) signal(s) … … 唤醒 状态: 就绪 执行 状态: 就绪 执行 阻塞
90
1、利用信号量实现进程互斥 wait(mutex)和signal(mutex)必须成对出现 利用信号量实现进程互斥的进程可描述如下:
semaphore mutex=1; PA(){ while(1) { wait(mutex); critical section signal(mutex); remainder section } PB(){ while(1) { wait(mutex); critical section signal(mutex); remainder section } wait(mutex)和signal(mutex)必须成对出现
91
2、利用信号量实现前趋关系 信号量初值为0 司机进程 售票员进程 正常行车 售票 前驱 后继 到站停车 同步点 开车门 喝茶 关车门 同步点
保证进程间的前驱、后继关系 信号量初值为0 司机进程 售票员进程 正常行车 售票 前驱 后继 到站停车 wait(停车) 同步点 signal(停车) 开车门 wait(s) signal(s) 喝茶 关车门 wait(关车门) signal(关车门) 同步点 正常行车 售票
92
2、利用信号量实现前趋关系 Pi代表进程; Si代表Pi中的语句;s为具有前驱关系的两进程的公用信号量,其初值为0。
p1(){S1; signal(s);} p2(){wait(s); S2;} main() { semaphore s; s.value=0; cobegin p1();p2(); coend } s S2 S1 p1 p2
93
2、利用信号量实现前趋关系 S1 S2 S5 S4 S3 S6 p1(){S1;signal(a);signal(b);}
p2(){wait(a);S2;signal(c);signal(d);} p3(){wait(c);S3;signal(e);} p4(){wait(d);S4;signal(f);} p5(){wait(b);S5;signal(g);} p6(){wait(e);wait(f);wait(g);S6;} main() { semaphore a,b,c,d,e,f,g; a.value=b.value=c.value=d.value=e.value=0; f.value=g.value=0; cobegin p1();p2();p3();p4();p5();p6(); coend } S1 a b S2 c d S5 S4 S3 e f g S6
94
管程机制 信号量同步的缺点 同步操作分散:信号量机制中,同步操作分散在各 个进程中,使用不当就可能导致各进程死锁(如P、 V操作的次序错误、重复或遗漏) 易读性差:要了解对于一组共享变量及信号量的操 作是否正确,必须通读整个系统或者并发程序 不利于修改和维护:各模块的独立性差,任一组变 量或一段代码的修改都可能影响全局 正确性难以保证:操作系统或并发程序通常很大, 很难保证这样一个复杂的系统没有逻辑错误
95
1、管程(Monitors)的定义 管程:代表共享资源的数据结构,以及由对该 共享数据结构实施操作的一组过程所组成的资 源管理程序,共同构成了一个操作系统的资源 管理模块。 管程的组成: 管程的名称 局部于管程内部的共享数据结构说明; 对该数据结构进行操作的一组过程; 对局部于管程内部的共享数据设置初始值的语句。 Hansen
96
1、管程的定义 进入队列 条件(不忙)队列 共享数据 … 一组操作过程 初始化代码 图 2-13 管程的示意图 96
97
1、管程的定义 管程的语法: monitor monitor_name { /*管程名*/
share variable declarations; /*共享变量说明*/ cond declarations; /*条件变量说明*/ public: /*能被进程调用的过程*/ void P1(……) /*对数据结构操作系统的过程*/ {……} void P2(……) …… void Pn(……) { /*管程主体*/ initialization code; /*初始代代码*/ } 97
98
1、管程的定义 管程的特性(从OOP): 局部性:管程内的数据结构只能被局部于管程内的过程所访问;局部于管程内的过程只能访问管程内的数据结构; 访问接口:任何进程只能通过调用管程提供的过程入口进入管程 互斥性:任一时刻,最多只能有一个进程在管程中执行 注:保证进程互斥地进入管程是由编译器负责的。即管程是一 种编程语言的构件,它的实现需要得到编译器的支持。
99
1、管程的定义 管程的特性(从语言角度看): 模块化:管程是一个基本程序单位,可以单独编译。
抽象数据类型:管程中不仅有数据,而且有对数据的操作。 信息隐蔽:管程中的数据结构只能被管程中的过程访问,这些过程也是在管程内部定义的,供管程外的进程调用,而管程中的数据结构以及过程(函数)的具体实现外部不可见。
100
1、管程的定义 管程和进程的区别: 定义的数据结构:进程定义的是私有数据结构PCB;管程定义的是公共数据结构;
数据结构上的操作:进程由顺序程序执行有关操作;管程主要进行同步操作和初始化操作; 设置的目的:设置进程的目的在于实现系统的并发性;管程的设置则是解决共享资源的互斥使用问题; 工作方式:进程通过调用管程中的过程对共享数据结构实行操作,因此进程是主动工作方式,管程是被动工作方式; 并发性:进程之间能并发执行,而管程则不能与其调用者并发; 进程具有动态性,由创建而生,由撤消而亡;而管程是操作系统的一个资源管理模块,供进程调用。
101
2、条件变量 条件变量:是局部于管程的变量,用于区分不同的 阻塞原因 格式: condition x, y;
条件变量是一种抽象数据类型,每个条件变量保存 了一个链表,记录因该条件而阻塞的所有进程。 条件变量的操作: x.wait操作:将执行进程挂到与条件变量x相应的等待队列 上,并释放管程,如果没有等待进程,x.signal不起任何 作用。 x.signal操作:唤醒与x相应的等待队列上的一个进程,如 果存在多个这样的进程,则选择其中的一个,但如果没有 被阻塞的进程,则x.signal操作不产生任何后果。 101
102
2.5 经典进程的同步问题
103
2.5.1 生产者—消费者问题 问题描述: 有多个生产者在生产消息 有多个消费者在消费消息 生产者与消费者共享一个有界缓冲池
消费者消费的是生产者生产的消息
104
消息缓冲池 生产者 消费者 消息缓冲池 空白块缓冲池 生产者产生的消息放入缓冲池内; 消费者从缓冲池内取走消息消费;
消费者消费后的空白消息块放进空白缓冲池内供生产者使用。 生产者 消费者 1 2 3 1 2 消息缓冲池 空白块缓冲池
105
算法分析 两类进程:生产者进程和消费者进程 (1)进程间的关系 后继进程: 消费者: wait(full) wait(s) 前驱进程:
生产者生产消息后消费者消费 消费者消费后的空白缓冲块由生产者生产消息 后继进程: 消费者: wait(full) wait(s) 前驱进程: 生产者: signal(full) signal(s) 后继进程: wait(empty) wait(s) 生产者: 前驱进程: 消费者: signal(empty) signal(s)
106
(2)队列的操作 生产者 生产者 放入消息 取空白块 消息缓冲池 空白块缓冲池 取走消息 放空白块 消费者 消费者 两个共享队列:
消息缓冲队列 空白缓冲队列 多个进程共享一个队列 是否需要保护 生产者 生产者 放入消息 取空白块 消息缓冲池 空白块缓冲池 取走消息 放空白块 消费者 消费者
107
进程间的关系 信号量 生产者生产消息 后 消费者消费的合作关系 消费者消费 后 的空白缓冲块由生产者生产消息的合作关系
进程间在队列操作上的互斥关系 信号量 full:表示缓冲池中满缓冲区数量 empty:表示缓冲池中空缓冲区数量 mutex:实现诸进程对缓冲池的互斥使用 进程1: wait(s) wait(mutex) 操作队列 访问资源 signal(mutex) signal(s) 进程2: wait(s) wait(mutex) 访问资源 操作队列 signal(mutex) signal(s)
108
1、利用记录型信号量解决生产者-消费者问题
算法流程 生产者 消费者 产生一个消息; 申请一个消息 申请一个空白缓冲区; 申请缓冲区操作的互斥; 申请缓冲区操作的互斥; 释放缓冲区操作的互斥; 从缓冲区中取走一个消息; 在空白缓冲区内填充消息 释放缓冲区操作的互斥 释放一个空白块信号 释放一个消息信号 消费消息
109
int in = 0, out = 0; item buffer[n]; semaphore mutex=1, empty=n, full=0; void producer() { do { produce an item nextp; … wait(empty); wait(mutex); buffer[in]=nextp; in=(in+1) % n; signal(mutex); signal(full); } while(TRUE); } void consumer () { do { wait(full); wait(mutex); nextc=buffer[out]; out= (out+1) % n; signal(mutex); signal(empty); consume the item in nextc; … }while(TRUE) } void main() { cobegin producer(); consumer(); coend
110
Discuss 为什么不是对称的? 生产者做:wait(empty)、 signal(full)
消费者做:wait(full)、signal(empty) wait操作的顺序重要吗? 死锁的产生! signal操作的顺序重要吗? 不重要,但它可能会影响调度效率 wait(mutex); wait(empty); buffer[in]=nextp; in=(in+1) % n; signal(mutex); signal(full);
111
生产者—消费者算法小结 小结: 在分析进程同步问题中,首先弄清楚主体,谁是生产者,谁是消费者
逐个分析进程间的关系是关键,不管多复杂的关系,总能归结为两种基本关系(竞争与合作),总是这两种关系的组合 根据分析的进程间关系,确定信号量及其初值 分析各进程的工作流程 根据工作流程编写代码 进行信号量平衡检查:不管是竞争还是合作,信号量的使用必须成对出现
112
2、利用AND信号量解决生产者-消费者问题
int in = 0, out = 0; item buffer[n]; semaphore mutex=1, empty=n, full=0; void producer() { do { producer an item nextp; … wait(empty); wait(mutex); buffer[in]=nextp; in=(in+1) % n; signal(mutex); signal(full); } while(TRUE); } void consumer () { do { wait(full); wait(mutex); nextc=buffer[out]; out= (out+1) % n; signal(mutex); signal(empty); consumer the item in nextc; … }while(TRUE) } Swait(full, mutex); Swait(empty, mutex); Ssignal(mutex, empty); Ssignal(mutex, full);
113
3、利用管程解决生产者-消费者问题 建立一个管程,并命名为producer-consumer, 或简 称为PC。其中包括两个局部过程:
put(x)过程:生产者将产品投放到缓冲池中; get(x)过程:消费者从缓冲池中取出产品。 其他变量: 整型变量count:表示在缓冲池中已有的产品数目; 条件变量及其上的操作: 条件变量notfull:对应于缓冲池不全满; 条件变量notempty:对应于缓冲池不全空。 cwait(condition)过程:当管程被占用时执行,调用者 阻塞,并挂在条件condition的队列上 csignal(condition)过程:唤醒阻塞在条件condition队列 上的进程 113
114
monitor producer-consumer { int in, out, count; item buffer[N];
condition notfull, notempty; public: void put(item x) { if (count>=N) cwait(notfull); buffer[in]= x; in=(in+1) % N; count++; csignal(notempty); } /定义管程 共享局部变量说明 条件变量 /将产品投放到缓冲池中的过程 114
115
if (count<=0) cwait(notempty); x=buffer[out]; out=(out+1) % N;
void get(item x) { if (count<=0) cwait(notempty); x=buffer[out]; out=(out+1) % N; count--; csignal(notfull); } {in=0; out=0; count=0;} }PC; /负责从缓冲池中取出产品的过程 初始化代码 115
116
在利用管程解决生产者-消费者问题时, 其中的生产者和消费者可描述为:
void producer() { item x; while(TRUE) { … produce an item in x; PC.put(x); } void consumer() { item x; while(TRUE) { PC.get(x); consume the item in x; … } void main() { cobegin producer(); consumer(); coend 116
117
2.5.2 哲学家进餐问题 问题描述 准备进餐 准备进餐 思考 思考 五位哲学家围桌而坐 桌上有5个碗和5只筷子 进餐
哲学家进餐问题 问题描述 五位哲学家围桌而坐 桌上有5个碗和5只筷子 哲学家在思考问题时不需要任何资源,思考完问题后进入进餐态。 每人必须获得左右两支筷子才能进餐 准备进餐 思考 思考 进餐 进餐 准备进餐 思考 思考 思考
118
1、利用记录型信号量解决哲学家进餐问题 经分析可知,放在桌子上的筷子是临界资源,在一段时间内只允许一位哲学家使用。为了实现对筷子的互斥使用,可以用一个信号量表示一根筷子,由这五个信号量构成信号量数组。其描述如下: semaphore chopstick[5] = {1, 1, 1, 1, 1};
119
所有信号量均被初始化为1, 第i位哲学家的活动可描述为: do {
wait(chopstick[i]); //拿左边的筷子 wait(chopstick[(i+1) % 5]); //拿右边的筷子 … eat; signal(chopstick[i]); //放下左边的筷子 signal(chopstick[(i+1) % 5]); //放下右边的筷子 … think; }while[TRUE];
120
死锁! 哲学家问题分析 准备进餐 准备进餐 准备进餐 准备进餐 思考 申请左边的筷子 申请右边的筷子 进餐 释放左边的筷子 准备进餐
释放右边的筷子
121
可采取以下几种解决方法: (1) 至多只允许有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐,并在用毕时能释放出他用过的两根筷子,从而使更多的哲学家能够进餐。 (2) 仅当哲学家的左、右两根筷子均可用时,才允许他拿起筷子进餐。 (3) 规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子;而偶数号哲学家则相反。按此规定,将是1、 2号哲学家竞争1号筷子;3、4号哲学家竞争3号筷子。即五位哲学家都先竞争奇数号筷子,获得后,再去竞争偶数号筷子,最后总会有一位哲学家能获得两根筷子而进餐。
122
2、利用AND信号量机制解决哲学家进餐问题
在哲学家进餐问题中,要求每个哲学家先获得两个临界资源(筷子)后方能进餐,这在本质上就是前面所介绍的AND同步问题,故用AND信号量机制可获得最简洁的解法。 semaphore chopstick[5] = {1, 1, 1, 1, 1}; do { … think; Swait(chopstick[(i+1) % 5], chopstick[i]);//同时申请右边和左边的筷子 … eat; Ssignal(chopstick[(i+1) % 5], chopstick[i]); //用完餐后同时释放右边和左 边的筷子 }while(TRUE);
123
问题 如果哲学家有K只手呢?
124
2.5.3 读者—写者问题 问题描述 一个数据记录,有多个进程进行读操作,另一 些进程进行修改操作 读写策略 (Reader)
读者—写者问题 问题描述 一个数据记录,有多个进程进行读操作,另一 些进程进行修改操作 (Reader) (Writer) 读写策略 允许多个进程同时进行读操作——读不互斥 不允许多于一个进程进行写操作——写互斥 不允许读写操作同时进行——读写互斥
125
读者-写者问题分析 无进程 关掉信号灯,其他所有进程都无法进入 写者进程到达 有写者 自我阻塞 有读者 自我阻塞 写者进程离开
有写者 自我阻塞 有读者 自我阻塞 写者进程离开 打开信号灯,其他所有进程都可以进入 无进程 是第一位读者,需关掉写者信号灯, 不是第一位,改读者数目后,进行阅读操作 读者进程到达 有写者 自我阻塞 有读者 互斥地修改读者数目,进行阅读操作 读者进程离开 互斥地修改读者数目,若是最后一位离开的读者,应该打开写信号灯
126
1、利用记录型信号量解决读者-写者问题 wmutex:表示写进程与其它进程(读、写)互斥地访问数 据集,初值为1
Readcount:记录当前正在读数据集的读进程数目,初值为0。 仅当Readcount=0, 表示尚无Reader进程在读时,Reader 进程才需要执行Wait(wmutex)操作; 若wait(Wmutex)操作成功,Reader进程便可去读,相应 地,做Readcount+1操作; 仅当Reader进程在执行了Readcount减1操作后其值为0时, 才须执行signal(Wmutex)操作,以便让Writer进程写; rmutex:表示读进程互斥地访问共享变量Readcount,初值 为1
127
读者-写者问题可描述如下: semaphore rmutex=1, wmutex=1; int readcount=0; void reader() { do { wait(rmutex); if (readcount==0) wait(wmutex); readcount++; signal(rmutex); … perform read operation; readcount--; if (readcount==0) signal(wmutex); signal(rmutex); }while(TRUE); } void writer() { do { wait(wmutex); … perform write operation; signal(wmutex); }while(TRUE); } void main() { cobegin reader(); writer(); coend }
128
控制读者数目:当L>0时允许读者进入读,当L=0时,不允许读者进入读
2、利用信号量集机制解决读者-写者问题 仅当writer进程在写(mx=1),又无reader进程在读(L=RN)时,writer进程才能进入临界区 void writer() { do { Swait(mx,1,1; L,RN,0); perform write operation; Ssignal(mx,1); }while(TRUE); } void main() { cobegin reader(); writer(); coend int RN; // 最多允许同时读的读者数 Semaphore L=RN, mx=1; void reader() { do { Swait(L,1,1); Swait(mx,1,0); … perform read operation; Ssignal(L,1); }while(TRUE); } 控制读者数目:当L>0时允许读者进入读,当L=0时,不允许读者进入读 可控开关:当mx>=1时,允许多个读者进入读,当mx=0时,阻止任何读者进入读 释放一个mx资源 释放一个L资源
129
问题 思考下列序列的操作 R1、R2、W1、R3 Reader:wait(rmutex); Writer:wait(wmutex);
if (readcount==0) wait(wmutex); readcount++; signal(rmutex); Writer:wait(wmutex); perform write operation; signal(wmutex); 最初:rmutex=1,wmutex=1,Readcount=0 R1到达:rmutex=0,wmutex=0,Readcount=1, rmutex=1 R2到达:rmutex=0,Readcount=2, rmutex=1 W1到达:因为wmutex=0,阻塞 R3到达,会怎么样呢?
130
问题 读优先解决方案 存在什么问题,如何解决? 写优先解决方案 课本上介绍的是什么解决方案?还有其他解 决方案吗? 如何进行改造
131
2.5.4 嗜睡的理发师问题 问题描述 一个理发店由一个有N张沙发的等候室和一个放有一张理发椅的理发室组成;
嗜睡的理发师问题 问题描述 一个理发店由一个有N张沙发的等候室和一个放有一张理发椅的理发室组成; 没有顾客要理发时,理发师便去睡觉 当一个顾客走进理发店时 如果所有的沙发都已被占用,他便离开理发店; 否则,如果理发师正在为其他顾客理发,则该顾客就找一张空沙发坐下等待; 如果理发师因无顾客正在睡觉,则由新到的顾客唤醒理发师为其理发。 理发完成后,顾客必须付费,直到理发师收费后才能离开理发店
132
嗜睡的理发师问题分析 顾客与理发师的同步关系1(理发关系):只有当理发椅上有顾客时,理发师才可以开始理发,否则他必须等待。引入信号量full。 顾客与理发师的同步关系2(付费关系):顾客理完发后必须向理发师付费,并等理发师收费后顾客才能离开;理发师则需要等待顾客付费,并在收费后唤醒顾客以允许他离开。引入信号量payment和receipt来控制。 顾客间的同步关系:上一个顾客离开理发椅,下一个顾客才能坐到理发椅上,否则顾客必须等待。引入信号量empty。
133
嗜睡的理发师问题分析 顾客间的互斥关系1(临界资源:沙发):等候室中的N张沙发是顾客进程竞争的资源。引入信号量sofa。
顾客间的互斥关系2(对沙发队列的访问与修改):为控制顾客的人数,使顾客能在所有的沙发都被占用时离开理发店,需设置一个整型变量count来对理发店中的顾客进行计数,该变量将被多个顾客进程互斥地访问并修改。引入信号量mutex。
134
嗜睡的理发师问题算法描述 int count=0; //对理发店中的顾客进行计数
semaphore mutex=1; //实现顾客进程对count变量的互斥访问 semaphore sofa=N; //对应于等候室中N张沙发 semaphore empty=1; //表示是否有空闲的理发椅 semaphore full=0; //表示理发椅上是否有等待理发的顾客 semaphore payment=0; //用来等待付费 semaphore receipt=0; //用来等待收费
135
G1; //如果所有的沙发都已被占用,他便离开理发店 else { count ++ signal(mutex);
void guest() { do { }while(TRUE) } void barber() { do { } while(TRUE) signal(mutex); 离开理发店; } wait(mutex); if (count>N) { G1; //如果所有的沙发都已被占用,他便离开理发店 else { count ++ signal(mutex); if (count>1) { G2;//如果理发师正在为其他顾客理发,则该顾客就找一张空沙发坐下等待 else { /*count=1*/ wait(empty); //在理发椅上就座; } G3;//顾客接受理发,结束后付费离开 wait(sofa); 在沙发中就座; wait(empty); 从沙发上起来; signal(sofa); } signal(full); 理发; 付费; signal(payment); wait(receipt); 从理发椅上起来; signal(empty); wait(mutex); count --; signal(mutex); 离开理发店; wait(full); 替顾客理发; wait(payment); 收费; signal(reciept); B1;//理发师为顾客理发,结束后收费
136
思考题—苹果桔子问题 桌上有一个只能放入一个水果的盘子,爸爸专向盘子中放苹果(apple),妈妈专向盘子中放桔子(orange),儿子专等吃盘子中的桔子,女儿专等吃盘子里的苹果 。 如果有两个家庭的爸爸、妈妈、儿子、女儿和二只盘子呢?这时候应该如何改写代码办?
137
2.6 进程通信
138
定义:进程通信指进程之间的信息交换 类型: 低级进程通信:进程之间控制信息的交换,一般只传送一个和几个字节的信息,达到控制进程执行速度的作用
用途:进程的同步与互斥 交换的信息量少、通信效率低; 通信对用户不透明 高级通信:用户可以直接利用操作系统所提供的一组通信命令,高效地传送大量数据的一种通信方式。 用途:进程间传送大量数据 高效、大量数据传递; 通信过程对用户透明
139
2.6.1 进程通信的类型 1、共享存储器系统(Shared-Memory System) 高级通信机制分类: 共享存储器系统 管道通信系统
进程通信的类型 高级通信机制分类: 共享存储器系统 管道通信系统 消息传递系统 客户机-服务器系统 相互通讯的进程通过共享数据结构和存储区进行通讯 基于共享数据结构的通信方式:诸进程共享某些数据结构 通信效率低下,只适于传递相对少量的数据,属于低级通信 基于共享存储区的通信方式:为了传送大量数据,在存储区中划出一块共享存储区,诸进程可通过对共享存储区进行读或写数据实现通讯 可传输大量数据,属于高级通信 1、共享存储器系统(Shared-Memory System)
140
2、管道(Pipe)通信系统 所谓“管道”,是指连接二个进程的一个打开的共享文件,又名pipe文件。
发送进程以字符流形式将大量的数据送入管道; 接收进程则在需要时从管道中读数据; 管道机制必须提供三种方面的协调能力: (1)互斥访问 (2)写后读,读后写的同步 (3)只有在管道双方都存在时才能通信 进程1写入 进程2读出 写入 进程3读出
141
3、消息传递系统(Message passing system)
程序员直接利用系统提供的一组通信命令(原语)进行通信 按实现方式分类: 直接通信方式——消息缓冲 采用进程的消息缓冲队列 消息发送者将消息直接放在接收者的消息缓冲队列 间接通信方式 利用中间者——信箱、邮局来传递信件 发送进程将消息发送到信箱中,接收进程从信箱中取出消息 3、消息传递系统(Message passing system)
142
4、客户机-服务器系统(Client-Server system)
1)套接字(Socket) 套接字是网络通信接口,用于解决多对进程同时通信时端口和物理线路的多路复用问题 套接字是通信标识类的数据结构,包含通信目的地址、端口号、传输层协议、进程所在网络地址、API函数等 套接字包括两类 基于文件型:通信进程运行在同一台机器上,一个套接字关联到一个特殊的文件 基于网络型:通常采用非对称方式通信(发送者需要提供接收者命名),通信进程运行在不同主机网络环境下 套接字的优势:适用于单机与网络环境;便于实现数据传输的并发服务;通过统一接口隐藏了通信设施及实现细节 142
143
4、客户机-服务器系统(Client-Server system)
2)远程过程调用(RPC)和远程方法调用 RPC:是一个用于通过网络连接的系统的通信协议,它允许运行于一台主机(本地)系统上的进程调用另一台主机(远程)系统上的进程。 远程方法调用:RPC涉及的软件采用面向对象编程,则将这种RPC称之为远程方法调用。 远程过程调用的进程: 本地客户进程:也称为网络守护进程,拥有一个客户存根 远程服务器进程:也称为网络守护进程,关联着一个服务器存根 网络守护进程负责在网络间的消息传递,一般这两个进程都是处理阻塞状态,等待消息 143
144
4、客户机-服务器系统(Client-Server system)
2)远程过程调用(RPC)和远程方法调用 RPC的主要步骤 调用客户端句柄;执行传送参数 调用本地系统内核发送网络消息 消息传送到远程主机 服务器句柄得到消息并取得参数 执行远程过程 执行的过程将结果返回服务器句柄 服务器句柄返回结果,调用远程系统内核 消息传回本地主机 客户句柄由内核接收消息 客户接收句柄返回的数据 1、本地过程调用者以一般方式调用远程过程在本地关联的客户存根,传递相应的参数,然后将控制权转移给客户存根; 2、客户存根执行,完成包括过程名和调用参数等信息的消息建立,将控制权转移给本地客户进程; 3、本地客户进程完成与服务器的消息传递,将消息发送到远程服务器进程; 4、远程服务器进程接收消息后转入执行,并根据其中的远程过程名找到对应的服务器存根,将消息转给该存根; 5、该服务器存根接到消息后,由阻塞状态转入执行状态,拆开消息从中取出过程调用的参数,然后以一般方式调用服务器上关联的过程; 6、在服务器端的远程过程运行完毕后,将结果返回给与之关联的服务器存根; 7、该服务器存根获得控制权运行,将结果打包为消息,并将控制权转移给远程服务器进程; 8、远程服务器进程将消息发送回客户端; 9、本地客户进程接收到消息后,根据其中的过程名将消息存入关联的客户存根,再将控制权转移给客户存根; 10、客户存根从消息中取出结果,返回给本地调用者进程,并完成控制权的转移。 144
145
2.6.2 消息传递通信的实现方法 1、直接消息传递系统 是指发送进程利用OS所提供的发送命令,直接把消息发送给目标进程; 直接通信原语:
消息传递通信的实现方法 1、直接消息传递系统 是指发送进程利用OS所提供的发送命令,直接把消息发送给目标进程; 直接通信原语: 对称寻址方式:要求发送进程和接收进程都以显式方式提供对方的标识符 send(receiver, message); 发送一个消息给接收进程; receive(sender, message); 接收Sender发来的消息; 非对称寻址方式:接收进程与多个发送进程的通信 send(P, message); 发送一个消息给接收进程; receive (id, message);接收来自任何进程的消息;
146
消息正文:则是发送进程实际上所发送的数据
1、直接消息传递系统 消息的格式 消息头:消息在传输时所需的控制信息,如源进程名、目标进程名、消息长度、消息类型、消息编号及发送的日期和时间; 消息 消息正文:则是发送进程实际上所发送的数据 定长消息格式: 比较短,减少了系统对消息的处理和存储开销; 变长消息格式: 进程所发送消息的长度是可变的; 系统在处理和存储变长消息时,须付出更多的开销
147
1、直接消息传递系统 进程同步方式 发送进程阻塞、 接收进程阻塞。 (2) 发送进程不阻塞、 接收进程阻塞。
用于进程间紧密同步 发送进程和接收进程间无缓冲 闲时阻塞 这种同步方式称为汇合 (2) 发送进程不阻塞、 接收进程阻塞。 是应用最广的进程同步方式 如:服务器上的服务进程 (3) 发送进程和接收进程均不阻塞。 如:生产者-消费者间的关系
148
1、直接消息传递系统 通信链路(communication link) 建立通信链路的方式:
由发送进程在通信之前,用显式的“建立连接”命令 (原语)请求系统为之建立一条通信链路;在链路使用 完后,也用显式方式拆除链路。(主要用于计算机网 络中) 发送进程无须明确提出建立链路的请求,只须利用系 统提供的发送命令(原语),系统会自动地为之建立一 条链路。(主要用于单机系统中)
149
1、直接消息传递系统 通信过程描述: 用直接通信原语解决生产者- 消费者问题
repeat … produce an item in nextp; send(consumer, nextp);until false; receive(producer, nextc); consume the item in nextc;until false; 用直接通信原语解决生产者- 消费者问题 当生产者生产出一个产品(消息)后,便用Send原语将消息发送给消费者进程; 消费者进程则利用Receive原语来得到一个消息; 如果消息尚未生产出来,消费者必须等待,直至生产者进程将消息发送过来。
150
2、信箱通信 指进程之间的通信,需要通过作为共享数据结构的实体。 信箱用来暂存发送进程发送给目标进程的消息; 接收进程从邮箱中取出消息;
只有核准用户才能读取邮箱中的消息; 既可实现实时通信,又可实现非实时通信。 A D B E C
151
2、信箱通信 信箱结构 (1) 信箱头 (2) 信箱体 存放信箱的描述信息:信息标识符、信息拥有者、信箱 口令、信箱空格数;
由若干个可以存放消息的信箱格组成,信箱格的数目与 每格的大小是在创建信箱时确定的。
152
2、信箱通信 信箱通信原语 (1) 信箱的创建和撤消。 (2) 消息的发送和接收。 信箱名字、信箱属性(公用、私用或共享);
对于共享信箱,还应给出共享者的名字。 (2) 消息的发送和接收。 Send(mailbox, message); //将一个消息发送到指定信箱; Receive(mailbox, message); //从指定信箱中接收一个消息;
153
2、信箱通信 信箱可由操作系统创建,也可由用户进程创建,创建者是信箱的拥有者。据此,可把信箱分为以下三类。
1) 私用信箱:由用户进程创建,采用单向通信链路 2) 公用信箱:由操作系统创建,采用双向通信链路 3) 共享信箱:由用户进程创建
154
2、信箱通信 在利用信箱通信时,在发送进程和接收进程之间,存在以下四种关系:
(1) 一对一关系。这时可为发送进程和接收进程建立一条两者专用的通信链路,使两者之间的交互不受其他进程的干扰。 (2) 多对一关系。允许提供服务的进程与多个用户进程之间进行交互,也称为客户/服务器交互(client/server interaction)。 (3) 一对多关系。允许一个发送进程与多个接收进程进行交互,使发送进程可用广播方式,向接收者(多个)发送消息。 (4) 多对多关系。允许建立一个公用信箱,让多个进程都能向信箱中投递消息;也可从信箱中取走属于自己的消息。
155
2.6.3 直接消息传递系统实例 1、消息缓冲队列通信机制中的数据结构 (1) 消息缓冲区。描述如下:
直接消息传递系统实例 1、消息缓冲队列通信机制中的数据结构 (1) 消息缓冲区。描述如下: typedef struct message_buffer { int sender; 发送者进程标识符 int size; 消息长度 char *text; 消息正文 struct message_buffer *next; 指向下一个消息缓冲区的指针 }
156
(2) PCB中有关通信的数据项。在PCB中应增加的数据项可描述如下:
typedef struct processcontrol_block { … struct message_buffer *mq; 消息队列队首指针 semaphore mutex; 消息队列互斥信号量 semaphore sm; 消息队列资源信号量 }PCB
157
2、发送原语 图 消息缓冲通信
158
2、发送原语 void send(receiver, a) { recevier为接收进程标识符,a为发送区首址
getbuf(a.size,i); 根据a.size申请缓冲区i; i.sender = a.sender; i.size = a.size; copy(i.text, a.text); 将发送区a中的信息复制到消息缓冲区i中; i.next = 0; getid(PCBset, receiver.j); 获得接收进程receiver的内部标识符(或PCB)j; wait(j.mutex); insert(&j.mq, i); 将消息缓冲区插入接收进程的消息缓冲队列mq; signal(j.mutex); signal(j.sm); }
159
3、接收原语 接收原语描述如下: void receive(b) { j = internal name; j为接收进程内部的标识符;
wait(j.sm); wait(j.mutex); remove(j.mq, i); 将消息队列中第一个消息移出; signal(j.mutex); b.sender = i.sender; b.size = i.size; copy(b.text, i.text); 将消息缓冲区i中的信息复制到接收区b; releasebuff(i); }
160
2.7 线程的基本概念
161
2.7.1 线程的引入 1、进程的两个基本属性 2、程序并发执行所需付出的时空开销 3、线程——作为调度和分派的基本单位
线程的引入:为了减少程序在并发执行时所付出的时 空开销,使OS具有更好的并发性,从而进一步提高了 资源的利用率和系统的吞吐量 进程是拥有资源的独立单位,是可独立调度和分派的基本单位 创建进程、撤消进程、进程切换 为适用于SMP的计算机系统 1、进程的两个基本属性 2、程序并发执行所需付出的时空开销 3、线程——作为调度和分派的基本单位
162
2.7.2 线程和进程的比较 1、调度的基本单位 2、并发性 在传统的OS中,拥有资源的基本单位和独立调度、分派 的基本单位都是进程。
有的应用程序需要执行多个相似任务,需要多线程实现, 如网络服务器。 1、调度的基本单位 2、并发性
163
3、拥有资源 4、独立性 5、系统开销 6、支持多处理机系统 在两种OS中,拥有资源的基本单位都是进程。
线程除了一点在运行中必不可少的资源外,本身基本不拥有系统资源,但它可以访问其隶属进程的资源。 同一进程中的不同线程之间的独立性要比不同进程之间的独立性低得多。 OS在创建和撤消进程,进程上下文切换时所付出的开销都明显地大于线程。 现代多处理机OS都引入了多线程 4、独立性 5、系统开销 6、支持多处理机系统
164
2.7.3 线程的状态和线程控制块 1、线程运行的三个状态 执行状态:表示线程正获得处理机而运行;
就绪状态:指线程已具备了各种执行条件,一旦获得CPU便可执行的状态; 阻塞状态:指线程在执行中因某事件而受阻,处于暂停执行时的状态。 1、线程运行的三个状态
165
2、线程控制块TCB 线程控制块中记录着所有控制和管理线程的信息,包括: 线程标识符,具有唯一性;
寄存器状态,包括程序计数器PC、状态寄存器和通用寄存器的内容; 线程运行状态,用于描述线程正处于何种运行状态; 优先级,描述线程执行的优先程度; 线程专有存储器,用于线和切换时存放现场保护信息,和与该线程相关的统计信息等贝; 信号屏蔽,即对某些信号加以屏蔽; 堆栈指针,通常保存有每次过程调用中的局部变量和返回地址。
166
3、线程的属性 4、多线程OS中的进程属性 轻型实体。 独立调度和分派的基本单位。 可并发执行。 共享进程资源。
是一个可拥有资源的基本单位。 可包括多个相对独立的线程,多线程可并发执行。 进程不是一个可执行的实体。
167
2.8 线程的实现
168
2.8.1 线程的实现方式 1、内核支持线程KST 内核支持线程是在内核空间实现的。 内核为每个线程在内核空间中设置了一个线程控制 块。
线程的实现方式 1、内核支持线程KST 内核支持线程是在内核空间实现的。 内核为每个线程在内核空间中设置了一个线程控制 块。 所有对线程的操作,如创建、撤消和切换等,都是 通过系统功能调用由内核中的相应处理程序完成。 设置了内核支持线程的系统,其调度是以线程为单 位进行的。
169
1、内核支持线程 优点: 缺点: 在多处理器系统中,内核能够同时调度同一进程中多个 线程并行执行到多个处理器中;
如果进程中的一个线程被阻塞,内核可以调度同一个进 程中的另一个线程; 内核支持线程具有很小的数据结构和堆栈,线程的切换 比较快,切换开销小; 内核本身也可以使用多线程的方式来实现。 缺点: 即使CPU在同一个进程的多个线程之间切换,也需要陷入 内核,因此其速度和效率不如用户级线程。
170
2、用户级线程ULT 用户级线程仅存在于用户空间中,与内核无关。
就内核而言,它只是管理常规的进程—单线程进 程,而感知不到用户级线程的存在。 每个线程控制块都设置在用户空间中,所有对线 程的操作也在用户空间中由线程库中的函数完成, 无需内核的帮助。 设置了用户级线程的系统,其调度仍是以进程为 单位进行的。
171
2、用户级线程 优点: 缺点: 线程的切换无需陷入内核,故切换开销小,速度非常快;
线程库对用户线程的调度算法与OS的调度算法无关,因 此,线程库可提供多种调度算法供应用程序选择使用。 用户级线程的实现与操作系统平台无关。 缺点: 系统调用的阻塞问题:对应用程序来讲,一个线程的阻 塞将导致整个进程中所有线程的阻塞; 多线程应用无法享用多处理机系统中多个处理器带来的 好处。
172
3、组合方式 内核支持多KST线程的建立、调度和管理,同时, 也允许用户应用程序建立、调度和管理用户级线程。
一些内核支持线程对应多个用户级线程,程序员可 按应用需要和机器配置对内核支持线程数目进行调 整,以达到较好的效果。 优点: 同一个进程内的多个线程可以同时在多处理器 上并行执行; 在阻塞一个线程时,并不需要将整个进程阻塞 172
173
4、用户级线程与内核控制线程的连接 一对一模型
为每一个用户线程都设置一个内核控制线程与 之连接,当一个线程阻塞时,允许调度另一个 线程运行; 在多处理机系统中,则有多个线程并行执行。 优点:并行能力强 缺点:开销大 实现该模型的操作系统:Windows 2000、 Windows NT、OS/2 173
174
4、用户级线程与内核控制线程的连接 多对一模型
将多个用户线程映射到一个内核控制线程,这些用户 线程一般属于一个进程,运行在该进程的用户空间, 对线程的调度和管理也是在该进程的用户空间完成; 用户线程需要访问内核时,才将其映射到一具内核控 制线程上,但每次只允许一个线程进行映射。 优点:线程管理的开销小、效率高。 缺点:当一个线程在访问内核时发生阻塞,整个进程 都会被阻塞;在多处理机系统中,一个进程的多线程 无法实现并行。 174
175
4、用户级线程与内核控制线程的连接 多对多模型
将多个用户线程映射到多个内核控制线程,内 核控制线程的数目可以根据应用进程和系统的 不同而变化。 优点:结合了一对一模型与多对一模型的优点 175
176
2.8.2 线程的实现 1、内核支持线程的实现 图 2 - 19 任务数据区空间 PTDA 进程资源 TCB # 1 TCB # 2
线程的实现 1、内核支持线程的实现 PTDA 进程资源 TCB # 1 TCB # 2 TCB # 3 图 任务数据区空间
177
2、用户级线程的实现 运行时系统(Runtime System)
是用于管理和控制线程的函数(过程)的集合, 包括用于创建和撤消线程的函数、线程同步和 通信的函数以及实现线程调度的函数等。 运行时系统中的所有函数都驻留在用户空间, 并作为用户级线程与内核之间的接口。
178
2、用户级线程的实现 内核控制线程 又称为轻型进程LWP(Light Weight Process)
每一个进程都可拥有多个LWP,每个LWP都有 自己的数据结构(如TCB),其中包括线程标识 符、优先级、状态,另外还有栈和局部存储区 等; 它们也可以共享进程所拥有的资源; LWP可通过系统调用来获得内核提供的服务。
179
图 利用轻型进程作为中间系统
180
线程的创建和终止 1、线程的创建 在多线程OS环境下,应用程序在启动时,OS将为它创建一个进程,同时为该进程创建第一个线程,这个线程被人们称为“初始化线程”。 “初始化线程”在运行过程中,可根据需要再利用线程创建 函数(或系统调用)再去创建若干个线程。所以线程是由线程 创建的,但线程间并不提供父子关系的支持。 在线程创建函数执行完后,将返回一个线程标识符供以后使 用。
181
2、线程的终止 终止线程通过调用相应的函数对线程执行终止操作。 正常中止 强行中止
在大多数的OS中,线程被中止后,被终止的线程不立即释 放它占有的资源,只有当进程中的其他线程执行了分离函 数后,被终止的线程才能与资源分享,此时资源才能被其 他线程利用。 被终止但尚未释放资源的线程仍可以被需要它的线程所调 用,并重新恢复运行。 调用线程调用“等待线程终止”的连接命令与终止线程 进行连接
Similar presentations