操作系统中的 PV操作原理和信号量

时间:2022-05-27 15:17:37

转自:http://blog.csdn.net/gnuhpc/article/details/7001865

PV操作原语和信号量sem是计算机操作系统进程和线程同步的核心手段,虽然说起来只有句话,但有几个点非常容易引起模糊。先把PV操作的说明如下:

P操作原语:

  1. sem 减1

  2. 若sem 大于等于0,线程继续执行.

  3. 若sem < 0 ,线程进入阻塞队列.

V 操作原语:

  1. sem加1

  2. 若sem 大于 0, 线程继续执行

  3. 若sem 小于等于0,唤醒阻塞队例的线程

问题1.原语概念
教 科书上一般定义是不允许发生中断,一段必须连续执行的指令。在Dijkstra发明原语概念的时代,CPU应该都是单核的,多线程是单个CPU分时间片执 行,所有只要是保证是” 连续执行”,就不会发生执行一个线程P操作的sem减1后,另一个线程V操作sem 加1。这意味着可以在现有的程序层面就可以实现多线程的同步而不需要别的指令或硬件。但在现在的多核时代,完全有可能在一个核上执行P操作sem减1后, 另一个核上执行V操作sem加1,修改相同的地址空间,从而破坏sem的状态. 这方面.NET和Java都提供了volatile关键字,估计是添加的新的指令来保证,不过还好,这是操作系统操心的事情,操作系统保证原语概念的完整 性。

问题2. 信号量

信号量sem在PV操作里具有三元性质.

让我们来看一个具体的实例: 假设现在的执行场景是有三个线程A,B,C进入一个信号量为1的临界资源,

  1. 当线程A进入时,执行P操作, sem=0,线程A继续执行.

  2. 当线程B进入时,线程A假设仍在占用临界资源, B执行P操作,sem = -1,B进入等待队列。

  3. 当线程C进入时,线程A假设仍在占用临界资源, C执行P操作,sem = -2,C进入等待队列

通过以上的步骤可以发现:

1) 当sem > 0 时,表示可用的临界资源的数量

2) 当sem = 0时,表示无可用的资源,也没有阻塞的线程

3) 当sem <0 时,sem的绝对值表示阻塞队列的线程数

问题3.对V操作2,3步骤的解释

V操作的第2步,sem > 0 ,线程继续执行.有些人这个地方不解,sem > 0不是代表有资源可用吗?为什么不是唤醒其它的线程。其实正是sem >0 ,sem当前的意义是有资源可用,而没有阻塞的线程,所有根本不需要唤醒.

V操作的第3步,sem 小于等于0, 唤醒阻塞队例的线程. sem < 0很多人直觉认识都是没资源可用了,干吗还唤醒阻塞队列的线程(包括我)。通过问题2的分析,在这个时候sem代表的是阻塞队列的线程数 ,所以需要唤醒. 让我们接着问题2的步骤.

4) 线程A资源使用完,执行V操作,sem = -1, 唤醒阻塞队列的线程(假设FIFO),B线程进入临界资源开始执行,注意,B不会再执行P操作,因为进入阻塞队列前已执行.P,V操作必须成对执行。

5) B使用完资源后,执行V操作,sem = 0; 唤醒阻塞队列的线程,C线程进入临界资源开始执行,C也不会再执行P操作

6) C使用完资源,执行V操作, sem = 1

PV操作由P操作原语和V操作原语组成(原语是不可中断的过程)。对信号量进行操作,具体定义如下:

P(S):
①将信号量S的值减1,即S = S - 1;
②如果s >= 0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):
①将信号量S的值加1,即S = S + 1;
②如果S > 0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
PV操作的意义:我们用信号量及PV操作来实现进程的同步和互斥。PV操作属于进程的低级通信。
信 号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时, 表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的值仅能由PV操作来改变。
一般来说,信号量S >= 0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的 进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S <= 0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。也就是说,有一个未被占用的资源就可以让一个阻塞的进程执行,而不是S为正是才可以执行。

个人附加:
进程(线程)之间的两种关系:同步与互斥。

所谓互斥,是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行,各程序片段的运行次序没有要求。

所谓同步,是指散布在不同进程之间的若干程序片断,它们的运行必须严格按照规定的先后次序来运行,这种先后次序依赖于要完成的特定的任务。  

显然,同步是一种更为复杂的互斥,而互斥是一种特殊的同步。也就是说互斥是两个线程之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)! 

总结:
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。  

同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。