📜  操作系统中的信号量解决方案

📅  最后修改于: 2022-05-13 01:56:11.728000             🧑  作者: Mango

操作系统中的信号量解决方案

信号量可以描述为由计数器、进程等待列表、信号和等待函数组成的对象。信号量最基本的用途就是将其初始化为1。当一个线程要进入临界区时,它会向下调用并进入该区。当另一个线程尝试做同样的事情时,操作系统会将其置于睡眠状态,因为信号量的值已经为零,因为之前调用了 down。当第一个线程完成临界区时,它会调用,这会唤醒等待进入的另一个线程。

从逻辑上讲,信号量 S 是一个整数变量,除了初始化之外,它只能通过两个原子操作访问:

  • Wait(S) 或 P :如果信号量值大于 0,则递减该值。否则,等到值大于 0 后再递减。
  • Signal(S) or V :增加信号量的值

忙等待信号量解决方案:

如果一个进程处于临界区,则试图进入其临界区的另一个进程必须在入口代码中不断循环。等待和信号的经典定义是——

wait (S) {
while (S<=0);
S--;
}
signal (S) {
S++;
}

实现信号量: n个进程问题的关键部分

Shared Data : semaphore mutex ;   // initially mutex=1
Process P :
do {
wait (mutex) ;

signal (mutex) ;

} while (1)

带阻塞和唤醒的信号量解决方案:
在忙等待问题中,进程在等待进入其临界区时会浪费 CPU 周期。将等待操作修改为块操作。该进程可以自己阻塞而不是等待总线。将进程放入与临界区关联的等待队列中。将信号操作修改为唤醒操作。将进程的状态从等待更改为就绪。

当进程执行等待操作,发现信号量值不是正数时,进程可以阻塞自己。块操作将进程置于与信号量相关的等待队列中。当另一个进程执行信号操作时,应该重新启动一个被阻塞等待信号量的进程。被阻塞的进程应该通过唤醒操作重新启动,该操作将该进程置于就绪队列中。

为了实现信号量,我们将信号量定义为记录:

typedef struct {
int value ;
struct process *L ;
} semaphore ;

假设两个操作:

  • block :暂停调用它的进程。
  • wakeup (P) :恢复被阻塞进程的执行 (P)

信号量操作定义为:

wait (S) {
S.value -- ;                 
if ( S.value < 0 ) {
add this process to S.L ;
block ;
}  }
signal (S) {
S.value ++ ; 
if ( S.value <= 0 ) {
removes a process P from S.L ;
wakeup (P) ;
} }

好处 :

  • 信号量易于实现且独立于机器
  • 更正很容易确定
  • 信号量同时获取许多资源
  • 可以有许多具有不同信号量的不同临界区
  • 不因忙等待而浪费资源

缺点 :

  • 可以从程序中的任何地方访问信号量
  • 信号量与信号量控制访问的数据之间没有语言联系
  • 信号量使用不当会导致死锁或饥饿
  • 无法控制或保证正确使用