1.整型信号量是一个整数变量,除初始化外,对其只能执行两个操作,即wait(s)和signal(s),也叫p(s)和v(s)操作,均是原语操作,用来实现进程的同步,互斥.
2.记录型信号量
value:integer
L: list of process;
end
procedure wait(s)
var s: semaphore;
begin
s.value = s.value - 1 ;
if (s.value < 0 ) then block(s.L)
end
procedure signal(s)
var s: semaphore
begin
s.value = s.value + 1 ;
if (s.value <= 0 ) then wakeup(s.L)
end
结论:
(1) 若信号量s为正值,则等于在封锁进程之前对信号梁可以施行的p操作数,也就是s所代表的实际可以使用的物理资源数.
(2) 若信号量s为负值,则其绝对值等于排列在该信号量s队列之中等待的进程个数,也就等于对信号量s实施p操作而被封锁起来并进入信号量s队列的进程数.
(3)p操作一般代表请求一个资源,v操作一般代表着释放一个资源,在一定条件下,p操作代表挂起进程操作,v操作代表唤醒被挂起的进程.
3.经典同步问题
1)生产者/消费者问题
a) 问题一:有一个生产者和一个消费者,共享一个缓冲区.两者要互斥访问缓冲区.
解:定义两个信号量:empty表示缓冲是否为空,初值为1,即初始时可以存入一件物品.full表示缓冲区中是否有物品,初值为0,即初始时缓冲区没有物品.
begin
buffer:integer
empty,full:semaphore = 1 , 0 ;
cobegin
process Producer
begin
L1:生产一件物品
wait(empty);
buffer = product;
signal(full);
goto L1;
end
process Consumer
begin
L2:wait(full);
从缓冲区取出一件物品;
signal(empty);
消费掉物品
goto L2;
end
coend
end
注:
(1)进程互斥只需要一个信号量,而同步可能要两个信号量.
(2)p,v操作仍然要成对,但在进程进入临界区前后调用的是针对不同信号量的wait,signal操作,而进程互斥时是针对相同的信号量.
(3)至少有一个信号量的初值>=1,否则所有进程无法执行,一般是指管理是否允许访问共享资源的那个信号量.如这里的empty设为1,如果缓冲区容量为n,则可以设为n;
b)问题二:m个生产者,n个消费者,容量为r的缓冲区,(m,n,r都大于1),不要生产者和消费者互斥存取物品.
解:1)生产者和消费者之间要同步,类似问题一,用两个信号量empty,full;2)m个生产者之间要互斥,n个消费者之间也要互斥,但生产者和消费者不用互斥存取物品,因此设两个计数器in,out和相应的互斥信号量mutex1,mutex2
begin
buffer:array[ 0 r - 1 ]: integer
in , out :integer = 0 , 0 ;
empty = r,full = 0 ,mutex1 = 1 ,mutex2 = 1 :semaphore;
cobegin
process Producer - i(i = 1 , 2 m)
begin
L1:生产一件物品;
wait(empty)
wait(mutex1)
buffer[ in ] = product;
in = ( in + 1 )mod r;
signal(mutex1)
signal(full)