进程与线程
CPU 调度
先来先服务(FCFS)调度算法
短作业优先(SJF)调度算法
高响应比优先调度算法
优先级调度算法
时间片轮转(RR)调度算法
多级队列调度算法
多级反馈队列调度算法
基于公平原则的调度算法
多处理机调度
同步与互斥
同步与互斥的基本概念
在多道程序环境中,进程是并发执行的,不同进程之间存在着不同的相互制约关系,进程之间执行顺序可能不同,同步要保证某些进程一定要发生在另一些进程之前,也就是要有执行的顺序。
对于某些计算机资源,进程之间是不能同时访问的(如:打印机),互斥就是要保证这点。
这一节核心就是同步和互斥机制的实现和运用。
临界资源
一次只允许一个进程访问的资源称为临界资源。
对于临界资源,进程必须互斥地进行访问,在每个进程中,访问临界资源的那段代码称为临界区。
对临界资源的访问过程可以划分为 4 个部分:
● 进入区
● 临界区
● 退出区
● 剩余区
同步
又称直接制约关系。
指为了完成某种任务而建立的两个或多个进程,这些进程因为需要协调它们的运行次序而等待、传递信息所产生的制约关系。
著名的“生产者-消费者问题”就是一个同步问题。
互斥
又称间接制约关系。
当一个进程进入临界区使用临界资源时,另一个进程必须等待,当占用临界资源的进行退出临界区后,另一个进程才能去访问此临界资源。
两个进程都需要使用“打印机”这个临界资源,但是只有一台打印机,当 A 进程在使用时,B 就不能去使用,只有当 A 释放了“打印机”资源,B 才能去使用。
为了禁止两个进程同时进入临界区,同步机制应该满足以下准则:
● 空闲让进
● 忙则等待
● 让权等待(不是必须,原则上应遵循)
● 有限等待
实现临界区互斥的基本方法
软件实现
因为是“软件实现”,那肯定为设置“变量”来达到目的撒(实际上也确实如此)。
通过设置一些“标志”来表明是否有进程进入临界区中,若有则其他进程在进入区通过循环检测进行等待,直到进入到临界区的进程在退出区修改“标志”,否则直接进入。
wjegghewoi
单标志法
主要用于两个进程之间的互斥访问临界资源。
使用一个 tag 变量作为标志,若 tag=0 则 P0 进程可以使用临界资源,若 tag=1 则 P1 进程可以使用临界资源。
int tag = 0;
P0(){
while(tag != 0){
};
临界区代码;
tag = 1;
剩余区代码;
}
P1(){
while(tag != 1){
};
临界区代码;
tag = 0;
剩余区代码;
}
对于单标志法,虽然简单,但有着巨大的缺陷。设想一下,如果 P0 顺利的执行并离开,那此时的临界资源是空闲的,但 P1 不再使用这个临界资源,则 tag=1 一直都成立,P0 也就无法再执行临界区代码了(两个进程必须交替的进入临界区,P0 执行了 P1 执行,P1 执行了 P0 执行,往复循环)。
这样违背了“空闲让进”准则,在实际生产中很少使用。
双标志先检查法
通过设置可以表示两个信号的 tag 标志进行互斥访问。
tag[0] 为 true 则表示 P0 想进入临界区,另一个同理。P0 先判断 P1 是否想进入临界区,并且在进入临界区之前将自己设置为想进入临界区,这样就算切换到 P1,P1 也不会进入临界区,在 P0 执行完临界区代码后设置自己不想访问临界区,这样 P1 就可以进行访问了。
bool tag[2];
P0(){
while(tag[1]){
};
tag[0] = true;
临界区代码;
tag[0] = false;
剩余区代码;
}
P1(){
while(tag[0]){
};
tag[1] = true;
临界区代码;
tag[1] = false;
剩余区代码;
}
这样的设计使得进程之间不用交替使用,但是 P0 和 P1 可能同时进入临界区,P0 在执行 while 循环后切换到了 P1,因为 P1 没有设置自己想访问临界区所以 P1 也可以通过 while 循环,这样 P0,P1 两个进程就同时进入了临界区,违背了“忙则等待”准则。
这里的核心痛点是:while 判断和设置不能一气呵成,导致其他进程可以“趁虚而入”。
双标志后检查法
Peterson 算法
硬件实现
互斥锁
信号量
经典同步问题
生产者-消费者问题
多生产者-多消费者问题
吸烟者问题
读者-写者问题
哲学家进餐问题
管程
死锁