通过共享内存和信号量实现进程间通信,其中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;
}