操作系统:通过共享内存和信号量实现进程间通信(含代码)


通过共享内存和信号量实现进程间通信,其中A进程读入指定路径的文件,每次将一行文件信息保存到共享内存中并等待其他进程将数据读走,直至文件结束;B、C进程为父子进程,并发互斥地读取缓冲区信息并显示,然后再将缓冲区清空,直至接收到“quit”后,父子进程相继退出。


本博代码有一个问题:只有父子进程的某一方在读取,没有父子交替读取。我也不知道为什么,但是应付作业还是可以的。

1、原理说明

我是这样理解题目的,进程A一行一行地读取某个文件,并把一行的内容存入共享内存中,等待进程B或者进程C将其读走并将共享内存清空后再继续读取下一行的内容,直到文件结束。

而进程B和进程C都等待着进程A在共享内存中放入数据后并发互斥地读取缓冲区并将缓冲区清空,也就是说,共享内存缓冲区的内容每次只能由进程B或者进程C中的一个读取。

如下图所示。

在这里插入图片描述

从上图我们可以总结出:
进程A和进程B之间有互斥关系;
进程A和进程C之间有互斥关系;
进程B和进程C之间有互斥关系;
进程A和进程B之间有同步关系;
进程A和进程C之间有同步关系。

我理解的结束的条件为:
① A读到了文件尾;
② B或C读到了quit。

2、代码说明

其实我们可以把整个过程想象成这样:
妈妈A每次在盘子里放入一个苹果,放入一个苹果后,爸爸B和儿子C开始抢夺,一个苹果只能被抢到的那个人吃掉,等苹果被吃完盘子空出来后,妈妈A才能开始放下一个苹果。

共享内存需要我们自己设置同步互斥关系,可以设置两个有名信号量apple、plate来模拟这个过程:
apple=1时,爸爸和儿子可以开始抢夺;
plate=1的时候妈妈可以开始放苹果。
初始时,apple=0、plate=1。

所以,A、B、C进程执行的函数大意如下。

在这里插入图片描述

由于只有一个临界资源,所以这道题无需设置互斥。

下面我说一下三个进程A、B、C具体是如何结束的:
1、文件的某一行为quit,那么进程A、B、C读到后结束运行;
2、文件里没有quit,那么进程A在文件都到最后一行以后,自动将quit存入共享内存,这样也可以使三个进程A、B、C结束运行。

3、运行结果

运行结果我进行了修改的,依旧存在我开头说的问题。

进程A为2-A.c
进程BC为2-BC.c
编译:
czxt2022@ubuntu:~$ gcc 2-A.c -o 2-A -lpthread
czxt2022@ubuntu:~$ gcc 2-BC.c -o 2-BC -lpthread

文件中的某一行为quit
首先查看一下我们要读的文件,里面有quit。
czxt2022@ubuntu:~$ cat prg/cfile/ts.txt
zhengzhou
university
operating
system
quit
lin
yang

进程A:
czxt2022@ubuntu:~$ ./2-A
请输入你想要读取的文件:
prg/cfile/ts.txt
A 读到了第1行,内容为:zhengzhou
A 读到了第2行,内容为:university
A 读到了第3行,内容为:operating
A 读到了第4行,内容为:system
A 读到了第5行,内容为:quit
结束!

进程BC:
czxt2022@ubuntu:~$ ./2-BC
我是父进程,我读到了:zhengzhou
我是子进程,我读到了:university
我是子进程,我读到了:operating
我是父进程,我读到了:system
我是父进程,我读到了:quit

文件里没有quit
首先查看一下我们要读的文件,里面没有quit。
czxt2022@ubuntu:~$ cat prg/cfile/ts.txt
zhengzhou
university
operating
system
lin
yang

进程A:
czxt2022@ubuntu:~$ ./2-A
请输入你想要读取的文件:
prg/cfile/ts.txt
A 读到了第1行,内容为:zhengzhou
A 读到了第2行,内容为:university
A 读到了第3行,内容为:operating
A 读到了第4行,内容为:system
A 读到了第5行,内容为:lin
A 读到了第6行,内容为:yang
A 读到了第7行,内容为:quit
结束!

进程BC:
czxt2022@ubuntu:~$ ./2-BC
我是父进程,我读到了:zhengzhou
我是子进程,我读到了:university
我是父进程,我读到了:operating
我是子进程,我读到了:system
我是子进程,我读到了:lin
我是父进程,我读到了:yang
我是子进程,我读到了:quit

注意:这里最后进程A、B、C都读到了quit,但是这个quit不是文件里的,而是文件读完后,作为一个信号来结束进程A、B、C的。

4、源代码

2-A.c

