操作系统概念:
1. 总述
2. 进程管理
3. 内存管理
4. 文件管理
5. IO管理
进程管理:进程是由于OS通过 创建原语 创建,运行所需的信息保存于 PCB 中,完成 进程调度 后,通过 进程控制原语 ,进行 进程状态切换。
进程可以划分成更小的调度单位 线程,用来提高系统并行度
为更好的描述和控制多道程序的并发执行,引入 进程 的概念
进程是运行中的进程实体在某一时刻的快照
线程间有独立性也有相关性
关联:进程是程序的一次执行过程
程序:存放于磁盘中的静态可执行文件
- 程序封闭性:运行结果取决于程序本身,不受外界影响
进程:在内存中运行的进程实体,是一个动态过程
- 进程执行速度不会改变执行结果
单个进程某一时刻只能执行一个程序
进程实体=PCB+程序段+数据段
程序控制块是进程存在的唯一标识
描述进程的基本情况和运行状态:OS对进程管理工作所需的信息都保存在PCB中
组成:
进程描述信息
PID:进程ID
UID:进程归属的用户ID
进程控制和管理信息
进程优先级
进程当前状态
资源分配清单
说明与该进程相关的内存地址空间或虚拟地址空间的使用情况
所示用的IO设备信息
处理机相关信息
CPU 中各寄存器的值
PCB的创建与销毁
指令集
多个进程可独立运行同一段程序
进程运行期间的相关数据
数据
动态性
进程的生命周期:创建,活动,暂停,终止
动态性时最基本特征
并发性
一段时间多个进程并发执行
独立性
进程实体是一个能独立运行、获取资源和接受调度的基本单位
异步性
进程按各自速度向不可预知的方向推进
进程不可复现
即PCB的组织方式
按照进程状态不同,分为多个队列
OS持有各队列的指针
根据进程状态的不同,创建索引表
OS持有各索引表的指针
引入进程:更好的描述和控制多道程序的并发执行
引入线程:
- 一个进程内,并发处理多个事务
- 减少程序在并发执行时,所付出的时空开销
支持线程的OS中:
线程可创建和撤销另一个线程
多核计算机中,各线程可占用不同的CPU
线程状态:就绪,阻塞,运行
同一进程的线程切换,不会引起进程切换
不同进程中的线程切换,会引起进程切换
切换线程,系统开销小
对于操作系统而言,可同时执行多个程序
对同一程序而言,一个程序可执行多个线程,完成不同事务
调度(处理机分配) | 资源分配 | 地址空间和其他资源 | 通信 | 并发性 | 切换开销 | |
---|---|---|---|---|---|---|
进程 | 传统OS,进程是资源分配和调度的基本单位 | 进程是拥有资源的基本单位 | 进程的地址空间独立 | 进程间通信需要同步与互斥机制 | 进程间的并发提高任务的并发度 | 需改变运行环境 |
线程 | 引入线程的OS,线程是调度的基本单位,进程是资源分配的基本单位 | 进程是拥有资源的基本单位 | 同一进程的线程间共享进程的资源,某进程内的线程对其他进程不可见 | 线程间共享存储,直接读/写数据段 | 多进程间的并发,多线程间的并发提高并发度 | 同一进程内线程切换开销小 |
一段代码逻辑只有获得运行机会才能被CPU执行
由应用程序完成线程管理的工作,线程的状态切换无需OS干预(在用户态完成),OS感受不到线程的存在
优点:
缺点
由OS完成线程管理工作,线程状态切换需要切换到 内核态 才能完成
内核级线程才是调度的基本单位
优点
缺点
一个用户级线程映射到一个内核级线程。
每个用户进程都有与用户级线程同等数量的内核级线程
优点:
缺点
多个用户线程映射到一个内核级线程
一个进程只能被分配到一个内核级线程
优点:
缺点
n个用户线程映射到m个内核级线程 n ≥ m n\ge m n≥m
每个用户进程对应 m 个内核级线程
运行->阻塞 进程自身做出的 主动行为
阻塞->就绪 不是进程自身控制 被动行为
运行->就绪:时间片用完;有优先级更高的进程抢占CPU
没有运行态的进程,一定没有阻塞态的进程
没有运行态的进程不代表没有进程,发生死锁时,所有进程都在阻塞态
进程控制的程序段,称为原语
通过相关的系统调用进入内核态,由内核态的CPU执行相应的程序段
原语具有原子性,用 关中断
和 开中断
这两个特权指令实现原子性
调度是决策行为
切换是执行行为
CPU模式切换与进程切换:区别在于是否改变运行环境(寄存器的值)
模式切换:在一个进程执行期间,因中断或异常进入内核态,执行完成后回到用户态
进程切换
进程调度与进程切换
进程调度:从就绪队列中选一个进程
进程切换:
进程调度与切换会带来系统开销,不是切换越频繁,并发度越高
调度:按某种算法从就绪队里中选择一个进程,分配CPU
调度是多道程序设计的基础
一个具体任务,一段程序
批处理系统中作业与进程的关系:一个作业一个根进程
分时系统中作业与进程关系:一个用户一个根进程
作业:用户的一次上机交互过程
系统为每个终端创建一个进程
进程执行解释程序
每条终端命令,创建一个子进程执行
为提高内存利用率,将暂时不能运行的进程调到外存中等待
挂起与阻塞都得不到CPU服务,但从运行态到阻塞态是进程映像留在内存中,到挂起态是将进程映像放入外存,主存中不存
做什么 | 调度场所 | 发生频率 | 对进程状态的影响 | |
---|---|---|---|---|
作业调度(高级调度) | 从后备队列中选择一个作业,分配内存呢等必要资源,建立根进程 一个作业只发生一次调入,一次调出 |
外存->内存(面向作业) | 最低 | 无 → 创建态 → 就绪态 无\rightarrow 创建态\rightarrow 就绪态 无→创建态→就绪态 |
内存调度(中级调度) | 从挂起队列中选择合适的进程将数据调回内存 | 外存->内存(面向进程) | 中 | 挂起态 → 就绪态 挂起态\rightarrow就绪态 挂起态→就绪态 |
进程调度(低级调度) | 从就绪队列中选择合适进程,分配处理机 | 高 | 就绪态 → 运行态 就绪态\rightarrow运行态 就绪态→运行态 |
当前进程 主动放弃 处理机
当前进程 被动放弃 处理机
在 处理中断的过程 中
当前进程在 操作系统内核程序临界区
当前进程在 原子操作过程中
有更高优先级的进程进入就绪队列,如何分配处理机
只允许进程主动放弃处理机
可优先处理更紧急进程,也可让各进程按时间片轮流执行
C P U 利用率 = 忙碌时间 总时间 CPU利用率=\frac{忙碌时间}{总时间} CPU利用率=总时间忙碌时间
如:某计算机只支持单道程序,某作业刚开始需要在CPU上执行5s,再在打印机上输出5s,之后再执行5s,才结束。CPU利用率,打印机利用率分别为多少
C P U 利用率 = 5 + 5 5 + 5 + 5 = 66.67 CPU利用率=\frac{5+5}{5+5+5}=66.67% CPU利用率=5+5+55+5=66.67
打印机利用率 = 5 15 = 33.33 打印机利用率=\frac{5}{15}=33.33% 打印机利用率=155=33.33
单位时间CPU完成的作业数
系统吞吐率 = 作业数量 时长 系统吞吐率=\frac{作业数量}{时长} 系统吞吐率=时长作业数量
如:某计算机系统处理10道作业,共花费100s,则系统吞吐率为
系统吞吐率 = 10 100 = 0.1 道 / s 系统吞吐率=\frac{10}{100}=0.1道/s 系统吞吐率=10010=0.1道/s
从作业提交到系统到处理完成的时间
- 一次高级调度时间——在后备队列中等待时间
- 多次低级调度时间——在就绪队列中时间
- 运行时间
- 等待IO操作完成时间
周转时间 = t 作业完成时刻 − t 作业提交时刻 平均周转时间 = 各作业周转时间 作业数 带权周转时间 = 作业周转时间 系统为其服务时间 \begin{aligned} &周转时间=t_{作业完成时刻}-t_{作业提交时刻}\\\\ &平均周转时间=\frac{各作业周转时间}{作业数}\\\\ &带权周转时间=\frac{作业周转时间}{系统为其服务时间} \end{aligned} 周转时间=t作业完成时刻−t作业提交时刻平均周转时间=作业数各作业周转时间带权周转时间=系统为其服务时间作业周转时间
进程等待CPU的时间之和
进程等待时间:从进程建立开始计时,等待IO的时间不算等待时间
作业等待时间:进程等待时间+处于后备队列的时间
用户提交请求到首次产生响应的时间
如:
进程 | 到达时间 | 运行时间 |
---|---|---|
P 1 P_1 P1 | 0 | 7 |
P 2 P_2 P2 | 2 | 4 |
P 3 P_3 P3 | 4 | 1 |
P 4 P_4 P4 | 5 | 4 |
非抢占式短作业优先:每次调度选择当前已到达且运行时间最短的作业/进程
调度顺序为: P 1 → P 3 → P 2 → P 4 P_1\rightarrow P_3 \rightarrow P_2\rightarrow P4 P1→P3→P2→P4
最短剩余时间优先(抢占式短作业优先)
调度顺序: P 1 → P 2 → P 3 → P 2 → P 4 → P 1 P_1\rightarrow P_2\rightarrow P_3\rightarrow P_2\rightarrow P4\rightarrow P_1 P1→P2→P3→P2→P4→P1
非抢占式,只有当期运行的进程 主动放弃CPU 才需要调度。
首先计算所有进程的响应比,选响应比最高的进程上处理机
就绪队列未必只有一个,可以按照不同优先级来组织
通常
系统进程优先级高于用户进程
前台进程优先级高于后台进程
操作系统更偏向IO型进程
假设某计算机系统有4个进程,用时间片轮转调度算法进行调度,分别计算各进程的调度次序和平均周转时间
进程 | 到达时间 | 运行时间 |
---|---|---|
P 1 P_1 P1 | 0 | 8 |
P 2 P_2 P2 | 1 | 4 |
P 3 P_3 P3 | 2 | 9 |
P 4 P_4 P4 | 3 | 5 |
P1用完前两个时间片被放到队尾,此时就绪队列为 P 2 → P 3 → P 1 P_2\rightarrow P_3\rightarrow P_1 P2→P3→P1,取队头的P2调度
P2运行完两个时间片被放到队尾,此时就绪队为 P 3 → P 1 → P 4 → P 2 P_3\rightarrow P_1\rightarrow P_4\rightarrow P_2 P3→P1→P4→P2,取队头的P3调度
…
进程 | 到达就绪队列时刻 | 预计执行时间 | 执行时间段 | 周转时间 |
---|---|---|---|---|
P 1 P_1 P1 | 0 | 8 | 0~2;6~8;14~16;20~22 | 22-0=22 |
P 2 P_2 P2 | 1 | 4 | 2~4;10~12 | 12-1=11 |
P 3 P_3 P3 | 2 | 9 | 4~6;12~14;18~20;23~25;25~26 | 26-2=24 |
P 4 P_4 P4 | 3 | 5 | 8~10;16~18;22~23 | 23-3=20 |
平均周转时间= 22 + 11 + 24 + 20 4 = 19.25 \frac{22+11+24+20}{4}=19.25 422+11+24+20=19.25
某单CPU系统中有输入和输出设备各一台,3个并发执行的作业,每个作业输入、计算、输出时间内分别为 2ms、3ms和4ms,且按输入、计算、输出的顺序执行,则执行完3个作业最少需要的时间是
某计算机系统有一个CPU,一台输入设备,一台打印机。进程A先获得CPU运行,进程B后运行。A:计算50ms,打印100ms,计算50ms,打印100ms。B:计算50ms,输入80ms,计算100ms
C P U 等待时间: 100 m s − 150 m s C P U 利用率 = 300 − ( 150 − 100 ) 300 = 0.833 进程 B 的等待时间: 0 − 50 m s , 180 − 200 m s \begin{aligned} &CPU等待时间:100ms-150ms\\ &CPU利用率=\frac{300-(150-100)}{300}=0.833\\ &进程B的等待时间:0-50ms,180-200ms \end{aligned} CPU等待时间:100ms−150msCPU利用率=300300−(150−100)=0.833进程B的等待时间:0−50ms,180−200ms
多个进程因竞争资源而造成的互相等待情况,若无外力作用,这些进程都无法向前推进
三者都是进程无法顺利向前推进的情况
涉及进程数量 | 进程状态 | 产生原因 | |
---|---|---|---|
死锁 | 至少两个或两个以上的进程发生死锁 | 所有进程都处于阻塞态 | 操作系统分配资源的策略不合理造成 |
饥饿 | 可能只有一个进程发生饥饿 | 发生饥饿的进程可能处于阻塞态(长期得不到系统资源)或就绪态(长期得不到处理机) | 操作系统分配资源的策略不合理造成 |
死循环 | 可能只有一个进程进入死循环 | 可能处于运行态 | 代码逻辑本身的问题 |
对不可剥夺的资源的不合理分配,导致死锁
系统中有不可剥夺资源,且数量不足以使多个进程同时运行
进程的推进顺序不合法
请求和释放资源的顺序不当
信号量的使用不合法(同步关系不合理)
资源分配图含圈不一定造成死锁,同类资源数大于1
当系统提出新的资源申请,且所有进程都进入阻塞态,系统才进入死锁
设置限制条件,破坏四个必要条件中的一个
如:SPOOLing技术将打印机改造为共享设备
缺点
根据优先级,在OS协助下强行剥夺运行资源,或
主动释放资源
缺点
静态分配:进程运行前一次性将所有资源分配
如:哲学家模型,只有左右筷子都能拿到才能拿
缺点
顺序资源分配法
- 给系统中的各资源编号,规定每个进程按编号递增的顺序请求资源,同类资源一次请求完成
- 一个进程有了小资源采取申请大资源
- 已持有大资源不能逆向请求小资源
如:哲学家减一可拿、编号:哲学家奇偶(拿到小的才能拿大的)
缺点:
动态分配资源,不进入不安全状态
系统按照某种序列分配资源,每个进程都能完成
只要能找出一个安全序列,系统当前处于安全状态
避免系统进入不安全状态
设共有n个进程,争用m个资源
Available[m]
:当前可用的资源数
Max[n][m]
:每个进程对资源的最大需求
Allocation[n][m]
:每个进程已经分配到的资源数
Need[n][m]
:当前各进程还需要多少资源
Request[m]
:某个进程请求向量
work[m]
:Available的一个临时副本,保存进入安全性检测算法的剩余资源数
现在已有进程Pi的资源请求向量 Request_i[m]
。
检查本次请求资源数量是否超过该进程所宣布的最大值 Request_i[m] <= Need[i][m]
,若成立,则下一步;若不成立,则报错
检查系统中的可分配资源是否满足本次分配 Request_i[m]<= Available[m]
,若成立,则下一步;若不成立,则等待
系统试探性分配,并修改相应的数据结构
Available[m] -= Request_i[m]
Allocation[i][m] += Request_i[m]
Need[i][m] -= Request_i[m]
系统执行安全性算法,检查此次资源分配后是否处于安全状态,若处于安全状态,才正式将资源分配给进程Pi。否则本次试探作废,恢复原先的数据结构,Pi等待。
安全性算法:
初始化安全序列为空,work[m]=Available[m]
检查当前剩余可用资源是否可满足某个不在安全序列中的进程的最大需求量。 就是从Need矩阵
找一行满足 Need[i][m]< work[m]
将该进程加入安全序列。表示该进程获得所需资源后,可以执行完成,最后会将分配给他的资源释放。
此时 work[m]+=Allocation[i][m]
。
一直执行第2步,直至 最大需求量小于可分配资源量 不成立或者安全序列已满
如果此时安全序列包含所有进程,则存在安全序列,系统处于安全状态。否则,不存在安全序列。
进程发生死锁,OS的检测机构及时检测到死锁状态,采取相应措施解除死锁
两种结点
两种边
从资源分配边删除几条就减少几点资源结点的数值
请求资源结点为0的进程阻塞;请求资源结点有空闲的,分配资源并删除请求边,若满足该进程的所有需求,则归还其占有的全部资源
若资源分配图不可简化,则系统进入了死锁状态
资源剥夺 :挂起死锁进程,并抢占其资源
撤销进程 :撤销全部或部分已死锁进程
进程回退
多个进程并发执行,存在 互斥 与 同步 两种关系,其中进程间同步以 信号量机制 为底层原理实现,有一系列的 同步互斥问题模型 。由于PV操作散落在各个进程中,引入 管程 对信号量进行集中管理
进程间的直接制约关系
如:拓扑关系,逆拓扑关系
进程间的间接制约关系
主要体现在各进程对临界资源的访问要互斥进行
临界资源:一个时间段内只允许一个进程使用的系统资源
区分共享资源:一次可供多个进程使用
如:公共队列属于临界资源;可重入代码属于共享资源
对临界资源的访问过程
进程请求临界资源的状态
空闲让进:临界资源空闲,则给进程分配该临界资源
忙则等待:已经获得临界区使用权的进程不能被剥夺
有限等待:不产生饥饿现象
让权等待:进程不能进入临界区,立即释放处理机
由于使用硬件实现,所以不允许被打断
原子操作,执行过程不可中断
硬件上由 “开/关中断” 指令实现
一组低级的进程通信原语
wait(S),P(S)
signal(S),V(S)
表示系统中的某种资源
整型信号量
用整数表示资源数量
操作:初始化,P使用,V归还
//某计算机系统有一台打印机
int S = 1;//初始化信号量S,表示当前系统中可用的打印机资源数量
void wait(int S){//相当于进入区
while(S <= 0);//资源不够,则一直等待
S=S-1;
}
void signal(int S){
S=S+1;//使用完后,在退出区释放资源
}
进程P0:
wait(S); //进入区,申请资源
使用打印机资源; //临界区,访问资源
signal(S); //退出区,释放资源
没有遵循让权等待
记录型信号量
用资源队列记录P操作,若资源有空闲,从队列中取
//记录型信号量定义
typedef struct{
int value; //剩余资源数
struct process *L;//想用该资源的进程队列
}semaphore;
//使用资源,通过P原语申请
void P(semaphore S){
S.value--;
if(S.value < 0)
block(S.L);//将等待中的队列阻塞,释放处理机
}
//释放资源,通过V原语申请
void V(semaphore S){
S.value--;
if(S.value <= 0)
wakeup(S.L);//当资源满足时,进入就绪队列,可分配处理机
}
mutex=1
,有几个互斥临界区设置几个互斥量P(mutex)
V(mutex)
原则
步骤
分析在什么地方需要 “同步关系”——先后关系
划分临界区
设置各临界区的信号量 semaphore S = n;
前操作完成后,对相应的信号量 V操作
后操作执行前,对相应的信号量 P操作
可以P的前提是之前V过,这就是同步关系
特点
代码实现
代码实现
会造成写进程的饥饿现象
将运行机会抽象成一个互斥量,只有获得运行机会的才能运行,提高写进程的运行概率
一个运行进程需持有多个临界资源
可能会造成 死锁
互斥关系:哲学家对哲学家间的筷子互斥访问
同步关系:凑齐所需资源的进程才能运行
如图:至少有一个进程可以拿到全部资源
semaphore chopsticks[5] = {1,1,1,1,1};
semaphore r = 4;
Pi(){
while(1){
P(r);
P(chopsticks[i]);
P(chopsticks[(i+1)%5]);
eat();
P(chopsticks[i]);
P(chopsticks[(i+1)%5]);
V(r);
think();
}
}
2. 奇数号的先拿左边,偶数号的先拿右边
拿到才申请另一边
semaphore chopsticks[5] = {1,1,1,1,1};
Pi(){
while(1){
P(r);
if(i&1){
P(chopsticks[i]);
P(chopsticks[(i+1)%5]);
eat();
P(chopsticks[i]);
P(chopsticks[(i+1)%5]);
}else{
P(chopsticks[(i+1)%5]);
P(chopsticks[i]);
eat();
P(chopsticks[(i+1)%5]);
P(chopsticks[i]);
}
}
}
3. 左右都能拿才拿
semaphore chopsticks[5] = {1,1,1,1,1};
semaphore mutex = 1;
Pi(){
while(1){
P(mutex);
P(chopsticks[i]);
P(chopsticks[(i+1)%5]);
V(mutex);
eat();
P(chopsticks[i]);
P(chopsticks[(i+1)%5]);
think();
}
}
这类问题主要研究先后关系,不涉及临界资源的访问
1. 面包师父有很多面包,由n个销售人员销售,每个顾客进店后取号,并且等待叫号,当一个销售人员空闲就叫下一个号。
同步关系:
semaphore num = 0;//拿到号码
semaphore service = 0;//等待服务
cobegin{
process 顾客{
进店;
取号;
V(num);//拿到号
等待被叫号;
P(service);//请求服务
}
process 销售{
while(1){
P(num);//有才叫号
叫号;
V(service);//提供服务
提供服务;
}
}
}coend
2. 公共汽车上,驾驶员与售票员的配合如下
同步关系:
semaphore start = 0;//同步关系:关了车门才能启动
semaphore open = 0;//同步关系:停车才能开门
cobegin{
process 驾驶员{
P(start);//请求发车
启动车辆;
正常行车;
到站停车;
V(open);//可以开门
}
process 售票员{
关车门;
V(start);
售票;
P(open);
开车门;
}
}coend
用同步有互斥,且进程间需要相互调用
三个合作进程P1,P2,P3,通过同一设备输入各自数据a,b,c,该输入设备必须互斥使用。第一个数据必须被P1读取,第二个数据必须被P2读取,第三个数据必须被P3读取,然后进行运算,最后由P1输出
互斥关系:
同步关系
semaphore Sa = 0,Sb=0,Sc=0;//输入a,b,c
semaphore Sz = 0,Sy = 0;//对计算结果的同步量
semaphore mutex = 1;//对输入设备的互斥访问
cobegin{
process P1{
P(mutex1);
输入a;
V(mutex2);
V(Sa);//提供a
P(Sb);
x = a+b;
P(Sy);
P(Sz);
输出x,y,z;
}
process P2{
P(mutex2);
输入b;
V(mutex3);
V(Sb);
P(Sa);
y = a*b;
V(Sy);
}
process P3{
P(mutex3);
输入c;
P(Sy);
P(Sa);
z = y+c-a;
V(Sz);
}
}coend
有互斥有同步关系
互斥资源
同步关系:
有空位才能生产
生产了才能消费
1. 三个进程P1、P2、P3互斥使用一个N个单位的缓冲区。P1生产一个正整数放入缓冲区;P2取奇数,用countodd()计数;P3取偶数,用counteven()计数
互斥关系
同步关系
semaphore mutex = 1;//对缓冲取得访问
semaphore empty = N;//同步关系1:缓冲区不满才能生产
semaphore odd = 0;//同步关系2:生产了奇数才能消费奇数
semaphore even = 0;//同步关系3:生产了偶数才能消费偶数
cobegin{
process 生产者P1{
while(1){
x = produce();
P(empty);
P(mutex);
Put();
V(mutex);
if(x&1)
V(odd);
else
V(even);
}
}
process 消费者P2{
while(1){
P(odd);
P(mutex);
x = getOdd();
V(mutex);
V(empty);
countOdd();
}
}
...
}coend
2. 设博物馆最多容纳500人,一个出入口,一次仅允许一个人通过。每个参观者:进门,参观,出门
互斥量
同步量
semaphore mutex = 1;//对门的互斥访问
semaphore empty = 500;//不空
cobegin{
参观者 Pi{
P(empty);
P(mutex);
进门;
V(mutex);
参观;
P(mutex);
出门;
V(mutex);
V(empty);
}
}coend
3. P,Q,R共享一个缓冲区,P-Q生产者-消费者,R既是生产者又是消费者
互斥关系
同步关系
semaphore mutex = 1;//互斥关系,缓冲区
semaphore full = 0;//同步关系1:不空才能消费
semaphore empty = 1;//同步关系2:不满才能生产
cobegin{
process P{
P(empty);
P(mutex);
存数据;
V(mutex);
V(full);
}
process Q{
P(full);
P(mutex);
取数据;
V(mutex);
V(empty);
}
process R{
if(empty){
P(empty);
P(mutex);
存数据;
V(mutex);
V(full);
}
if(full){
P(full);
P(mutex);
取数据;
V(mutex);
V(empty);
}
}
}coend
4. 某银行提供1个服务窗口和10个供顾客等待的座位。顾客到达银行时,若有空座位,则到取号机上领取一个号,等待叫号。取号机每次仅允许一位顾客使用。当营业员空闲时,通过叫号选取一位顾客,并为其服务。
同步关系:
empty
full
互斥关系
service
mutex
semaphore empty = 10;//有空位才去取号
semaphore full = 0;//有顾客才去服务
semaphore mutex = 1;//互斥访问取号机
semaphore service = 1;//服务的互斥
cobegin{
process 顾客(i){
P(empty);
P(mutex);
取号;
V(mutex);
V(full);
P(service);
接收服务;
}
process 营业员{
while(1){
P(full);//有顾客才能服务
V(empty);
为顾客服务;
V(service);
}
}
}coend
互斥关系
同步关系
semaphore full = 0;//同步关系1:有顾客才理发
semaphore empty = n;//同步关系2:有空椅子才坐
semaphore mutex = 1;//互斥关系,只有一个正在理发的顾客
cobegin{
process 顾客{
if(empty != 0){
P(empty);//椅子有空位才坐
V(full);
P(mutex);
理发;
}else
离开;
}
process 理发师{
P(full);//椅子上有人才理发
V(empty);
理发;
V(mutex);
}
}
6. 生产者与消费者共享1000个单位的环形缓冲区,当缓冲区未满,生产者可放入一个产品,当缓冲区不空,消费者可以从缓冲区取走一件产品。消费者从缓冲区连续取走10个产品后其他消费者才能取
互斥关系
同步关系
semaphore mutex = 1;//各进程互斥访问缓冲区
semaphore empty = 1000;//同步关系1:有空闲缓冲区才能生产
semaphore full = 0;//有产品才能取
semaphore mutex2 = 1;//
cobegin{
process producer{
生产一个产品;
P(empty);
P(mutex);
把产品放入缓冲区;
V(mutex);
V(full);
}
process consumer{
P(mutex2);
for(int i = 0;i < 10;++i){
P(full);
P(mutex);
从缓冲区取出一件产品;
V(mutex);
V(empty);
消费产品
}
V(mutex);
}
}coend
互斥关系
同步关系
1. 两个车间分别生产A,B两种零件,装配车间将A,B组装成产品。两个生产车间生产零件后放入各自缓冲区F1,F2(容量都为10)。
semaphore empty1 = 10;//同步关系1:F1不满A可生产
semaphore empty2 = 10;//同步关系2:F2不满B可生产
semaphore full1 = 0;//同步关系3:F1不空A可取
semaphore full2 = 0;//同步关系4:F2不空B可取
cobegin{
process A{
while(1){
P(empty1);
生产A;
P(mutex1);
将A放到F1上;
V(mutex1);
V(full1);
}
}
process B{
while(1){
P(empty2);
生产B;
P(mutex2);
将B放到F2上;
V(mutex2);
V(full2);
}
}
process C{
while(1){
P(full1);
P(mutex1);
从货架F1上取A;
V(mutex1);
V(empty1);
P(full2);
P(mutex2);
从货架F2上取B;
V(mutex2);
V(empty1);
将A与B组装成产品
}
}
}
2. 在一个仓库中存放A和B两种产品,每次只能存入一种产品,A产品数量-B产品数量 互斥关系 同步关系 互斥 同步关系 互斥关系 同步关系 1. 有3个并发执行的线程 thread1,thread2和thread3,其伪代码 读写互斥 2. 读完才能写 同一文件可被多个进程同时读 多个写进程FCFS 读优先的做法 3. 第一个读者负责上锁,最后一个读者负责解锁 相当于进程既可作读者又可作写者 4. 在两地之间有一条路,中间有一个错车点,允许两辆车错车,但其余路段同一时刻只能有一辆车。 NN:读进程 TT:写进程 读进程和写进程分别最多有一个 T与N只能读或者写 M读写可同时进行,也可只读只写 互斥资源 引入管程解决临界区分散带来的管理和控制问题 管程 资源管理程序:代表资源的数据结构+在这些数据结构上的操作 条件变量:阻塞原因定义为条件变量,每个条件变量保存了一个等待队列,用于记录因该条件变量阻塞的所有进程 必须由内核参与实现进程间的通信 OS提供用于共享使用的存储空间,同步互斥的工具 用户进程自行安排读写指令完成数据交换 基于数据结构:但有格式限制和速度慢的缺点。 基于存储区的共享 利用OS提供的文件共享功能 需要信号量机制解决文件共享操作中的同步与互斥功能 管道:用于连接读写进程的一个共享文件,实质上是在内存中开辟一个固定大小的缓冲区 管道只采用半双工通信,某一时间段只实现单向传输 各进程要互斥地访问管道 数据以字符流的形式写入管道 如果没写满,则不允许读;没读空,则不允许写 进程间的数据交换以格式化消息 为单位。通过 发送消息/接收消息 两个原语进行数据交换 消息头:发送进程ID,接收进程ID,消息类型,消息长度 消息体 直接通信方式:消息直接挂在接收进程的消息缓冲队列上 间接通信方式:消息先发送到中间实体中(信箱)
semaphore well = 1;//互斥访问水井
semaphore vat = 1;//互斥访问水井
semaphore empty = 10;//同步关系1:水缸不满才能提水
semaphore full = 0;//同步关系2:水缸不空才能瞌睡
semaphore pail = 3;//同步关系:拿到桶才能改变水缸的水
cobegin{
process 老和尚{
while(1){
P(full);
P(pail);
P(vat);
从水缸打一桶水;
V(vat);
V(empty);
喝水;
V(pail);
}
proccess 小和尚{
while(1){
P(empty);
P(pail);
P(well);
从井中打一桶水;
V(well);
P(vat);
将水倒入水缸中;
V(vat);
V(full);
V(pail);
}
}
}
c. 读者-写者
//复数的结构类型定义
typedef struct{
float a;
float b;
}cnum;
cnum x,y,z;
//复数之和
cnum add(cnum p,cnum q){
cnum s;
s.a = p.a+q.a;
s.b = p.b+q.b;
return s;
}
thread1{
cnum w;
w = add(x,y);
}
thread2{
cnum w;
w = add(y,z);
}
thread3{
cnum w;
w.a = 1;
w.b = 1;
z = add(z,w);
y = add(y,w);
}
int countSN = 0;//从S到N的汽车数量
int countNS = 0;//从N到S的汽车数量
semaphore mutexSN = 1;//SN方向的写锁
semaphore mutexNS = 1;//NS方向的写锁
semaphore bridge = 1;//临界区
cobegin{
StoN{
P(mutexSN);
if(countSN == 0)
P(bridge);
countSN++;
V(mutexSN);
过桥;
P(mutexSN);
countSN--;
if(countSN == 0)
V(bridge);
V(mutexSN);
}
NtoS{
P(mutexNS);
if(countNS == 0)
P(bridge);
countNS++;
V(mutexNS);
过桥;
P(mutexNS);
countNS--;
if(countNS == 0)
V(bridge);
V(mutexNS);
}
}
semaphore N2T = 1;
semaphore T2N = 1;
semaphore T = 1;
semaphore N = 1;
cobegin{
process TT{
P(T2N);//请求从T到N发车
P(T);
在T路段行车,进入错车点;
V(T);
V(N);
在N路段行车;
V(N);
V(T2N);//到达N后可以有T2N的车
}
process NN{
P(N2T);//请求从N到T发车
P(N);
在N路段行车,进入错车点;
V(N);
V(T);
在T路段行车;
V(T);
V(N2T);//到达T后可以有N2T的车
}
}coend
//没有同步关系
semaphore N2T = 0;//该方向有车
semaphore T2N = 0;
semaphore T = 1;//互斥资源
semaphore N = 1;//互斥资源
process TT{
P(T);
V(T2N);
在T路段行车,进入错车点;
V(T);
P(N);
在N路段行车;
V(N);
//显然无法建立同步关系P(N2T)放哪,整个路段只有一辆车也是可以的
}
2.5.5 管程
a. 目的
b. 怎么做的
c. 约定
2.6 进程间的通信
2.6.1 低级的通信方式——PV操作
2.6.2 高级通信方式
a. 共享内存区
低级方式的共享
高级方式的共享
b.共享文件
c. 管道通信
write()系统调用
会被阻塞,等待读进程取走read()系统调用
会被阻塞d. 消息传递
消息组成
消息传递方式