摘自[url]http://blog.chinaunix.net/u/24174/showart_220598.html[/url]


7、信号灯

信号灯也可以说是一种锁,但它可以用来控制除了文件以外的更多资源。信号灯的初始值一般为一个正数,决定了可以分配的资源数,为进程分配一个资源后自减,减到0后被锁住。SysV IPC要求信号灯必须定义为一个集合。创建信号量时则指定此集合中的值。
双态信号灯是最简单的一种,0表示锁定,无资源;1表示解锁,有一个可用资源。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int flags);

semget通过key(IPC_PRIVATE等)创建nsems个(集合)新的或者访问一个(nsems为0)已经存在的信号灯,并使用flags(IPC_CREAT、8进制访问模式等)控制它的行为。成功在返回信号灯描述符,失败返回-1并设置errno。

操作信号灯的常用函数为semop。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *semops, unsigned nops);

nops为sembuf结构数组集合的元素个数。sembuf结构的定义为

struct sembuf{
        short sem_num;
        short sem_op;
        short sem_flg;
}

这里sem_num是信号灯在集合中的编号,从0开始。
sem_op 为正数则释放信号灯控制的资源并增加信号灯的值;sem_op为负数则先考察信号灯减去sem_op绝对值的结果:大于0则使用信号灯,等于0则等待资源 被释放,小于0则等待到信号灯的值增加到大于等于sem_op绝对值为止;sem_op为0则进程等待信号灯的值为0然后返回。
上面说的等待都是在sem_flg没有IPC_NOWAIT的前提的,sem_flg标志包括IPC_NOWAIT和SEM_UNDO等。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, union semun arg);

semctl使用cmd控制semid中的semnum信号灯,并以联合arg中的成员作为参数。
联合semun的定义为:

union semun{
        int val;
        struct semid_ds *buf;
        unsigned short int *array;
}

cmd包括了
GETVAL —— 返回信号灯当前解锁或者加锁的状态;
SETVAL —— 以联合arg的成员val(即arg.val)设置信号灯的当前状态;
GETPID —— 返回上次调用semop的进程的PID;
GETNCNT —— 返回正在信号灯上等待的进程数;
GETZCNT —— 返回等待信号灯的值为0的进程数;
GETALL —— 返回和semid关联的集合中所有信号灯的值;
SETALL —— 以联合arg的成员array(即arg.array)设置和semid关联的集合中所有信号灯的值;
IPC_RMID —— 删除含semid的信号灯;
IPC_SET —— 设置信号灯的访问模式(权限位);
IPC_STAT —— 复制信号灯的semid_ds到arg.buf中。