共享内存和信号量实现进程间通信
题目
通过共享内存和信号量实现进程间通信,其中A进程读入指定路径的文件,每次将一行文件信息保存到共享内存中并等待其他进程将数据读走,直至文件结束;B、C进程为父子进程,并发互斥的读取缓冲区信息并显示,然后再将缓冲区清空,直至接收到“quit”后,父子进程相继退出。
思路
写程序先申请一块共享内存,再打开文件,逐行读取文件内容到缓冲区并写入到共享内存。
读程序创建一个子进程,父子进程互斥访问共享内存并输出信息到显示器。
为了保证这个过程中信息读、写操作的正确性,需要通过信号量限制各个进程对共享内存的访问。写程序和读程序之间进行同步,读程序的父子进程之间进行互斥。
一、写程序
部分核心代码如下:
struct Poem
{
char verse[96];
};//定义要绑定到共享内存上的数据结构
FILE *fp;
int shmid;
struct Poem *poem;
char data[96];//定义缓冲区
sem_t*sem;//同步信号量1
sem_t*semm;//同步信号量2
key_t shmkey;//定义IPCkey
shmkey=ftok("declaimer.c",0);//获取IPCkey
shmid=shmget(shmkey,1024,0666|IPC_CREAT); //创建共享内存
poem=(struct Poem *)shmat(shmid,0,0);//绑定共享内存
sem=sem_open("asemofpoem",O_CREAT,0644,1);//创建并初始化有名信号量
semm=sem_open("asemofwrite",O_CREAT,0644,1);
fp = fopen("poem.txt","r");//打开文件
do
{
sem_wait(semm);//等待读进程读取完毕post后继续进行读写操作
memset(data, 0, 96);//缓冲区清零
memset(poem->verse, 0, 96);//结构体变量清零
if(fgets(data, 96, fp) == NULL)//读取一行文件内容并判断是否结束
{
sem_post(sem);
break;
}
strncpy(poem->verse, data,strlen(data)-1);//复制一行到缓冲区
sem_post(sem);//读程序中的进程获得sem信号量后可以进行读取
sleep(1);
}while(strncmp(data,"quit",4)!=0);
fclose(fp);//关闭文件
这里似乎没有必要定义一个结构体来绑定共享内存,但是参考教材上的例子就照着用了,其实换成用字符数组直接绑定共享内存应该也是可以的。
二、读程序
部分核心代码如下:
struct Poem
{
char verse[96];
};//定义读取数据结构
struct Poem *poem;
pid_t pid;//进程号
sem_t*sem;//同步信号量1
sem_t*semm;//同步信号量2
sem_t*mutex;//互斥信号量
int shmid;
key_t shmkey;
shmkey=ftok("declaimer.c",0);//获取IPCkey
sem=sem_open("asemofpoem",0,0644,0);
semm=sem_open("asemofwrite",0,0644,0);
mutex=sem_open("asemofread",O_CREAT,0644,1);//定义互斥信号量
shmid=shmget(shmkey,0,0666);
poem=(struct Poem *)shmat(shmid,0,0);//绑定共享内存
pid=fork();//创建子进程
do
{
sem_wait(mutex);//互斥访问临界资源
sem_wait(sem); //等待写进程写入数据
if(pid==0)//根据进程ID输出信息
printf("\n子进程读取:%s \n",poem->verse);
else
printf("\n父进程读取:%s \n",poem->verse);
if (strncmp(poem->verse, "quit", 4)== 0) //检测到结束标志
{
sem_post(sem);
sem_post(mutex);
break;
}
sem_post(semm);//读取完成,写程序获取sem后可继续写入
sem_post(mutex);//释放访问临界资源信号量
} while(1);
三、运行结果
写程序没有定义向标准输出流输出数据,命令行窗口不显示任何信息:
username:~/$ ./declaimer
读程序父子进程交替读取写程序写入共享内存中的信息并进行显示,读到quit停止:
username:~/$ ./audient
父进程读取:君不见黄河之水天上来,奔流到海不复回。
子进程读取:君不见高堂明镜悲白发,朝如青丝暮成雪。
父进程读取:君不见高堂明镜悲白发,朝如青丝暮成雪。
子进程读取:人生得意须尽欢,莫使金樽空对月。
父进程读取:天生我材必有用,千金散尽还复来。
子进程读取:烹羊宰牛且为乐,会须一饮三百杯。
父进程读取:岑夫子,丹丘生,将进酒,杯莫停。
子进程读取:与君歌一曲,请君为我倾耳听。
父进程读取:钟鼓馔玉不足贵,但愿长醉不愿醒。
子进程读取:古来圣贤皆寂寞,惟有饮者留其名。
父进程读取:陈王昔时宴平乐,斗酒十千恣欢谑。
子进程读取:主人何为言少钱,径须沽取对君酌。
父进程读取:五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。
子进程读取:quit
父进程读取:quit
总结
题目涉及到多进程间的同步和互斥问题,可以看作是生产者-消费者问题的变种,因为在读程序(消费者)中存在着多个进程,需要在原有的生产者-消费者系统的消费者程序设置互斥信号量保证多个进程之间互斥访问共享内存,如此就可以保证对共享内存读写操作的正确性。