进程间通信(四)信号量

版权声明:本文为博主原创文章,转载请注明出处-- https://blog.csdn.net/qq_38790716/article/details/88094057

在多线程同步的方式中包含了信号量这一同步方法,但用于多线程同步的信号量是 P O S I X POSIX 信号量,而用于进程间通信的则是 S Y S T E M SYSTEM V V 信号量,本质上说这两种都是用户态进程可以使用的信号量。

创建信号量

(1)在 L i n u x Linux 中,使用函数 s e m g e t semget 来创建和打开信号量,函数原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
  • 1)该函数执行成功返回信号量标识符,失败则返回-1
  • 2)参数 k e y key 是函数通过调用 f t o k ftok 函数得到的键值, n s e m s nsems 代表创建信号量的个数,如果只是访问而不创建则可以指定该参数为0;但一旦创建了该信号量,就不能更改其信号量的个数。只要不删除该信号量,就可以重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,而不会重新创建。
  • 3) s e m f l g semflg 指定该信号量的读写权限,当创建信号量时不允许加 I P C IPC _ C R E A T CREAT ,若指定 I P C IPC _ C R E A T CREAT | I P C IPC _ E X C L EXCL 后创建时发现存在该信号量,创建失败

(2) s e m o p semop 函数,用于改变信号量的值:

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

(3) s e m c t l semctl 函数,该函数用来直接控制信号量信息,原型:

int semctl(int semid, int semnum, int cmd, ....);
  • 1)前两个参数与前一个函数中的一样, c m d cmd 通常是 S E T V A L SETVAL I P C IPC _ R M I D RMID S E T V A L SETVAL 用来把信号量初始化为一个已知值。 p p 值通过 u n i o n union s e m u m semum 中的 v a l val 成员设置,其作用是在信号量第一次使用前对它进行设置。 I P C IPC _ R M I D RMID 用于删除一个已经无须继续使用的信号量标识符
  • 如果有第四个参数,它通常是一个 u n i o n union s e m u m semum 结构,定义如下:
union semum {
	int val;
	struct semid_ds *buf;
	unsigned short *array;
};

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

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

/*************************************************************************
      > File Name: reader.cpp
      > Author: ersheng
      > Mail: [email protected] 
      > 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: [email protected] 
      > 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;
}

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

再尝试以下多开几个终端,让 w r i t e r writer 增加,测试 r e a d e r reader 能否接受全部消息:

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

猜你喜欢

转载自blog.csdn.net/qq_38790716/article/details/88094057