利用共享内存和信号灯集实现进程间同步一例
现在完成一个任务:
writer.c完成将键盘输入的内容写到共享内存,然后通知由reader.c创建的进程,其负责将从共享内存中读到的数据输出到屏幕上,然后通知writer.c创建的进程继续将从缓冲区读到的内容写到共享内存。当输入quit时,两个进程同时退出。同时还应该完成的是,可以有多个读者,当发出退出命令后,全部退出。
流程图:
writer.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>
#define N 1024
#define R 0
#define W 1
union semun //声明一个共用体,并定义一个共用体变量,该共用体的具体信息可以 man semctl
{
int val; /* Value for SETVAL */
}un;
void p(int semid , int num); //声明对信号灯集semid的编号为num的信号进行P操作的函数
void v(int semid, int num); //声明对信号灯集semid的编号为num的信号进行V操作的函数
int main()
{
key_t key; //声明一个密钥
int shmid, semid; //声明一个共享内存id和一个信号灯集id
char *shmaddr = NULL; //用于存放共享内存的起始地址
if ((key = ftok(".", 'a')) == -1) //制造一个密钥
{
perror("ftok");
exit(-1);
}
if ((semid = semget(key, 2, 0666 | IPC_CREAT | IPC_EXCL)) == -1) //不存在创建,存在报错
{
if (errno == EEXIST)//last run
{
semid = semget(key, 2, 0666); //重新得到信号灯集的id
}
else
{
perror("semget");
exit(-1);
}
}
else//first run //对于首先运行的进程完成对信号灯集的初始化
{
//init R-0
un.val = 0; //将读资源初始化为0个
if (semctl(semid, R, SETVAL, un) == -1)
{
perror("init R");
exit(-1);
}
//init W-1
un.val = 1; //将写资源初始化为1个
if (semctl(semid, W, SETVAL, un) == -1)
{
perror("init R");
exit(-1);
}
}
if ((shmid = shmget(key, N, 0666 | IPC_CREAT)) == -1) //得到共享内存的id
{
perror("shmget");
exit(-1);
}
if ((shmaddr = (char *)shmat(shmid, NULL, 0)) < (char *)0) //得到共享内存映射后的起始地址
{
perror("shmat");
exit(-1);
}
while (1)
{
p(semid, W); //写之前先申请写资源
fgets(shmaddr, N, stdin); //将输入的内容写到共享内存
v(semid, R); //发送读信号
if (strncmp(shmaddr, "quit", 4) == 0) //看是否退出
break;
}
if (shmdt(shmaddr) == -1) //退出后,取消该进程对共享内存的映射,它的删除有另一方完成
{
perror("shmdt");
exit(-1);
}
exit(0);
}
void p(int semid , int num) //定义P函数
{
struct sembuf buf = {num, -1, 0};
if (semop(semid, &buf, 1) == -1)
{
perror("p w");
exit(-1);
}
}
void v(int semid, int num) // 定义V函数
{
struct sembuf buf = {num, 1, 0};
if (semop(semid, &buf, 1) == -1)
{
perror("v r");
exit(-1);
}
}
reader.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>
#define N 1024
#define R 0
#define W 1
union semun //声明一个共用体,并定义一个共用体变量,该共用体的具体信息可以 man semctl
{
int val; /* Value for SETVAL */
}un;
void p(int semid , int num); //声明对信号灯集semid的编号为num的信号进行P操作的函数
void v(int semid, int num); //声明对信号灯集semid的编号为num的信号进行V操作的函数
int main()
{
key_t key; //声明一个密钥
int shmid, semid; //声明一个共享内存id和一个信号灯集id
char *shmaddr = NULL; //用于存放共享内存的起始地址
struct shmid_ds buf;
if ((key = ftok(".", 'a')) == -1) //制造一个密钥
{
perror("ftok");
exit(-1);
}
if ((semid = semget(key, 2, 0666 | IPC_CREAT | IPC_EXCL)) == -1)
{
if (errno == EEXIST)//last run
{
semid = semget(key, 2, 0666); //重新得到信号灯集的id
}
else
{
perror("semget");
exit(-1);
}
}
else//first run //对于首先运行的进程完成对信号灯集的初始化
{
//init R-0
un.val = 0; //将读资源初始化为0个
if (semctl(semid, R, SETVAL, un) == -1)
{
perror("init R");
exit(-1);
}
//init W-1
un.val = 1; //将写资源初始化为1个
if (semctl(semid, W, SETVAL, un) == -1)
{
perror("init R");
exit(-1);
}
}
if ((shmid = shmget(key, N, 0666 | IPC_CREAT)) == -1) //得到共享内存的id
{
perror("shmget");
exit(-1);
}
if ((shmaddr = (char *)shmat(shmid, NULL, 0)) < (char *)0) //得到共享内存映射后的起始地址
{
perror("shmat");
exit(-1);
}
while (1)
{
p(semid, R); //读之前申请读资源
printf("%s", shmaddr); //将共享内存中内容输出到屏幕上
if (strncmp(shmaddr, "quit", 4) == 0) //看是否退出
{
/*
//退出之前先发一个读资源,完成唤醒另一个读者的作用,当写进程退出后,保证所有的读者全部退出,
而最后退出的读者删除共享内存和信号灯集
*/
v(semid, R);
break;
}
v(semid, W); //发送写信号
}
if (shmdt(shmaddr) == -1) // 解除该进程的共享内存映射
{
perror("shmdt");
exit(-1);
}
if (shmctl(shmid, IPC_STAT, &buf) == -1) //读取共享内存的信息
{
perror("shmctl");
exit(-1);
}
if (buf.shm_nattch == 0) //判断该共享内存的映射数是否为0,说明这已经是最后一个读者了。如果是的话,删除共享内存和信号灯集
{
if (shmctl(shmid, IPC_RMID, NULL) == -1) //删除共享内存
{
perror("smmctl");
exit(-1);
}
if (semctl(semid, 0, IPC_RMID, NULL) == -1) //删除信号灯集
{
perror("sem rm");
exit(-1);
}
}
exit(0);
}
void p(int semid , int num) //定义P操作函数
{
struct sembuf buf = {num, -1, 0};
if (semop(semid, &buf, 1) == -1)
{
perror("p r");
exit(-1);
}
}
void v(int semid, int num) //定义V操作函数
{
struct sembuf buf = {num, 1, 0};
if (semop(semid, &buf, 1) == -1)
{
perror("v w");
exit(-1);
}
}
本文来自博客园,作者:摩斯电码,未经同意,禁止转载