操作系统学习-6. 信号量

这篇博客详细介绍了信号量机制,包括整型信号量、记录型信号量、AND型信号量及其在进程互斥和前趋关系中的应用。通过信号量,可以有效地管理进程对共享资源的访问,防止死锁并确保进程同步。

写在前面:

这一篇博客将讨论信号量(Semaphores)机制。将学习三种基本类型的信号量,然后将用信号量实现互斥与前趋两种进程关系。

该机制由荷兰学者 Dijkstra 提出,是一种卓有成效的进程同步工具。
在系统中,给予每一个进程一个信号量,代表每个进程目前的状态,未得到控制权的进程会在特定地方被强迫停下来,等待可以继续进行的信号到来。如果信号量是一个任意的整数,通常被称为计数信号量(Counting semaphore),或一般信号量(general semaphore);如果信号量只有二进制的0或1,称为二进制信号量(binary semaphore)。在linux系统中,二进制信号量(binary semaphore)又称互斥锁(Mutex)。——Wikipedia。

整型信号量:

最初由 Dijkstra 把整型信号量定义为一个用于表示资源数目的整型量 S,它与一般整型量不同,除初始化外,仅能通过两个标准的原子操作(Atomic Operation) wait(S)和 signal(S)来访问。这两个操作一直被分别称为 P、V 操作。wait操作是使进程等待,申请资源;signal操作是使资源释放。

其实就是利用整型数这样的一个数据结构来表示进程资源是否可用:

  • 当S<=0时,资源不可用;
  • 当S>0时,资源可用。

进一步,Wait(S)与Signal(S)操作可描述为:

wait(S): while S<=0 do no-op;
          S:=S-1;

signal(S): S:=S+1

wait(S)和 signal(S)是两个原子操作,因此,它们在执行时是不可中断的。亦即,当一个进程在修改某信号量时,没有其他进程可同时对该信号量进行修改。此外,在 wait 操作中,对 S 值的测试和做 S:=S-1 操作时都不可中断。

在整型信号量机制中的 wait 操作,只要是信号量 S≤0,就会不断地测试。因此,该机制并未遵循“让权等待”的准则,而是使进程处于“忙等”的状态。换句话说,一旦一个进程需要的资源不可用,那么进程就会一直等待,直到资源可用为止,相当于在火车上,想上厕所,但厕所现在有人,那么就会一直等到里面的人出来,然后去上厕所才行。而应该退出临界区。为了解决这一问题,提出了记录型信号量。

记录型信号量:

记录型信号量机制是一种不存在“忙等”现象的进程同步机制。但在采取了“让权等待”的策略后,又会出
现多个进程等待访问同一临界资源的情况。为此,在信号量机制中,除了需要一个用于代表资源数目的整型变量 value 外,还应增加一个进程链表指针 L,用于链接上述的所有等待进程。记录型信号量是由于它采用了记录型的数据结构而得名的。它所包含的上述两个数据项可描述为:

type semaphore=record
               value: integer;
               L: list of processend

相应地,wait(S)和 signal(S)操作可描述为:

procedure wait(S)
          var Ssemaphorebegin
              S.value:=S.value-1if S.value<0 then block(S.L);
          end

procedure signal(S)
          var S: semaphore;
          begin
              S.value:=S.value+1if S.value<=0 then wakeup(S.L);
          end

在记录型信号量机制中,S.value 的初值表示系统中某类资源的数目,因而又称为资源信号量。如果 S.value 的初值为 1,表示只允许一个进程访问临界资源,此时的信号量转化为互斥信号量,用于进程互斥。当 S.value<=0 时,表示该类资源已分配完毕;当 S.value>0 时,表示该类资源可用。

每次 wait 操作,意味着进程请求一个单位的该类资源,使系统中可供分配的该类资源数减少一个,因此描述为 S.value:=S.value-1;当 S.value<0 时,表示该类资源已分配完毕,因此进程应调用 block 原语,进行自我阻塞,放弃处理机,并插入到信号量链表S.L 中。

每次 signal 操作,表示执行进程释放一个单位资源,使系统中可供分配的该类资源数增加一个,故 S.value:=S.value+1 操作表示资源数目加 1。若加 1 后仍是 S.value≤0,则表示在该信号量链表中,仍有等待该资源的进程被阻塞,故还应调用 wakeup 原语,将 S.L 链表中的第一个等待进程唤醒。

关于block与wakeup原语可参考:操作系统学习-4. 进程控制

AND型信号量:

上述的进程互斥问题,是针对各进程之间只共享一个临界资源而言的。在有些应用场合,是一个进程需要先获得两个或更多的共享资源后方能执行其任务。然而这些时候如果仍采用记录型信号量,会导致两个进程各占用一部分的共享资源,从而谁也不让谁,导致“死锁”。

例如:现有两个进程 A和 B,他们都要求访问共享数据 D 和 E。可为这两个数据分别设置用于互斥的信号量 Dmutex 和 Emutex,并令它们的初值都是 1。。相应地,在两个进程中都要包含两个对 Dmutex 和 Emutex 的操作,即:

process A: wait(Dmutex);wait(Emutex);
process B: wait(Emutex);wait(Dmutex);

若进程 A 和 B 按下述次序交替执行 wait 操作:

process A: wait(Dmutex); //于是 Dmutex=0
process B: wait(Emutex); //于是 Emutex=0
process A: wait(Emutex); //于是 Emutex=-1 A 阻塞
process B: wait(Dmutex); //于是 Dmutex=-1 B 阻塞

