进程同步、进程互斥的两种机制,这里简单总结是其中的信号量机制(Semaphores)。数据结构
建议: 很多概念涉及到进程同步的内容,因此查看这个内容时能够结合或提早参考进程同步的内容,并发
信号量机制是 荷兰学者 Dijkstra 提出的,这是一种卓有成效的进程同步工具。发展:整型信号量->记录型信号量->AND 型信号量->信号量集,依次讲解下。工具
定义:把整型信号量定义为一个用于表示资源数目的整型量 S,它与通常整型 量不一样,除初始化外,仅能经过两个标准的原子操做(Atomic Operation) wait(S)和 signal(S)来访问。this
PV操做由P操做原语和V操做原语组成(原语是执行时不可中断,PV操做即定义中的原子操做wait(S)和signal(S))。所以这个操做一般被叫作PV操做。spa
注:在荷兰文中,经过叫passeren,释放叫vrijgeven,PV操做所以得名。wait(S)即申请资源,signal(S)释放资源。指针
描述:code
wait(S) { while(S <= 0); S--; } signal(S) { S++; }
不足:当S<=0时,就要不断检测。陷入“忙等”状态,不符合让权等待。(在进程同步总结中有详细说明)blog
在整型信号量基础上,增长了一个进程链表指针L,连接全部等待的进程。 队列
数据结构定义描述以下:一个整型变量value表示资源数目,进程链表指针L。进程
typedef struct{ int value; struct process *L; } semaphore;
相应的,wait(S)和signal(S)操做过程就是:
当wait(S)申请资源时,S.value减1(为负数时的绝对值即等待进程的数目)。若S.value<0时表示资源耗尽了,进程使用block原句 自我阻塞,放弃处理机,插入到等待进程链表中S.L。这就符合了让权等待,避免了“忙等”。
当signal(S)释放资源,S.value加1。若加 1 后还是 S.value≤0,则表示在该链表L中,仍有等待该资源的进程被阻塞,故还应 调用 wakeup 原语,将 S.L 链表中的第一个等待进程唤醒。
若是 S.value 的初值为 1,表示只容许一个进程访问临界资源,此时的信号量转化为互斥信号量,用于进程互斥。
void wait(semaphore S) { S.value--; if(S.value<0) { block(S.L); } } void signal(semaphore S) { S.value++; if(S.value<=0){ wakeup(S.L); } }
不足:同整型信号量同样,都是正对的是共享一种临界资源。 若一个进程须要两个或更多资源后才可执行,就会出现死锁的可能,共享资源越多,进程死锁可能性越大。
好比:有两个进程A、B。两个临界资源D、E,互斥信号量(初值为1)分别是Dmutex、Emutex。
按下面执行次序,A得到了D等待E,B得到了E等待D,就处于了僵持状态,无外界干预,A、B就陷入了死锁状态。共享资源越多,进程死锁可能越大。
process A: wait(Dmutex); 因而 Dmutex=0
process B: wait(Emutex); 因而 Emutex=0
process A: wait(Emutex); 因而 Emutex=-1 A 阻塞
process B: wait(Dmutex); 因而 Dmutex=-1 B 阻塞
如上所述,整型信号量和记录型信号量当共享多种临界资源时都容易引发死锁问题。AND型信号量可以避免上述死锁状况的出现。
AND同步机制:要么把进程在整个运行过程当中所请求的资源所有分配到进程(进程使用完成后一块儿释放),要么一个也不分配。
描述:
wait():申请资源S1-Sn,当碰到Si不知足后,则执行else里,将进程加入到Si关联的等待队列中 而进程指针移向wait()开始。wait()是原语,不可中断。
wait(S1,S2,...,Sn) { if(S1>=1 and … and Sn>=1) { for(i:=1 to n) Si--; } 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 } }
signal():释放资源S1-Sn,将S1-Sn关联的等待队列清空 并调入就绪队列。
signal(S1,S2,...,Sn) { for(i:=1 to n) { Si++; Remove all the process waiting in the queue associated with Si into the ready queue. } }
不足:整型信号量、记录型信号量局限在只共享一种临界资源。AND型信号量局限在单种(一种)临界资源为1,若单种资源为N(N>=1)时,AND型信号量的wait()和signal()就需操做N次,这是很低效的。
但不少状况是一个进程可能申请多种临界资源且某种临界资源数目大于1的。
如上所述。当须要单种资源为N(N>=1),AND型操做 wait()和signal()须要操做N次,低效。
除低效问题,还考虑情景:当某种资源数低于某一下限时变不在分配,所以每次都需检测该资源数是否大于下限值。
从而衍生出了信号量集:
操做可描述以下,其中 S 为信号量,d 为需求值,而 t 为下限值。
wait():申请S1-Sn, Si须要di个,而资源Si下限为ti。当S1-Sn中,Si不知足下限值ti后,就不能分配了。进入else,进程加入到等待队列,指针移向wait()开始位置。
wait(S1,t1,d1,…,Sn,tn,dn) { if (S1>=t1 and … and Sn>=tn){ for (i:=1 to n) Si:=Si-di; } else{ Place the executing process in the waiting queue of the first Si with Si<ti and set its program counter to the beginning of the wait Operation. } }
signal():释放资源S1-Sn,Si释放di个,S1-Sn关联的等待队列清空 并调入就绪队列。
signal(S1,d1,…,Sn,dn) { for i:=1 to n do { Si:=Si+di; Remove all the process waiting in the queue associated with Si into the ready queue } }
“信号量集”的几种特殊状况:
(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 后,将阻止任何进程进入特定区。换言之,它至关于一个可
控开关。
为使多个进程能互斥地访问某临界资源,只须为该资源设置一互斥信号量 mutex,并设其初始值为 1,而后将各进程访问该资源的临界区 置于 wait(mutex)和 signal(mutex)操做之间便可。
Var mutex: semaphore:=1;
process 1: begin repeat wait(mutex); critical section signal(mutex); remainder seetion until false; end process 2: begin repeat wait(mutex); critical section signal(mutex); remainder section until false; end
在利用信号量机制实现进程互斥时应注意,wait(mutex)和 signal(mutex)必须成对地出现。 缺乏 wait(mutex)将会致使系统混乱,不能保证对临界资源的互斥访问;而缺乏 signal(mutex)将会使临界资源永远不被释放,从而使因等待该资源而阻塞的进程不能被唤醒。
设有两个并发执行的进程 P1 和 P2。P1 中有语句 S1;P2 中有语句 S2。
咱们但愿在 S1 执行后再执行 S2。为实现这种前趋关系: S1->S2。
咱们只须使进程 P1 和 P2 共享一个公用信号量 S,并赋予其初值为 0,将 signal(S)操做放在语句 S1 后面;而在 S2 语句前面插入 wait(S)操做,即
在进程 P1 中
S1;
signal(S);
在进程 P2 中
wait(S);
S2;
因为 S 被初始化为 0,这样,若 P2 先执行一定阻塞,只有在进程 P1 执行完 S1;signal(S);操做后使 S 增为 1 时,P2 进程方能执行语句 S2 成功。
这是最基础的实现,在此基础上能够实现更多进程间更复杂的先后驱关系,须要多个公共信号量协同操做。