生产消费模型是信号量的最典型的应用实例。然而实际操作系统中P、V操作并不是如理想模型那样方便使用的,这里以Linux为例描述了生产、消费模型的实现方法,同时涉及了Linux下共享内存、信号量的基本操作。如有不正确之处,请大家指正,谢谢!
一、Linux下信号量的建立
要建立一个信号量,首先要给信号量一个主键,即给信号量命名。Linux下的信号量与Windows有很大的不同,Windows以一个字符串来命名一个信号量,而Linux则以一个t_key类型的数值命名一组信号量。为了方便调试一般我们会在公共头文件中,以宏定义的方式进行信号量的命名,比如
#define KEY_MUTEX ((key_t) 403040914 )
Linux下信号量集合的新建使用如下库函数
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
其中key就是前文所说的主键,nsems是集合中包含的元素个数,semflg则是用于新建信号量的重要参数,比如访问控制权限。返回值是以key为主键的信号量标号,若返回-1说明产生了操作错误。具体参数的说明请看http://www.opengroup.org/onlinepubs/007908799/xsh/semget.html。
为了方便理解与操作,这里我们需要建立的是仅有一个元素的公有信号量集。代码如下
id = semget(KEY_MUTEX, 1, IPC_CREAT|0660);
其中,一定要注意0660最高位的0不能省略,因为这是一个八进制数,代表rw-rw----的访问控制。
二、信号量的初始化
信号量建立之后必须要进行初始化,在Linux信号的基本控制使用统一的API函数:
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
其中semnum是集合中元素的序号(以0开始计算);cmd则是需要进行的控制命令,在sys/sem.h 头文件中有宏定义,我们使用的是设置值SETVAL;附加参数可以接受如下的联合体(需要用户自行定义)。具体说明见http://www.opengroup.org/onlinepubs/007908799/xsh/semctl.html。
union semun
{
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* array for GETALL, SETALL */
/* Linux specific part: */
struct seminfo *__buf; /* buffer for IPC_INFO */
};
这里我们假设要将信号量集中第一个元素(也是唯一一个元素)赋初值1,使用如下代码
union semun un;
un.val = 1;
semctl(id, 0, SETVAL, un);
三、信号量的PV操作
在教科书里PV操作是两个完全不同的操作,但是在Linux系统中PV操作则在形式上统一为一个API函数。而且由于Linux下信号量集合的特点,一个调用可以同时作用于多个信号量元素中。函数原型如下:
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
其中sops是一个sembuf结构体类型的数组,数组的每一个元素就是对一个信号量的操作,nsops则指出了数组的长度(操作的个数)。此结构体有三个成员
short sem_num 信号量元素序号(0起始)
short sem_op 操作数值
short sem_flg 操作标记
对于P操作有sem_op=-1,对于V操作有sem_op=1。操作之后,若信号量小于零则semop()调用被挂起直到大于等于0时才返回。我使用wait()和release()两个函数分别实现了P操作和V操作:
void wait(int id)
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = -1;
buf.sem_flg = SEM_UNDO;
semop(id, &buf, 1);
}
void release(int id)
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = 1;
buf.sem_flg = SEM_UNDO;
semop(id, &buf, 1);
}
至于信号量的删除,亦是使用semctl()调用,但是由于涉及收到SIGKILL信号后的处理所以不作详细介绍了。