最后,进程 A 和 B 处于僵持状态。在无外力作用下,两者都将无法从僵持状态中解脱出来。我们称此时的进程 A 和 B 已进入死锁状态。显然,当进程同时要求的共享资源愈多时,发生进程死锁的可能性也就愈大。

宁可锦上添花,也不雪中送炭:
AND 同步机制的基本思想是:将进程在整个运行过程中需要的所有资源,一次性全部地分配给进程,待进程使用完后再一起释放。只要尚有一个资源未能分配给进程,其它所有可能为之分配的资源也不分配给它。亦即,对若干个临界资源的分配,采取原子操作方式:要么把它所请求的资源全部分配到进程,要么一个也不分配。由死锁理论可知,这样就可避免上述死锁情况的发生。

在 wait 操作中,增加了一个“AND”条件,故称为AND 同步,或称为同时 wait 操作,即Swait(Simultaneous wait)定义如下:

Swait(S1,S2,…,Sn)
        if S1>=1 andand Sn>=1 then
            for i:=1 to n do
                Si:=Si-1;
            endfor
        else
            当发现第一个Si<1就把该进程放入等待队列,并将其程序计数器置于Swait操作的开始位置。
        endif

同样的,同时signal操作,即Ssignal(Simultaneous signal)定义如下:

Ssignal(S1,S2,…,Sn)
        for i:=1 to n do
            Si:=Si+1;
        将所有等待Si的进程由等待队列取出放入到就绪队列。
        endfor;

这里有涉及到程序计数器的概念,程序计数器(Program Counter,PC)是用于存放下一条指令所在单元的地址的地方。当执行一条指令时,首先需要根据PC中存放的指令地址,将指令由内存取到指令寄存器中,此过程称为“取指令”。与此同时,PC中的地址或自动加1或由转移指针给出下一条指令的地址。此后经过分析指令,执行指令。完成第一条指令的执行,而后根据PC取出第二条指令的地址,如此循环,执行每一条指令。

利用信号量实现进程互斥:

为使多个进程能互斥地访问某临界资源,只须为该资源设置一互斥信号量 mutex,并设其初始值为 1,然后将各进程访问该资源的临界区 CS 置于 wait(mutex)和 signal(mutex)操作之间即可。

关于访问临界资源的循环进程可参考:操作系统学习-5. 进程同步

利用信号量实现进程互斥的进程可描述如下:

Var mutex: semaphore:=1;
    begin
        parbegin
            process 1: begin
                           repeat
                               wait(mutex);
                               critical section
                               signal(mutex);
                               remainder seetion
                           until falseend
            process 2: begin
                           repeat
                               wait(mutex);
                               critical section
                               signal(mutex);
                               remainder section
                           until falseend
        parend
    end

每个欲访问该临界资源的进程在进入临界区之前,都要先对 mutex 执行wait 操作,若该资源此刻未被访问,本次 wait 操作必然成功,进程便可进入自己的临界区;这时若再有其他进程也欲进入自己的临界区,此时由于对 mutex 执行 wait 操作定会失败,因而该进程阻塞,从而保证了该临界资源能被互斥地访问。

当访问临界资源的进程退出临界区后,又应对 mutex 执行 signal 操作,以便释放该临界资源。

在利用信号量机制实现进程互斥时应注意,wait(mutex)和 signal(mutex)必须成对地出现。
缺少 wait(mutex)将会导致系统混乱,不能保证对临界资源的互斥访问;
缺少 signal(mutex)将会使临界资源永远不被释放,从而使因等待该资源而阻塞的进程不能被唤醒。

用信号量实现前趋关系:

设有两个并发执行的进程 P1 和 P2。P1 中有语句 S1;P2 中有语句 S2。我们希望在 S1 执行后再执行 S2。

为实现这种前趋关系,我们只须使进程 P1 和 P2 共享一个公用信号量 S,并赋予其初值为 0,将 signal(S)操作放在语句 S1 后面;而在 S2 语句前面插入 wait(S)操作,即:

  • 在进程 P1 中,用 S1;signal(S);
  • 在进程 P2 中,用 wait(S);S2;

由于 S 被初始化为 0,这样,若 P2 先执行必定阻塞,只有在进程 P1 执行完 S1;signal(S);操作后使 S 增为 1 时,P2 进程方能执行语句 S2 成功。

同样,我们可以利用信号量,按照语句间的前趋关系(见下图),写出一个更为复杂的可并发执行的程序。


图1.前趋图举例
这里写图片描述


图中 S1,S2,S3,…,S6 是最简单的程序段(只有一条语句)。

为使各程序段能正确执行,应设置若干个初始值为“0”的信号量。如为保证 S1→S2,S1→S3的前趋关系,应分别设置信号量 a 和 b,同样,为了保证 S2→S4,S2→S5,S3→S6,S4→S6和 S5→S6,应设置信号量 c,d,e,f,g。进程描述如下:

Var a,b,c,d,e,f,g:semaphore: =0,0,0,0,0,0,0begin
        parbegin
            begin 
                S1; 
                signal(a); signal(b); 
            endbegin 
                wait(a); 
                S2; 
                signal(c); signal(d); 
            endbegin 
                wait(b); 
                S3; 
                signal(e); 
            endbegin 
                wait(c); 
                S4; 
                signal(f); 
            endbegin 
                wait(d); 
                S5; 
                signal(g); 
            endbegin 
                wait(e); wait(f); wait(g); 
                S6; 
            end;
        parend
    end
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值