使用共享内存实现信号灯集
用于进程/线程实现同步或互斥机制
信号灯的类型:
Posix 无名信号灯(信号量)
Posix 有名信号灯(有对应文件)
System V 信号灯
含义:
计数信号灯(代表某类资源):无名/有名
System V 信号灯是一个或多个计数信号灯的集合
可同时操作集合中的多个信号灯
申请多个资源时避免死锁
使用步骤: 1.打开/创建 semget
int semget(key_t key, int nsems, int semflg); 返回值: 成功返回信号灯集的id;失败 -1 key_t key: 关联的key int nsems: 集合中包含的计数信号灯个数 int semflg: 标志位 IPC_CREAT|0666 IPC_EXECL(如果已创建则失败)
2.信号灯初始化 semctl
int semctl(int semid, int semnum, int cmd, ...); 返回值: 成功返回0;失败 EOF int semid: 要操作的信号灯集ID int semnum: 要操作的集合中的信号灯编号(从0开始类似数组下标) int cmd: 执行的操作 SETVAL IPC_RMID union semun: 取决于cmd (需要自己定义这个共用体)
3.信号灯集的P/V操作 semop
int semop(int semid, struct sembuf *sops, unsigned nsops); 返回值: 成功返回0;失败-1 int semid: 要操作的信号灯集 struct sembuf *sops: 描述对信号灯操作的结构体(多个信号灯操作应该定义对应结构体数组) unsigned nsops: 要操作的信号灯的个数 系统头文件定义好的结构体: struct sembuf{ short semnum; short sem_op; short sem_flg; }; semnum: 信号灯编号 sem_op: 负数(-1):P操作; 正数(1):V操作 sem_flg: 0/IPC_NOWAIT
练习: 父子进程通过System V信号灯同步对共享内存的读写
/*===============================================
* 文件名称:sem_shm.c
* 创 建 者:WM
* 创建日期:2023年09月02日
* 描 述:
================================================*/
#include <stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/shm.h>
#include<signal.h>
#include<unistd.h>
#include<string.h>
#define READ 0
#define WRITE 1
#define N 64
union semun{
int val; /* Value for SETVAL */
struct semid__ds *buf;/* Buffer for IPC_STAT, IPC_SET */
unsigned short *array;/* Array for GETALL, SETALL */
struct seminfo *__buf;/* Buffer for IPC_INFO (Linux-specific) */
};
//s[]信号灯对应初始值集合,n信号灯的个数
int sem_init(int semid,int s[],int n)
{
union semun myun;
for(int i=0;i<n;i++)
{
myun.val=s[i];
semctl(semid,i,SETVAL,myun);
}
}
//封装一个自定义pv操作函数
void pv(int semid, int i,int op)
{
struct sembuf buf;
buf.sem_num=i;
buf.sem_op=op;
buf.sem_flg=0;
semop(semid,&buf,1);
}
int main(int argc, char *argv[])
{
//生成key值
key_t key=ftok(".",'s');
if(-1==key)
{
perror("ftok");
return-1;
}
//创建共享内存
int shmid=shmget(key,N,IPC_CREAT|0666);
if(-1==shmid)
{
perror("shmget");
return -1;
}
//映射共享内存
char *shmaddr=shmat(shmid,NULL,0);
if((char *)-1==shmaddr)
{
perror("shmat");
goto _error1;
}
//创建信号灯集
int semid=semget(key,2,IPC_CREAT|0666);
if(-1==semid)
{
perror("semget");
goto _error1;
}
//初始化信号灯集
int s[]={0,1};
sem_init(semid,s,2);
//创建父子进程
pid_t pid=fork();
if(-1==pid)
{
perror("fork");
goto _error2;
}else if(0==pid)
{
char *p,*q;
while(1)
{pv(semid,READ,-1);//对READ进行p操作
p=q=shmaddr;
while (*p)
{
if(' '!=*p)
{
*q++=*p;
}
p++;
}
*q='\0';
printf("%s",shmaddr);
pv(semid,WRITE,1);//WRUTE进行v操作
}
}
else{
while(1)
{pv(semid,WRITE,-1);//p操作
printf("> ");
fgets(shmaddr,N,stdin);
if(0==strcmp(shmaddr,"quit\n"))
{
break;
}
pv(semid,READ,1);//v操作
}
//杀死子进程
kill(pid,SIGUSR1);
}
_error2:
semctl(semid,2,IPC_RMID);
_error1 :
shmctl(shmid,IPC_RMID,NULL);
return 0;
}