同步与互斥的概念
①、间接相互制约关系(互斥):若某一进程要求使用某种资源,而该资源正被另一进程使用,并且该资源不允许两个进程同时使用,那么该进程只好等待已占有资源的进程释放资源后再使用。这种制约关系的基本形式是“进程-资源-进程”。
这种制约关系源于多个同种进程需要互斥地共享某种系统资源,互斥是设置在同种进程之间达到互斥地访问资源的谜底。
②、直接相互制约(同步):某一进程若收不到另一进程给它提供的必要信息就不能继续运行下去,这种情况表明了两个进程之间在某些点上要交换信息,相互交流运行情况。这种制约关系的基本形式是“进程-进程”。
这种制约主要源于进程间的合作,同步设置在不同进程之间以达到多种进程间的同步。
区分互斥与同步只要记住,同类进程即为互斥关系,不同类进程即为同步关系。
临界资源与临界区
同时仅允许一个进程使用的资源成为临界资源。为了保证临界资源的正确使用,可以把临界资源的
访问过程分成4个部分:
进入区
|
临界区
|
退出区
|
剩余区
|
进入区:为了进入临界区使用临界资源,在进入区要检查是否可以进入临界区。如果可以进入临界区,通常设置相应的“正在访问临界区”标志,以阻止其它进程同时进入临界区。
临界区:进程中用于访问临界资源的代码,又称临界段。
退出区:临界区后用于将“正在访问临界区”标志清除的部分。
剩余区:进程中除上述3部分以外的其他部分。
注意:临界资源是一种系统资源,而临界区是代码。每个进程的临界区代码可以不相同。每个进程对临界资源进行怎样的操作,和临界资源及互斥同步管理无关。
互斥
为了禁止两个进程同时进入临界区,应遵循以下准则:
①、空闲让进:当没有进程处于临界区时,应该允许一个请求进入临界区的进程立即进入自己的临界区。
②、忙则等待:当已有进程进入其临界区时,其他试图进入临界区的进程必须等待。
③、有限等待:对要求访问临界资源的进程,应保证能在有限的时间内进入自己的临界区。
④、让权等待:当一个进程因为某些原因不能进入自己的临界区时,应释放处理器给其他进程。
互斥实现方法
标志数组flag[]表示进程是否希望进入临界区域,turn变量表示允许进入临界区的进程标识。代码如下:
enum boolean{false, true};
boolean flag[2] = {false, false};
int turn;
P0:{
do{
flag[0] = true;
turn = 1;
while(flag[1] && turn == 1);
//进程P0的临界区代码CS0;
flag[0] = false;
//进程P0的其他代码;
}while(true);
}
P1:{
do{
flag[1] = true;
turn = 0;
while(flag[0] && turn == 0);
//进程P1的临界区代码CS1;
flag[1] = false;
//进程P1的其他代码;
}while(true);
}
信号量机制
信号量是一个确定的二元组(s, q)其中s是一个有非负初值的整型变量,q是一个初始状态为空的队列。整型变量s表示系统中某类资源的数目,其值大于0时,表示系统中当前可用资源的数目;其值小于0时,其绝对值表示系统中因请求该类资源而被阻塞的进程数目。除信号量的初值外,信号量的值仅能由P操作(又称为wait操作)和V操作(又称为signal操作)改变。操作系统利用它的状态对进程和资源进行管理。
设s为一个信号量,
P(s)执行时主要完成以下动作:先执行s = s - 1; 若s >= 0,则该进程继续运行;若s<0,则阻塞该进程,并将它插入该信号量的等待队列中;
V(s)执行时主要完成以下操作:先执行s = s + 1;若s>0,则该进程继续执行;若s<=0,则从该信号量等待队列中移出第一个进程,使其变为就绪状态并插入 就绪队列,然后再返回原进程继续执行。
P操作和V操作在系统中一定是成对出现的,但未必在一个进程中,可以分布在不同进程中。
实现进程同步
假设要求S1必须在S2之前执行,使用信号量实现如下:
semaphore N = 0;
P1(){
...
S1;
V(N);
...
}
P2(){
...
P(N);
S2;
}
实现进程互斥
如下,设置信号量N,初值为1(即可用资源数为1),只需要将临界区放在P(N)和V(N)之间即可实现两进程的互斥进入。
semaphore N = 1;
P1(){
...
p(N);
P1的临界区代码;
V(N);
...
}
P2(){
...
P(N);
P2的临界区代码;
V(N);
...
}
经典同步问题
生产者-消费者问题
设置两个同步信号量:①、empty:空缓冲区数目,初始值为有界缓冲区大小n;②、full:满缓冲区数目(即产品数目),初值为0。
设置一个互斥信号量mutex,初值为1,以保证多个生产者或者多个消费者互斥地访问缓冲池。
Semaphore full = 0;
Semaphore empty = n;
Semaphore mutex = 1;
Producer(){
while(true){
Produce an item put in nextp; //nextp为临时缓冲区
P(empty);
P(mutex);
将产品放入缓冲池
V(mutex);
V(full);
}
}
Consumer(){
while(true){
P(full);
P(mutex);
取出产品;
V(mutex);
V(empty);
Consume the item in nextc; //消费掉产品
}
}
注意:P(empty)/P(full)与P(mutex)的顺序不可更改,如果生产者先P(mutex)获得对缓冲池的访问权,但是P(empty)阻塞,生产者无法向前推进,也就无法释放对缓冲区的访问权,导致此时消费者也无法取出产品,导致死锁。而V操作对这两个信号量的操作则无顺序要求。总结就是,在有多个信号量同时存在的情况下, P操作往往是不能颠倒顺序的,必须先对资源信号量进行P操作,再对互斥信号量进行P操作。
另外,只要有多个同类进程(同类进程是指使用同一个记录型信号量的进程),就一定需要互斥信号量。若同类进程只有一个,则记录型信号量即可完成进程同步。