临界区问题的解决方案不便于推广到更为复杂的问题。为了克服这个困难,可使用称为信号量(semaphore)的同步工具。信号量S是个整数变量,除了初始化外,它只能通过两个标准原子操作wait和signal来访问。这些操作原来被称为P(用wait,表测试)和V(用于signal,表增加)。
wait的经典定义可用伪代码表示为
wait(S){
while(S<=0);
S--;
}
signal的经典定义可用伪代码表示为
signal(S){
S++;
}
忙等待浪费了CPU时钟,这本来可有效地为其他进程所使用。这种类型的信号量也称为自旋锁。自旋锁在多处理器系统中是有用的。自旋锁的优点是在进程必须等待一个锁时无需上下文切换,上下文切换可能需要花费相当长的时间。
当一个进程执行wait操作时,发现信号量值不为正,则它必须等待。然而,该进程不是忙等待而是阻塞自己。阻塞操作将一个进程放入到与信号量相关的等待队列中,且该进程的状态被切换成等待状态。接着,控制被转到CPU调度程序,以选择另一个进程来执行。
typedef struct{
int value;
struct process *L;
}semaphore;
每个信号都有一个整数值和一个进程链表。当一个进程必须等待信号量时,就加入到进程链表中。操作signal会从等待进程链表中取一个进程以唤醒。
信号量操作wait现在可按如下来定义
void wait(semaphore S){
S.value--;
if(S.value < 0){
add this process to S.L;
block();
}
}
信号量操作signal现在可按如下来定义
void signal(semaphore S){
S.value++;
if(S.value<=0){
remove a process P from S.L;
wakeup(P);
}
}