版权声明:本文为博主原创文章,转载请注明出处-- https://blog.csdn.net/qq_38790716/article/details/88094057
在多线程同步的方式中包含了信号量这一同步方法,但用于多线程同步的信号量是 信号量,而用于进程间通信的则是 信号量,本质上说这两种都是用户态进程可以使用的信号量。
创建信号量
(1)在 中,使用函数 来创建和打开信号量,函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
- 1)该函数执行成功返回信号量标识符,失败则返回-1
- 2)参数 是函数通过调用 函数得到的键值, 代表创建信号量的个数,如果只是访问而不创建则可以指定该参数为0;但一旦创建了该信号量,就不能更改其信号量的个数。只要不删除该信号量,就可以重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,而不会重新创建。
- 3) 指定该信号量的读写权限,当创建信号量时不允许加 _ ,若指定 _ | _ 后创建时发现存在该信号量,创建失败
(2) 函数,用于改变信号量的值:
int semop(int semid, struct sembuf *sops, unsigned nsops);
- 1) _ 是由 返回的信号量标识符
- 2) 结构的定义如下:
struct sembuf {
short sem_num; //除非使用一组信号量,否则它为0
short sem_op; //信号量在一次操作中需要改变的数据,通常是两个数
//一个是-1,即p(等待)操作,一个是+1,即V(发送信号)操作
short sem_flg; //通常设置为SEM_UNDO,使操作系统跟踪信号
//并在进程没有释放该信号量而终止时,操作系统释放信号量
};
(3) 函数,该函数用来直接控制信号量信息,原型:
int semctl(int semid, int semnum, int cmd, ....);
- 1)前两个参数与前一个函数中的一样, 通常是 或 _ 。 用来把信号量初始化为一个已知值。 值通过 中的 成员设置,其作用是在信号量第一次使用前对它进行设置。 _ 用于删除一个已经无须继续使用的信号量标识符
- 如果有第四个参数,它通常是一个 结构,定义如下:
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;
}
再尝试以下多开几个终端,让 增加,测试 能否接受全部消息: