System V 信号量
API 函数
创建或打开一个信号量集
#include <sys/types.h>
#include <sys/sem.h>
int semget( key_t key, int nsems, int semflg );
参数 key:由 IPC_PRIVATE 或 ftok() 返回的键
参数 nsems:
- 创建信号集时:nsems 指定信号量的数量,必须大于0
- 获取信号集时:nsems 必须小于等于集合的大小
参数 semflg:位掩码和指定文件权限
- IPC_CREAT:如果 key 不存在则创建新的信号集
- IPC_EXCL:同时指定了 IPC_CREAT ,如果信号集已存在,则返回EEXIST错误
信号量控制操作
#include <sys/types.h>
#include <sys/sem.h>
int semctl( int semid, int semnum, int cmd, .../* union semun arg */ );
参数 semid:信号量集的标识符
参数 semnum:信号量集中的具体信号量编号
参数 cmd:需执行的操作
-
常规操作
-
IPC_RMID:删除信号量集
-
IPC_STAT:将 semid_ds 数据结构的副本放入 arg.buf
-
IPC_SET:用 arg.buf 更新 semid_ds 数据结构
-
-
信号量值操作
- GETVAL:返回 semid 信号量集中的第 semnum 个信号量的值
- SETVAL:设置 semid 信号量集中的第 semnum 个信号量的值
- GETALL:获取 semid 信号量集中所有信号量的值,放入 arg.array 指向的数组
- SETALL:使用 arg.array 指向的数组更新 semid 信号量集中的所有信号量的值
-
获取单个信号量的信息
- GETPID:返回上一个在该信号量上执行 semop() 的进程的进程ID
- GETNCNT:返回等待该信号量增长的进程数
- GETZGNT:返回等待该信号量变为0的进程数
参数 arg:一些特定的操作需要传入第四个参数
信号量操作
#include <sys/types.h>
#include <sys/sem.h>
int semop( int semid, struct sembuf *sops, unsigned int nsops );
参数 sops :数组
struct sembuf{
unsigned short sem_num; /*执行操作的信号量编号*/
short sem_op; /*执行的操作*/
short sem_flg;
};
- sem_op 大于 0,信号量加上 sem_op
- sem_op 等于 0,检查信号量值是否为0,为0就立即结束操作,否则阻塞直到为0
- sem_op 小于 0,信号量减去 sem_op
参数 nsops :数组的大小
参数 sem_flag:
- IPC_NOWAIT 防止阻塞,而直接返回错误
- SEM_UNDO 当进程被终止时,会撤销上一步的信号量操作
struct sembuf sops[3];
/*信号量0 - 1*/
sops[0].sem_num = 0;
sops[0].sem_op = -1;
sops[0].se,_flg = 0;
/*信号量1 + 2*/
sops[1].sem_num = 1;
sops[1].sem_op = 2;
sops[1].se,_flg = 0;
sops[3].sem_num = 2;
sops[3].sem_op = 0;
sops[3].se,_flg = IPC_NOWAIT; /*设置非阻塞*/
if( semop( semid, sops, 3 ) == -1 )
{
if( errno == EAGAIN )
{
printf( "信号量2发送了阻塞\n" );
}
else
{
perror( "其他错误\n" );
}
}
信号量关联结构体
struct semid_ds{
struct ipc_perm sem_perm;
time_t sem_otime;
time_t sem_ctime;
unsigned long sem_nsems;
};
sem_otime:在创建信号量集时会将这个字段设置为 0,然后在每次成功的 semop() 调用或当信号量值因 SEM_UNDO 操作而发生变更时将这个字段设置为当前时间
sem_ctime:在创建信号量时以及每个成功的 IPC_SET、SETALL 和 SETVAL 操作执行完毕之后将这个字段设置为当前时间
sem_nsems:在创建集合时将这个字段的值初始化为集合中信号量的数量
代码
利用二值信号量在父子进程之间进行同步
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#if _SEM_SEMUN_UNDEFINED
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
#if defined(__linux__)
struct seminfo *__buf;
#endif
};
#endif
/*创建信号量*/
int createSem( void )
{
int semid;
semid = semget( IPC_PRIVATE, 1, IPC_CREAT | IPC_EXCL | S_IRUSR|S_IWUSR );
if( semid != -1 )/*成功创建信号集*/
{
union semun arg;
/*信号量值初始化为0*/
arg.val = 0;
semctl( semid, 0, SETVAL, arg );
}
else /*创建失败*/
{
perror("semget failed\n");
while(1);
}
return semid;
}
/*保留信号量*/
int reserveSem( int semId, int semNum )
{
struct sembuf sops;
sops.sem_num = semNum;
sops.sem_op = -1; /*信号量-1*/
sops.sem_flg = SEM_UNDO;
/*如果在信号量等于0时减1会导致阻塞*/
while( semop( semId, &sops, 1 ) == -1 )
{
if( errno != EINTR) /*在阻塞时被信号打断了*/
{
perror( "EINTR\n" );
return -1;
}
}
return 0;
}
/*释放信号量*/
int releaseSem( int semId, int semNum )
{
struct sembuf sops;
sops.sem_num = semNum;
sops.sem_op = 1; /*信号量+1*/
sops.sem_flg = SEM_UNDO;
return semop( semId, &sops, 1 );
}
int main( void )
{
int semid;
semid = createSem();
switch( fork() )
{
case -1:
perror("fork failed\n");
_exit( EXIT_FAILURE );
break;
case 0:
reserveSem( semid, 0 );
printf("case 0\n");
_exit( EXIT_SUCCESS );
break;
default:
sleep(5);
releaseSem( semid, 0 );
printf("default\n");
wait(NULL);
break;
}
exit( EXIT_SUCCESS );
}