信号量S
(Semaphore)是一个结构体变量,它有两个数据项:一个是值域,另一个是指针域。值域用于保存信号量的值,指针域用于保存指向等待该信号量队列的指针。
# 信号量定义
struct semaphore
{
int value;
struct PCB* queue;
};
信号量是由操作系统来维护的,用户进程只能通过初始化和两个标准原语,即P原语和V原语来对它进行访问。所谓原语
,其实就是一个函数,它通常由若干条语句组成,用来实现某个特定的操作。与普通函数的区别在于:它是由操作系统提供的,而且在执行过程中不会被中断所打断。原语是操作系统内核的一个组成部分,它必须在管态下执行。
# P操作
void P(semaphore S)
{
S.value--;
if(S.value < 0)
block(S.queue); // 将进程阻塞,并将其插入等待队列
}
# V操作
void V(semaphore S)
{
S.value++;
if(S.value<=0)
wakeup(S.queue); // 唤醒阻塞进程,将其从等待队列取出,插入就绪队列
}
信号量的物理意义:
-
在信号量机制中,信号量的初值S.value标识系统中某种资源的数目,因而又称为资源信号量。
-
P操作意味着进程请求一个资源,因此描述为
S.value=S.value-1
;当S.value<0
时,表示资源已经分配完毕,因而进程所申请的资源不能够满足,进程无法继续执行,所以进程执行block(S.queue)
自我阻塞,放弃处理机,并插入到等待该信号量的等待队列。 -
V操作意味着进程释放一个资源,因此描述为
S.value=S.value+1
;当S.value<0
时,表示在该信号量的等待队列中有等待该资源的进程被阻塞,故应调用wakeup(S.queue)
原语将等待队列中的一个进程唤醒。 -
当
S.value<0
时,|S.value|
表示等待队列的进程数。
用信号量解决互斥问题
如果信号量的初值为1,表示仅允许一个进程访问临界区,此时的信号量转换为互斥信号量。P操作和V操作分别置于进入区和退出区便可实现多个进程互斥进入临界区。
# P1、P2两个进程共享变量count
设P1和P2共享信号量为mutex,且mutex的初值为1。
void P1() void P2()
{ {
... ...
P(mutex); P(mutex);
N = count; M = count;
N = N + 100; M = M + 200;
count = N; count = M;
V(mutex); V(mutex);
... ...
} }
void main()
{
semaphore mutex = 1;
cobegin
P1();
P2();
coend
}
用信号量解决同步问题
利用信号量可以实现进程之间的同步,即可以控制进程执行的先后次序。根据信号量的初值,在某个进程的程序段前加P操作,在另一个的程序段后加V操作即可。
# 有两个进程P1和P2,进程P1中有一个语句S1,进程P2中有一个语句S2。
# 要求必须在P1执行完S1语句后,P2才能执行S2。
设P1和P2共享信号量S,且S的初值为0。
void P1() void P2()
{ {
... ...
S1; P(S);
V(S); S2;
... ...
} }
void main()
{
semaphore S = 0;
cobegin
P1();
P2();
coend
}
用信号量同时解决同步与互斥问题
利用信号量机制可以在实现进程同步的同时也实现进程互斥,即在控制进程执行的先后次序的同时,也实现了进程互斥使用共享变量或共享资源。
# 供者进程L1将读卡机中的数据放入缓冲区,用者进程L2从缓冲区中读取数据送打印机。
设置两个信号量。S1:表示缓冲区是否为空(0表示不空,1表示空)、S2:表示缓冲区是否满(0表示不满,1表示满)
置初值:S1=1,S2=0
void L1() void L2()
{ {
do{ do{
... ...
P(S1); P(S2);
启动读卡机; 从缓冲区取出信息;
读入数据至缓冲区; V(S1);
收到输入结束中断; 加工并且存盘;
V(S2); }while(true);
}while(true); }
}
void main()
{
semaphore S1=1, S2=0;
cobegin
L1();
L2();
coend
}
(最近更新:2019年09月18日)