Skip to content

LV005-信号量简介

一、信号量

其实前边我们在学习进程通信的时候,会不会有一个疑问,就是多个进程同时写一块区域的时候咋办?或者说那个进程在写,这个来读,读到的数据会不会就有问题呢?进程中没有像线程的互斥锁这些东西来保护共享的资源,但是我们可以使用信号量来达到相同的效果。

1. 信号量简介

信号量是一个计数器,与其它进程间通信方式不大相同,它主要用于控制多个进程间或一个进程内的多个线程间对共享资源的访问,相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同时,进程也可以修改该标志,除了用于共享资源的访问控制外,还可用于进程同步。

常被作为一种锁机制,防止某进程在访问资源时其它进程也访问该资源,因此,信号量主要作为进程间以及同一个进程内不同线程之间的同步手段,而不是用于缓存进程间通信的数据。

2. 信号量的操作

信号量代表某一类资源,其值表示系统中该资源的数量,只能通过三种操作来访问它:

  • (1)初始化

  • (2)P操作(申请资源)

  • (3)V操作(释放资源)

【注意事项】P 操作是用在进入共享资源之前,V 操作是用在离开共享资源之后,这两个操作是必须成对出现的。

2.1 P 操作(申请资源)

P操作其实就是将信号量减去 1,相减后如果信号量< 0,则表明资源已被占用,没有资源可用,进程需阻塞等待,进入等待队列;相减后如果信号量 >= 0,则表明还有资源可使用,进程可正常继续执行。也就是

c
信号量的值 - 1;
if(信号量的值 ≥ 0) 
{  
 	申请资源的任务继续运行;
} 
else 
{   
	申请资源的任务阻塞,并插入等待队列;
}

2.2 V 操作(释放资源)

V 操作会把信号量加上 1,相加后如果信号量 <= 0,则表明当前有阻塞中的进程,于是会从等待队列中取出一个进程唤醒运行;相加后如果信号量 > 0,则表明当前没有阻塞中的进程。也就是

c
信号量的值 + 1;
if (信号量的值 <= 0) 
{   
	唤醒等待队列的一个任务,让其继续运行;
}

3. 信号量种类

一共有三种信号量,分别是POXIX中的无名信号量和有名信号量,以及System V信号量。

  • POSIX中的无名信号量主要用于线程间的通信,保存在内存中,如果想要在进程间同步就必须把无名信号量放在进程间的共享内存中。

  • 而在进程间的通信中同步用的通常是有名信号量,有名信号量一般保存在/dev/shm/ 目录下,它就像文件一样存储在文件系统中。

  • System V 信号量是一个或多个计数信号量的集合,可同时操作集合中的多个信号量,并且申请多个资源时可以避免死锁。

二、互斥信号量

上边的已经了解了信号量的操作,那具体是怎样工作的呢?比如我们现在有两个进程:进程1和进程2,还有一个信号量sem,我们将信号量sem初始化为1然后进程1和进程2都要访问共享内存。

2

进程 1在访问共享内存前,先执行了 P 操作,信号量sem的初始值为 1,故在进程 1 执行 P 操作后信号量sem变为 0,表示共享内存可用,于是进程 1 就可以访问共享内存。

进程1正在访问共享内存,进程 2 也想访问共享内存,执行了 P 操作,会使信号量sem变为 -1,这就意味着共享内存已被占用,因此进程 2 被阻塞。

当进程 1 访问完共享内存,才会执行 V 操作,使得信号量sem恢复为 0,接着就会唤醒阻塞中的线程 2,使得进程 2 可以访问共享内存,最后完成共享内存的访问后,执行 V 操作,信号量sem恢复到初始值 1

由此我们知道,信号初始化为 1,就代表着是互斥信号量,它可以保证共享内存在任何时刻只有一个进程在访问,这就达到了保护了共享内存的目的。

三、同步信号量

前边我们在学习线程的时候,有一个生产者与消费者的问题,生产者生产了产品后,消费者才能使用产品,进程之间是否也可以这样呢?我们假设有两个进程,分别为进程1和进程2,进程1负责生产产品,进程2负责使用产品,初始的信号量sem我们初始化为0

2

如果进程 2 比进程 1 先执行,那么执行到 P 操作时,而信号量sem初始值为 0,所以信号量sem会变为 -1,表示进程 1 还没生产产品,于是进程 2 就阻塞等待;

当进程 1 生产完产品后,执行了 V 操作,就会使得信号量sem变为 0,于是就会唤醒阻塞在 P 操作的进程 2

进程 2 被唤醒后,意味着进程 1 已经生产了数据,于是进程 2 就可以正常使用产品啦了。

由此可知,信号初始化为 0,就代表着是同步信号量,这样可以保证进程 1 应在进程 2 之前执行。