进程同步
信号量(Semaphores)机制
1965年,荷兰学者Dijkstra提出的信号量(Semaphores)机制是一种卓有成效的进程同步工具。在长期且广泛的应用中,信号量机制又得到了很大的发展,它从整型信号量经记录型信号量,进而发展为“信号量集”机制。
信号量S是一个整数,S大于等于零是代表可供并发进程使用的资源实体数,当S小于零时则表示正在等待使用临界区的进程数。Dijkstra同时提出了对信号量操作的PV原语。
P原语操作的动作是:
(1)S减1;
(2)若S减1后仍大于或等于零,则进程继续执行;
(3)若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。
V原语操作的动作是:
(1)S加1;
(2)若相加结果大于零,则进程继续执行;
(3)若相加结果小于或等于零,则从该信号的等待队列中唤醒一个等待进程,然后再返回原进程继续执行或转进程调度。
PV操作对于每一个进程来说,都只能进行一次,而且必须成对使用。在PV原语执行期间不允许有中断的发生。
信号量机制分:整型信号量机制、记录型信号量机制、and型信号量机制、信号量集。整型信号量是一种最简单的信号量,主要用于解决并发程序互斥访问临界资源问题。记号信号量在整型信号量的举出上进行了改进,让不能进入临界区的进程“让权等待”,即进程状态有运行转换为阻塞状态,进程进入阻塞队列中等待。AND型信号量集是将进程在运行中所需要的临界资源全部一次性分配给进程,等进程用完后再全部一次释放。
1. 整型信号量
将信号量定义为一个整形量S;S<=0表示资源已被占用;S>0表示资源可用。信号量S只能通过两个标准原子操作wait(S)和signal(S)来实现,它们在执行时是不可中断的。
wait(S):
while S<=0 do; //请求资源
S:S-1;
signal(S):
S:S+1; //释放资源
//进入区执行wait(S)操作,退出区执行signal(S),例如:
var s:integer;
s:=1;
parbegin
p1:begin p2:begin
…… ……
wait(s); wait(s);
临界区代码; 临界区代码;
signal(s); signal(s);
…… ……
end; end;
parend
2. 记录型信号量
在整型信号量机制中的wait操作,只要是信号量S<=0,就会不断地测试,因此使进程进入“忙等”状态。记录型信号量机制采取了“让权等待”的策略,这时又会出现多个进程等待访问同一临界资源的情况。因此除了有一个用于代表资源数目的整型变量value外,还有一个进程链表指针,用于链接上述所有的等待进程。
记录型信号量可描述为:
type semaphore=record
value:integer;
L:list if process;
end
wait(S)可描述为:
procedure wait(S) //请求资源
var S: semaphore;
begin
S.value:=S.value-1; // 相应的资源减少一个
if S.value<0 then block(S.L) ;
//小于0,该类资源已分配完毕,进程自我阻塞,放弃处理机,并插入到信号量链表S.L中。
//此时,S.value的绝对值表示在链表中已阻塞进程的个数
end
signal(S)可描述为: //释放资源
procedure signal(S)
var S: semaphore;
begin
S.value:=S.value+1; //资源数目加一个
if S.value<=0 then wakeup(S.L);
//仍然<=0表示在链表中仍有等待资源的进程被阻塞,应唤醒链表中的第一个等待进程
end
如果S.value的初值为1,表示只允许一个进程访问临界资源,此时的信号量转化为互斥信号量,用于进程互斥。
3. ADD型信号量
当某进程要先获得多种临界资源后才能执行时,容易处于僵持状态(死锁)。AND同步机制的基本思想是:将进程在整个运行中需要的所有资源,一次性全部地分配给进程,待进程使用完后再一起释放,只要尚有一个资源未能分配给进程,其他所有可能为之分配地资源也不给它。
//Swait(Simultaneous wait)定义:
Swait(S1,S2,……Sn)
if S1>=1 and …and Sn>=1 then
for i:=1 to n do
Si=Si-1;
endfor
else
自我阻塞,插到第一个Si<1的队列Si.L,并将程序计数定位到Swait操作的起点
endif
Ssignal(S1,S2,……Sn)
if S1>=1 and …and Sn>=1 then
for i:=1 to n do
Si=Si+1;
endfor
endif
4. 信号量集
集,说明是不止一个,前面讲的记录型信号量机制中,wait(S)或signal(S)操作仅能对信号量加一或者减一,即每次只能获得或者释放一个资源,而当一次需要N个资源时,便要进行N次wait(S)操作,显然很低效;还有一种情况是,当资源低于某一下限值时不予分配,这样每次分配前都需要测试该资源的数量。
当一次需要申请n种资源,且资源Si申请di个资源,该类资源的可用数目低于某下限值ti时不予分配,这样就需要对ADD信号机制进行扩充,形成信号量集机制。
Swait(S1,t2,d1,……Sn,tn,dn)
if S1>=t1 and …and Sn>=tn then
for i:=1 to n do
Si=Si-di;
endfor
else
自我阻塞,插到第一个Si<ti的队列Si.L,并将程序计数定位到Swait操作的起点
endif
Ssignal()类似。
5. 信号量集的特殊情况
a)AND信号量是所有ti、di都等于1的特例
b)Swait(S,d,d)一个信号量,每次申请d个,低于d个不分配
c)Swait(S,1,1)退化为一般记录型信号量,但操作有差别
d)Swait(S,1,0)特殊信号量,当S>=1时,允许多个进程进入特定区;当S变位0后,将阻止任何进程进入特定区,相当于一个开关。
整型和一般记录型信号量是先减再分配,ADD和信号量集是先判断后减。
6、信号量机制的作用
1)使用信号量实现进程间的互斥
为临界资源设置一个互斥信号S,并设其初值为1,在每个进程中将临界区代码置于wait(S)和aignal(S)原语之间。
2)使用信号量实现进程间的同步
前趋关系:并发执行的进程P1和P2中,分别有代码C1和C2,要求C1在C2开始前完成。
为每个前趋关系设置一个互斥信号量S,其初值为0。
3)利用信号量实现前趋关系
当进程P1中有语句S1,进程P2中有语句S2两个并发执行希望S1执行后再执行S2,要实现这种前趋关系只需共享一个共用信号量S并赋初值0,将signal(S)放在语句S1后,而在S2之前插入wait(S)。
7、wait-signal操作讨论
1)信号量的物理含义
S>0:表示有S个资源可用
S=0:表示无资源可用
S<0:绝对值表示等待队列或链表中的进程个数
信号量的初值应大于等于0
wait(S):申请一个资源
signal(S):时放一个资源
2)wait-signal操作必须成对出现
互斥操作时,它们同处一进程
同步操作时,不在同一进程出现
3)wait-signal操作的优缺点
优点:简单而且表达能力强,可解决任何同步互斥问题
缺点:不够安全;使用不当会出现死锁;遇到复杂同步互斥问题时实现复杂
进程互斥的软件解法
1、互斥的加锁实现
2、DEKKER算法
3、PETERSON算法
4、信号量
5、管程机制
进程互斥的硬件解法
1、屏蔽中断:使每个进程在刚进入临界区后立即屏蔽所有中断,在就要离开之前再打开中断。
简单,高效。
代价高,限制CPU并发能力。
不适用于多处理器
适用于操作系统本身,不适用于用户进程
2、TSL指令:即测试并加锁指令。第一条指令将lock原来的值复制到寄存器中并将lock设置为1,然后判断这个值是否为0,即寄存器中的值是否为0。如果它非零,则说明之前已被加锁,则程序回到开始并再次测试,如果是0,则进入临界区。这种方法也需忙等待。
3、XCHG指令(交换指令):跟TSL指令相同,只不过复制操作变成原子性地交换两个位置的内容。