一、信号量和信号量集简介
1、用于进程间的互斥和同步,来控制对共享资源的访问。
2、为了便于对大量共享资源的操作引入了信号量集,每个共享资源对应一个信号量。
3、信号量的操作:P(对信号量减)操作和V(对信号量加)操作。
二、信号量相关API
1、创建信号量集
int semget(key_t key,int nsems,int flag);
参数:
key:用户指定的信号量集键值
nsems: 信号量集合中的信号量个数
flag:IPC_CREAT、IPC_EXCL等权限的组合。
返回: 成功返回信号量集ID,失败返回-1
2、信号量集控制
int semctl(int semid, int semnum, int cmd, .../* union semnu arg*/);
union semnu {
int val;
struct semid_ds *buf;
unsigned short *arry;
};
参数:
semid: 信号量集ID
semnu: 0表示对所有信号量操作,信号量编号从0开始
val: 获取或者信号量集中某个信号量的值
buf: 信号量集属性指针
arry:获取或设置信号量集中所有信号量的值。
cmd:
3、信号量集操作(对信号量集中的信号量做加和减操作(PV操作))
int semop(int semid, struct sembuf * sops, size_t nsops);
struct sembuf{
unsigned short sem_num;
short sem_op;
short sem_flag;
};
参数:
semid: 信号量集ID
sops:sembuf 结构体数组指针
nsops:sops中信号量的个数,sizeof(sops)/sizeof(sops[0])
sem_num: 信号量集中信号量的编号
sep_op: 正数为V 操作(对信号量加操作),负数为P操作(对信号量减)
sem_flag:一般为SEM_UNDO
返回:
成功返回0;出错返回-1。
三、代码测试
任务:创建一块共享内存和两个进程P1和P2,P1向共享内存中写数据,写完后,P2从共享内存中读取数据,P2读完后P1继续写,然后依次循环。
代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
typedef union {
int val;
struct semid_ds *buf;
unsigned short* arry;
}semnn_u;
typedef struct
{
int val;
int semid;
}Storage_t;//共享内存的数据结构
int create_sem(Storage_t * s)
{
//创建两个信号量
s->semid = semget(IPC_PRIVATE,2,IPC_CREAT|IPC_EXCL|0777);
if(s->semid < 0)
{
printf("semget error\n");
return -1;
}
//对信号量进行初始化,都初始化为0
semnn_u nu;
unsigned short val[2] = {0,0};
nu.arry = val;
if(semctl(s->semid,2,SETALL,nu) < 0)
{
printf("semctl error\n");
return -1;
}
return 0;
}
void destory_sem(Storage_t *s)
{
//销毁2个信号量
if(semctl(s->semid,2,IPC_RMID,NULL) < 0)
{
printf("destory error\n");
}
}
void writer(Storage_t *s ,int val)
{
//将数据写入共享内存
s->val = val;
printf("writer:%d,pid:%d\n",val,getpid());
//对S1做V操作
//s1的sem_num是0
struct sembuf sem_op_v = {0,1,SEM_UNDO};
if(semop(s->semid,&sem_op_v,1) < 0)
{
printf("sem op error 1\n");
}
//对S2做P操作
//s2的sem_nu是1
struct sembuf sem_op_p = {1,-1,SEM_UNDO};
if(semop(s->semid,&sem_op_p,1) < 0)
{
printf("sem op error 2\n");
}
}
void reader(Storage_t * s)
{
//先对S1做P操作
struct sembuf sem_op_p = {0,-1,SEM_UNDO};
if(semop(s->semid,&sem_op_p,1) < 0)
{
printf("reader semop error 1\n");
}
//对共享内存数据进行读操作
printf("reader: %d,pid:%d\n",s->val,getpid());
//对S2做V操作
struct sembuf sem_op_v = {1,1,SEM_UNDO};
if(semop(s->semid,&sem_op_v,1) < 0)
{
printf("reader semop error 2\n");
}
}
int main(void)
{
//创建共享内存
pid_t pid ;
int shmid = shmget(IPC_PRIVATE,sizeof(Storage_t),IPC_CREAT|IPC_EXCL|0777);
Storage_t *p;
//将共享内存映射到本进程中
p = (Storage_t *)shmat(shmid,0,0);
if(p == (Storage_t *) -1)
{
printf("shmat error\n");
return -1;
}
if(shmid < 0)
{
printf("shmget error\n");
return -1;
}
//创建信号量
create_sem(p);
pid = fork();
if(pid > 0)
{
//parent process
printf("parent pid :%d\n",getpid());
//向共享内存中写20次
for(int i = 0; i < 20;i ++)
{
writer(p,i);
}
wait(0);//等待回收子进程
destory_sem(p); //销毁信号量
shmdt(p); //解除共享内存映射
shmctl(shmid,IPC_RMID,NULL);//销毁共享内存
}
else if(pid == 0)
{
printf("child pid:%d\n",getpid());
//从共享内存中读20次
for(int i = 0;i < 20;i ++)
{
reader(p);
}
shmdt(p);
}
return 0;
}
测试结果: