操作系统原理实验-进程通信-综合型

1.实验目的

学习如何利用管道机制或消息缓冲队列进行进程间的通信,并加深对上述通信机制的理解。提高学生分析问题和解决问题的能力,并学习撰写规范的科学研究报告。

2.实验内容和要求

实验内容:

编写一段程序,使用管道来实现父子进程之间的进程通信。子进程向父进程发送自己的进程表示符,以及某字符串。父进程则通过管道读出子进程发来的消息,将消息显示在屏幕上,然后终止。或者,编写一段程序,使其用消息缓冲队列来实现client和server进程之间的通信。

实验要求:

(1)了解系统pipe() ,msgsnd(), msgrcv()的功能和实现过程。
(2)编写一段程序,使其用管道来实现父子进程之间的进程通信。

3.主要仪器设备

仪器:PC机
实验环境: Ubuntu16.04

4.预备知识

  1. 管道的概念

管道是连接读写进程的一个共享文件,允许进程以先进先出的方式写入和读出数据,并对读写操作进行同步。发送进程以字符流形式把大量数据送入管道尾部,接收进程从管道头部接收数据。

  1. 管道读写进程之间的同步

(1)管道应该互斥使用,管道读写不能同时进行。一个进程正在执行管道写入或读出操作时,另一进程必须等待。读写结束时,唤醒等待进程,自己应阻塞。
(2)读写双方必须能够知道对方是否存在,只有存在才能通信。系统发送SIGPIPE信号通知进程对方不存在。
(3)管道空间的大小通常是固定的,读写操作时需要考虑写满和读空问题。

  1. pipe函数
    创建管道:
int fds[2];
pipe (fds);

函数调用成功返回r/w两个文件描述符,规定fd[0]->r;fd[1]->w。有点类似于0对应标准输入,1对应标准输出。向管道文件读写数据其实是在读写内核缓冲区。

  1. msgsnd函数
头文件:
#include<sys/msg.h>
函数定义:
int msgsnd(int msgid, struct msgbuf *msgp, int msgsz, int msgflg);
功能:往队列中发送一个消息
返回值:成功返回0,失败返回-1;
参数:
msgid:消息标识id,也是msgget()函数的返回值。
msgp:指向消息缓冲区的指针,该结构体为:
struct mymesg{
	long mtype;//消息类型
	char mtext[512];//存放的数据内容
}
msgsz:消息的字节大小,不包含消息类型的长度(4字节)
msgflg:可以设置为0,控制消息进入消息队列或返回进程中。
  1. msgrcv()函数
头文件:
#include<sys/msg.h>
格式:
int msgrcv(int msgid, struct msgbuf *msgp, int msgsz, long mtype, int msgflg);
功能:读取消息,并且把消息队列中消息读完。
返回值:成功返回0,失败返回-1;
参数:
msgid:消息队列的id号
msgp:我们要读到的消息数据存储消息的地址
msgsz:消息的长度
mtype:我们要从消息队列中读取的消息类型。如果此值为0,则会读取时间最长的那一条消息,即我们第一个写入消息队列中的消息。
msgflg:可以设置为0,控制消息进入消息队列或返回进程中。
  1. msgget()函数
	头文件:#include<sys/msg.h>
	格式:int msgget(key_t key,int msgflg);
	功能:创建一个消息队列或者取得一个消息队列;
	返回值:成功则返回消息队列的标识符,失败返回-1;
	参数:
	key:消息队列的键值,为IPC_PRIVATE时创建一个只能被创建进程读写的消息队列。IPC_CREAT如果内存不存在则创建一个消息队列,否则直接取得。
  1. msgctl()函数
头文件:#include<sys/msg.h>
格式:int msgctl(int msgid,int cmd,struct msgqid_ds *buf)
功能:对消息队列的控制处理函数
返回值:成功返回0,失败返回-1
参数:
msgid是msgget()函数的返回值
cmd 是对消息队列的处理,如IPC_RMID是从系统内核中删除队列消息、IPC_STAT获取消息队列的详细信息。IPC_SET设置消息队列的信息。
buf是存放消息队列状态结构体的地址。

5.原理图

在这里插入图片描述

  1. 父进程先调用pipe()创建管道,得到两个文件描述符指向管道的读端和写端。
  2. 父进程fork()创建子进程,此时子进程也有两个文件描述符指向同一管道。
  3. 父进程关闭管道写端,子进程关掉管道读端,从而实现子进程向管道里面写入数据,父进程从管道里面读取数据,从而实现了进程间通信。

6.实验步骤与调试

  1. 使用pipe函数进行进程间通信
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/wait.h>
#include<sys/types.h>

int main()
{
	pid_t pid;
	char buf[1024];
	int fd[2];
	char *p1 = "this is child.\nAnd child's id is:";//定义字符数组p1
	
	if(-1==pipe(fd))
	{printf("pipe error.");}

	pid = fork();
	if(pid<0)
	{printf("fork error.");}
	
	else if(0==pid)
	{	
		int id = getpid();//获得子进程序列号
		char *p2;
		sprintf(p2,"%d",id);//用sprintf把子进程序列号从int型转为char型数组
		close(fd[0]);//子进程关闭管道读端
		write(fd[1],p1,strlen(p1));//子进程向管道写入p1、p2
		write(fd[1],p2,strlen(p2));
		close(fd[1]);
	}
	
	else
	{
		close(fd[1]);//父进程关闭管道写端
		int len = read(fd[0],buf,sizeof(buf));
		write(STDOUT_FILENO,buf,len);
		close(fd[0]);
	}
}

运行结果:
在这里插入图片描述

调试过程:
在使用pipe()进行管道通信时,只能使用write()函数向管道里面写入数据。
write函数用法为:write(int fd, const void *buf, size_t nbyte);
但是子进程序列号是int型,所以要用sprintf()函数把int型的id转为字符数组。

  1. 使用消息缓冲队列进行进程间通信
利用消息缓冲队列来实现server和client端的通信。
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/msg.h>
#define MSGKEY 75 //设置msgkey
struct msgform{//定义msgform结构体
	long mtype;
	char mtext[1024];
};

int main()
{
	int id;
	struct msgform msg;
	id = msgget(MSGKEY,0777|IPC_CREAT);//创建消息缓冲队列
	
	if(-1==fork())
	{
		printf("fork error!\n");
	}

	else if(0==fork())
	{
		msg.mtype = 1;
		sprintf(msg.mtext,"%d",getpid());//获取子进程标识符
		msgsnd(id,&msg,sizeof(msg),0);//子进程通信过程
		sleep(1);
		msgrcv(id,&msg,sizeof(msg),0,0);
		printf("receive reply form %s\n",msg.mtext);
	}
	else
	{
		msgrcv(id,&msg,sizeof(msg),0,0);//获取消息
		if(1==msg.mtype)//父进程通信
		{
			printf("%d\n",getpid());
			printf("serving for client,%s\n",msg.mtext);
			sprintf(msg.mtext,"%d",getppid());
			msgsnd(id,&msg,sizeof(msg),0);
			sleep(2);
			msgctl(id,IPC_RMID,0);//删除队列消息
		}
	}
	return 0;
}

运行结果:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值