利用共享内存和信号灯集实现进程间同步一例

现在完成一个任务:

writer.c完成将键盘输入的内容写到共享内存,然后通知由reader.c创建的进程,其负责将从共享内存中读到的数据输出到屏幕上,然后通知writer.c创建的进程继续将从缓冲区读到的内容写到共享内存。当输入quit时,两个进程同时退出。同时还应该完成的是,可以有多个读者,当发出退出命令后,全部退出。

流程图:

image

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);
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值