简单来讲,信号量是一个用来描述临界资源的资源个数的计数器。信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件、外部设备等)来实现进程间通信,他本身更只是一种外部资源的标识。信号量在此过程中负责数据操作的同步、互斥等功能。 当请求 一个使用信号量来表示 的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求;等于0,无资源可用,进程会进入睡眠状态直至资源可用。在Linux系统中可以通过 【ipcs -s】的命令查看信号量,通过【rmipc -s 信号量】删除指定的信号量。
1.信号量创建 函数原型如下:
int semget(key_t key,int nsems,int semflg); 其中: key:信号量键值,可以理解为信号量的唯一性标记; num_sems:信号量的数目,一般为1; sem_flags:标志位IPC_CREATE和IPC_EXCL; IPC_CREATE表示若信号量已存在,返回该信号量标识符; IPC_EXCL表示若信号量已存在,返回错误;返回值:成功相应的信号量标识符,失败返回-1
2.修改信号值 函数原型如下:
int semop(int sem_id,struct sembuf *sem_opa,size_t num_sem_ops); 其中: sem_id:信号量标识符,即semget函数的返回值; sem_opa:结构如下 struct sembuf{ short sem_num;//除非使用一组信号量,否则它为0 short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作, //一个是1,即V(发送信号)操作。 short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号, //并在进程没有释放该信号量而终止时,操作系统释放信号量 }; num_sem_ops:指的是结构数组的元素个数;返回值:失败返回-1
3.信号量控制 函数原型如下:
int semctl(int sem_id, int sem_num, int command, ...); 其中: sem_id:信号量标识符,即semget函数的返回值; sem_num:信号量的下标,从0开始。; command:具体的操作命令,有SETVAL(设置初值)、IPC_RMID(移除信号量; 可变参数:它是一个union semun结构,包含的成员如下: union semun { int val; struct semid_ds *buf; usigned short *array; }一般只使用val这个成员,来为信号量赋初值。当信号量值为0时,进程会阻塞运行。返回值:失败返回-1
4.示例代码 封装API函数:
sem.h: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/sem.h> struct semun { int val; }; int sem_init(int key); void sem_p(int sem_id); void sem_v(int sem_id); void sem_destroy(int sem_id); sem.c: #include "sem.h" int sem_init(int key) { int semid; semid = semget((key_t)key,1,IPC_CREAT | IPC_EXCL| 0600); if(semid == -1){ printf("semget error"); return -1; } union semun a;//a传值 a.val = 1; if(semctl(semid,0,SETVAL,a)==-1){//0代表信号量下表 perror("semctl init error"); return -1; } retrun semid; } void sem_p(int sem_id) { struct sembuf buf; buf.sem_num = 0;//信号量下标 buf.sem_op = -1;//p操作 buf.sem_flg = SEM_UNDO; if(semop(sem_id,&buf,1)==-1) { perror("p error"); } } void sem_v(int sem_id) { struct sembuf buf; buf.sem_num = 0; buf.sem_op = 1; buf.sem_flg = SEM_UNDO; //设置在进程出现错误时信号量值自动恢复,防止一个进程占着信号量 if(semop(sem_id,&buf,1)==-1){ //1表示操作数,sembuf的数量 perror("v error"); } } void sem_destroy(int sem_id) { if(semctl(sem_id,0,IPC_RMID)==-1){ //0代表信号量集 perror("semctl destroy error"); } }示例进程A:
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> #include "sem.h" int main(void) { int sem_id = sem_init(214); if(sem_id == -1) return -1; int i = 0; for(;i<10;++i) { sem_p(sem_id ); printf("A\n"); int n = rand()%3; sleep(n); printf("A\n"); sem_v(sem_id ); n = rand()%3; sleep(n); } return 0; }示例进程B:
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> #include "sem.h" int main(void) { int sem_id = sem_init(214); if(sem_id == -1) return -1; int i = 0; for(;i<10;++i){ sem_p(sem_id); printf("B\n"); int n = rand()%3; sleep(n); printf("B\n"); sem_v(sem_id); n = rand()%3;//产生随机睡眠时间 sleep(n); } sleep(10); sem_destroy(sem_id); return 0; }