#include<unistd.h>
#include<stdio.h>
#include<semaphore.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>
#include<sys/types.h>
#include<sys/wait.h>
#include <fcntl.h>
#define MAX_SIZE 100 
int main()
{
    
    
	struct sentence
	{
    
    
		char sen[MAX_SIZE];
	};
	//打开有名信号量,没有就创建
	sem_t* apple;
	sem_t* plate;
	apple=sem_open("apple",O_CREAT,0644,0);
	if(apple==SEM_FAILED)
	{
    
    
		printf("unable to creat semaphore!");
		sem_unlink("apple");//删除有名信号量
		exit(-1);
	}
	plate=sem_open("plate",O_CREAT,0644,1);
	if(plate==SEM_FAILED)
	{
    
    
		printf("unable to creat semaphore!");
		sem_unlink("plate");//删除有名信号量
		exit(-1);
	}
	//创建IPC键值为shmkey的共享内存,其大小为1024字节,允许读写
	key_t shmkey=ftok("2.c",0);
	int shmid=shmget(shmkey,1024,0666|IPC_CREAT);
	if(shmid==-1)printf("unable to create shm!\n");
	struct sentence* addr=(struct sentence*)shmat(shmid,0,0);
	if(addr==(struct sentence*)-1)printf("shm shmat is fail\n");
	//输入文件名,打开文件
	char fileroad[100];
	printf("请输入你想要读取的文件:\n");
	scanf("%s",fileroad);
	FILE* fp=fopen(fileroad,"r");
	char buffer[MAX_SIZE];
	int i=1;
	//A
	while(1)
	{
    
    
		sem_wait(plate);//请求盘子
		memset(buffer, 0, MAX_SIZE);//清空缓存区
		if(fgets(buffer , MAX_SIZE , fp) == NULL)
		{
    
    //如果文件已经读到了最后一行,那么将quit放到缓冲区中
			strcpy(buffer,"quit\n");
		}
		printf("A 读到了第%d行,内容为:%s",i,buffer);
		strncpy(addr->sen, buffer, strlen(buffer));
		i++;
		sem_post(apple);
		if(strncmp(buffer,"quit",4)==0)break;//如果读到了某一行为quit,退出
	}
	printf("结束!\n");
	//断开连接,删除共享内存
	if(shmdt(addr)==-1)printf("shmdt is fail\n");
	if(shmctl(shmid,IPC_RMID,NULL)==-1) printf("shmctl delete error\n");
	//关闭并删除有名信号量
	sem_close(apple);
	sem_close(plate);
	sem_unlink("apple");
	sem_unlink("plate");
	return 0;
}

2-BC.c

#include<unistd.h>
#include<stdio.h>
#include<semaphore.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>
#include<sys/types.h>
#include<sys/wait.h>
#include <fcntl.h>
#define MAX_SIZE 100
int main()
{
    
    
	struct sentence
	{
    
    
		char sen[MAX_SIZE];
	};
	//打开有名信号量,没有就创建
	sem_t* apple;
	sem_t* plate;
	apple=sem_open("apple",O_CREAT,0644,0);
	if(apple==SEM_FAILED)
	{
    
    
		printf("unable to creat semaphore!");
		sem_unlink("apple");//删除有名信号量
		exit(-1);
	}
	plate=sem_open("plate",O_CREAT,0644,1);
	if(plate==SEM_FAILED)
	{
    
    
		printf("unable to creat semaphore!");
		sem_unlink("plate");//删除有名信号量
		exit(-1);
	}
	//打开共享内存
	key_t shmkey=ftok("2.c",0);
	int shmid=shmget(shmkey,0,0666);
	if(shmid==-1)printf("unable to open shm!\n");
	struct sentence* addr=(struct sentence*)shmat(shmid,0,0);
	if(addr==(struct sentence*)-1)printf("shm shmat is fail\n");
	//BC
	pid_t pid;
	while(pid=fork()==-1)printf("failure");
	if(pid==0)
	{
    
    
		while(1)
		{
    
    
			sem_wait(apple);//要苹果
			printf("我是子进程,我读到了:%s",addr->sen);
			if (strncmp(addr->sen, "quit", 4) == 0) break;//读到quit,退出
			memset(addr->sen, 0,MAX_SIZE);
			sem_post(plate);//放盘子
		}
		exit(0);
	}
	else
	{
    
    
		while(1)
		{
    
    
			sem_wait(apple); //要苹果
			printf("我是父进程,我读到了:%s",addr->sen);
			if (strncmp(addr->sen, "quit", 4) == 0) break; //读到quit,退出
			memset(addr->sen, 0,MAX_SIZE);
			sem_post(plate);//放盘子
		}
		wait(0);
	}
	//断开连接,删除共享内存
	if(shmdt(addr)==-1)printf("shmdt is fail\n");
	if(shmctl(shmid,IPC_RMID,NULL)==-1) printf("shmctl delete error\n");
	//关闭并删除有名信号量
	sem_close(apple);
	sem_close(plate);
	sem_unlink("apple");
	sem_unlink("plate");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45750767/article/details/125404720
今日推荐