进程间通信(四)信号量

在多线程同步的方式中包含了信号量这一同步方法,但用于多线程同步的信号量是POSIXPOSIXPOSIX信号量,而用于进程间通信的则是SYSTEMSYSTEMSYSTEM VVV信号量,本质上说这两种都是用户态进程可以使用的信号量。

创建信号量

(1)在LinuxLinuxLinux中,使用函数semgetsemgetsemget来创建和打开信号量,函数原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
  • 1)该函数执行成功返回信号量标识符,失败则返回-1
  • 2)参数keykeykey是函数通过调用ftokftokftok函数得到的键值,nsemsnsemsnsems代表创建信号量的个数,如果只是访问而不创建则可以指定该参数为0;但一旦创建了该信号量,就不能更改其信号量的个数。只要不删除该信号量,就可以重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,而不会重新创建。
  • 3)semflgsemflgsemflg指定该信号量的读写权限,当创建信号量时不允许加IPCIPCIPC_ CREATCREATCREAT,若指定IPCIPCIPC_ CREATCREATCREAT | IPCIPCIPC_ EXCLEXCLEXCL后创建时发现存在该信号量,创建失败

(2)semopsemopsemop函数,用于改变信号量的值:

int semop(int semid, struct sembuf *sops, unsigned nsops);
  • 1)semsemsem_ ididid是由semgetsemgetsemget返回的信号量标识符
  • 2)sembufsembufsembuf结构的定义如下:
struct sembuf {
	short sem_num;  //除非使用一组信号量,否则它为0
	short sem_op;  //信号量在一次操作中需要改变的数据,通常是两个数
					//一个是-1,即p(等待)操作,一个是+1,即V(发送信号)操作
	short sem_flg;  //通常设置为SEM_UNDO,使操作系统跟踪信号
					//并在进程没有释放该信号量而终止时,操作系统释放信号量
};

(3)semctlsemctlsemctl函数,该函数用来直接控制信号量信息,原型:

int semctl(int semid, int semnum, int cmd, ....);
  • 1)前两个参数与前一个函数中的一样,cmdcmdcmd通常是SETVALSETVALSETVALIPCIPCIPC_ RMIDRMIDRMIDSETVALSETVALSETVAL用来把信号量初始化为一个已知值。ppp值通过unionunionunion semumsemumsemum中的valvalval成员设置,其作用是在信号量第一次使用前对它进行设置。IPCIPCIPC_ RMIDRMIDRMID用于删除一个已经无须继续使用的信号量标识符
  • 如果有第四个参数,它通常是一个unionunionunion semumsemumsemum结构,定义如下:
union semum {
	int val;
	struct semid_ds *buf;
	unsigned short *array;
};

例1:使用信号量实现进程间通信(共享内存 + 信号量、读者-写者模型)

共享内存是进程间通信的最快方式,但是共享内存的同步问题自身无法解决(即进程何时去共享内存取得数据,而何时不能取),但用信号量即可轻易解决这个问题

/*************************************************************************
      > File Name: reader.cpp
      > Author: ersheng
      > Mail: ershengaaa@163.com 
      > Created Time: Sat 02 Mar 2019 04:57:51 PM CST
 ************************************************************************/

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <errno.h>
using namespace std;
#define SEM_KEY 4001
#define SHM_KEY 5678
union semun {
	int val;
};
int main() {
	int semid, shmid;
	shmid = shmget(SHM_KEY, sizeof(int), IPC_CREAT | 0666);
	if (shmid < 0) {
		printf("create shm error\n");
		return -1;
	}
	void * shmptr;
	shmptr = shmat(shmid, NULL, 0);
	if (shmptr == (void *)-1) {
		printf("shmat error:%s\n", strerror(errno));
		return -1;
	}
	int * data = (int *)shmptr;
	semid = semget(SEM_KEY, 2, IPC_CREAT | 0666); //这里四创建一个semid,并且有两个信号量 
	union semun semun1;
	//初始化两个信号量,一个val = 0,一个val = 1 
	semun1.val = 0;
	semctl(semid, 0, SETVAL, semun1);
	semun1.val = 1;
	semctl(semid, 0, SETVAL, semun1);
	struct sembuf sembuf1;
	while (1) {
		sembuf1.sem_num = 0;  //sem_num等于0指的是下面操纵指向第一个信号量,上面设置可知其val = 0 
		sembuf1.sem_op = -1;  //初始化值为0,再-1的话就会等待 
		sembuf1.sem_flg = SEM_UNDO;
		semop(semid, &sembuf1, 1);  //reader在这里会阻塞,直到收到信号 
		printf("the NUM:%d\n",*data);    //输出结果 
		sembuf1.sem_num = 1;  //这里让writer再次就绪,就这样循环 
		sembuf1.sem_op = 1;
		sembuf1.sem_flg  = SEM_UNDO;
		semop(semid, &sembuf1, 1);
	}
	return 0;
}
/*************************************************************************
      > File Name: writer.cpp
      > Author: ersheng
      > Mail: ershengaaa@163.com 
      > Created Time: Sat 02 Mar 2019 05:08:32 PM CST
 ************************************************************************/

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <errno.h>
using namespace std;
#define SEM_KEY 4001
#define SHM_KEY 5678
union semun {
	int val;
};
int main() {
	int semid, shmid;
	shmid = shmget(SHM_KEY, sizeof(int), IPC_CREAT | 0666);
	if (shmid < 0) {
		printf("create shm error\n");
		return -1;
	}
	void * shmptr;
	shmptr = shmat(shmid, NULL, 0);
	if (shmptr == (void *)-1) {
		printf("shmat error:%s\n",strerror(errno));
		return -1;
	}
	int *data = (int *)shmptr;
	semid = semget(SEM_KEY, 2, 0666);
	struct sembuf sembuf1;
	union semun semun1;
	while (1) {
		sembuf1.sem_num = 1;  //这里指向第二个信号量(sem_num =  1)
		sembuf1.sem_op = -1; //操作是-1,因为第二个信号量初始值为1,所有下面不会阻塞 
		sembuf1.sem_flg = SEM_UNDO;
		semop(semid, &sembuf1, 1);  //继续 
		scanf("%d", data);  //用户在终端输入数据 
		sembuf1.sem_num = 0;  //指向第一个信号量 
		sembuf1.sem_op = 1;  //操作加1 
		sembuf1.sem_flg = SEM_UNDO;
		semop(semid, &sembuf1, 1);
		//执行加1后我们发现,reader阻塞正是因为第一个信号量为0,无法再减1,而现在writer先为其加1,那reader就绪后writer继续循环,发现第二个信号量
		//已经减为0,则阻塞了,回到reader 
	}
	return 0;
}

在这里插入图片描述
在这里插入图片描述

再尝试以下多开几个终端,让writerwriterwriter增加,测试readerreaderreader能否接受全部消息:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值