简单来讲,信号量是一个用来描述临界资源的资源个数的计数器。信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件、外部设备等)来实现进程间通信,他本身更只是一种外部资源的标识。信号量在此过程中负责数据操作的同步、互斥等功能。
当请求 一个使用信号量来表示 的资源时,进程需要先读取信号量的值来判断资源是否可用。大于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;
}