以下用到的消息队列中的相关内容,均可在这里找到:https://blog.youkuaiyun.com/sandmm112/article/details/79936107
信号量是进程间通信的有一种方式。之前有提到共享内存是不带同步与互斥机制的,这样在多个进程同时对共享内存写入数据时,会导致数据错乱。而信号量就是确保多个进程在对共享内存这样的共享资源同时读写时,使之实现互斥与同步的,下面,在具体介绍一下互斥与同步的概念。
互斥:当多个进程竞争同一资源时,这些资源一次只能用于一个进程,所以这些资源只能互斥使用。比如说两个进程都要向显示器上输出内容,但同一时刻显示器只能被一个进程使用,此时,显示器就叫做临界资源或互斥资源。这两个进程之间的关系就叫做互斥。而两个进程中涉及到临界资源的代码段就叫做临界区。
同步:多个进程在执行顺序上存在着遵循着一定的关系。对于某临界资源来说,只能一个进程使用完了,另一个进程才能使用。
一,信号量
信号量本质上可以理解为一个计数器,记录临界资源的个数的计数器。比如说上面的显示器资源只有一个,那么此时记录显示器资源的信号量的值即为1。
称信号量值为1的为二元信号量,也叫互斥锁。
信号量的P操作:
假设有一个二元信号量即它维护的临界资源个数只有一个。当一个进程要使用临界资源(如显示器资源)时,对记录该临界资源的信号量的值减1。当另一进程也要使用该临界资源时,因此临界资源的数目此时为0,所以该进程必须挂起等待,将该进程的PCB放入等待队列中。
信号量的V操作
当进程使用完临界资源时,信号量加1。如果此时在等待队列中有等待的进程,就唤醒该进程将临界资源分配给他使用。
通过以上描述得知,一个信号量不仅包含临界资源的数目,还必须维护一个队列。所以,信号量的结构体伪代码如下:
struct semaphore
{
int value;//信号量的值,即它维护的临界资源的数目
pointer_PCB queue;//等待队列
}
当多个进程要使用同一临界资源时,都要向维护该资源的信号量进行申请,所以,多个进程都必须能看到这个信号量资源,所以信号量也是一个临界资源。
信号量想要维护临界资源使其满足互斥与同步机制,而信号量本身又是临界资源,所以它自己必须得满足互斥与同步,才能维护它的临界资源。所以,对信号量的操作必须是原子的。
原子操作:访问资源时要么全做,即把一整套流程全部做完,要么不做,不会有做到一半就停下或被打扰的状态。
因此,上述信号量的P-V操作都必须是原子的(此时,临界资源的数目不一定为1),它们可分别概括为:
P-操作:
P(s)//s为信号量维护的临界资源
{
s.value--;
if(s.value <= 0)
{
//使申请临界资源的进程挂起等待,它的PCB放入等待队列s.queue中
}
}
V-操作:
V(s)
{
s.value++;
if(s.value > 0)
{
//唤醒等待队列s.queue中的进程,给它分配临界资源,使其状态变为就绪态
}
}
二,信号量集
维护一种临界资源需要一个信号量,那维护多种临界资源就需要多个信号量。多种信号量组成一个信号量集。信号量集可以看做是计数器的个数,信号量值可看作计数器的个数。信号量集是以数组形式进行组织的,以下标来提取各个信号,数组元素表示信号量的值即临界资源的数目。
在对信号量进行操作时,都是以信号量集为单位进行的。比如要对显示器资源进行维护,此时就需要一个信号量集,该集中只有一个信号量,所以其下标为0即可。
以下为信号量集的操作函数:
1. 创建和访问一个信号量集
int semget(key_t key,int nsems,int semflg);//头文件:<sys/types.h>,<sys/ipc.h>,<sys/sem.h>
参数:
key:由ftok函数返回的key值,与消息队列中的用法相同。
nsems:信号量集中的信号两个数
semflg:可选参数为IPC_CREAT和IPC_EXCL,用法与消息队列中的相同。
返回值:成