您的位置:首页 > 其它

管道和消息队列实现进程间通信

2017-05-08 17:41 323 查看

管道和消息队列实现进程间通信

管道和消息队列实现进程间通信
管道
基础知识

函数介绍

练习内容

实现代码

消息队列
基础知识

函数介绍

练习内容

实现代码

管道

基础知识

管道

就像现实中的水管,水就像数据。

管道是一种半双工的通信方式

数据只能单向流动,而且只能在具有共同祖先的进程间使用

函数介绍

int pipe(int  fd[2])


功能:创建管道

参数fd关联:

一个读端:fd[0]

一个写端:fd[1]

头文件
<unistd.h>




int read(int fd, void *buf, int count);


功能:从参数fd指定的读端读取管道数据到大小为count的缓存buf中,返回实际读取到的字节数。

参数

fd:管道读端

buf:缓存区,保存读到的数据

count:读取字节数

int write(int fd, void *buf, int count);


功能:向参数fd指定的写端从缓存buf中取出count个字节到管道中,返回值为实际写入的字节数。

参数

fd:管道写端

buf:缓存区,将要写入管道的数据

count:写入的字节数



练习内容

1.父进程创建管道和两个子进程p1和p2
2.子进程p1打开给定文件(如果没有,则创建文件),并向文件中写数据,写完关闭文件,然后向管道写入一条消息“ok",目的是通知进程p2可以读取文件内容了。
3.子进程p2通过管道读取消息,如果消息是“ok”,则打开文件,读取文件内容,并将其输出到屏幕上,关闭文件


实现代码

/*2.利用管道实现进程间的通信
编写程序,父进程创建管道和两个子进程p1和p2
子进程p1打开给定文件(如果没有,则创建文件),并向文件中写数据,写完关闭文件,然后向管道写入一条消息“ok",目的是通知进程p2可以读取文件内容了。
子进程p2通过管道读取消息,如果消息是“ok”,则打开文件,读取文件内容,并将其输出道屏幕上**/

//管道是半双工的通信方式
//数据只能单向流动,而且只能在具有共同祖先的进程间使用。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>   //管道需要的头文件
#include <string.h>
//#include <mem.h>   //memset()函数
#include <fcntl.h>
#define BUFFER_SIZE 1024

int write_file()  //把内容写到文件里面去
{
int f=open("a.txt",O_WRONLY|O_CREAT,0660);//打开指定路径的文件,如果不存在自动创建
//f为一文件指针
if(f==-1) //打开失败
{
printf("open file error !\n");
return 0;
}
char buffer[]="this is a file";//写到文件里面去的内容
write(f,buffer,sizeof(buffer));
//向参数f指定的写端从缓存buf中取指定大小的字节到管道中
//返回值为实际写入的字节数
close(f);//关闭文件
return 1;
}
int read_file(char *buffer)
{
int f=open("a.txt",O_RDONLY,0660);//打开指定路径的文件,如果不存在自动创建
if(f==-1)//打开文件失败
{
printf("open file error !\n");
return 0;
}
int numofchar=read(f,buffer,BUFFER_SIZE); //f代表读端
//不知道缓冲区有多少有效的数据,所以把buffer大小的内容全读出来
//从参数f指定的读端读取管道数据到指定大小为的缓存buf中
//返回实际读取到的字节数
close(f);
return numofchar;
}
int main(int argc,char *argv[])
{
int fd[2];  //创建管道的读写端(fd【0】读,fd【1】写)
if(pipe(fd)<0)//创建管道
{
perror("pipe");//错误输出函数,没有错误的时候就显示 error 0
exit(0);
}
char mas[100];
int n;
memset(mas,0,sizeof(mas)); //清空buf内容为0
//一个字节一个字节的设定为指定值
//build son
pid_t p1;
p1=fork();
if(p1<0)  //创建进程失败
{
fprintf(stderr,"fork failed");
exit(-1);
}
else if(p1==0)//son
{
//子进程p1打开给定文件(如果没有,则创建文件)
//并向文件中写数据,写完关闭文件,然后向管道写入一条消息“ok"
//目的是通知进程p2可以读取文件内容了
if(write_file())  //调用写文件函数
{
close(fd[0]);  //close read
char *massage = "ok";
write(fd[1],massage,100); //写管道消息
//ok 写到fd【1】里面
}
}
else  //father
{
pid_t p2;
p2=fork();
if(p2<0)//创建进程失败
{
fprintf(stderr,"fork failed");
exit(-1);
}
else if(p2==0)//son
{
close(fd[1]); //close write
read(fd[0],mas,sizeof(mas));// 从 读端读消息
if(!strcmp(mas,"ok"))  // is "ok" or no
{
char data[BUFFER_SIZE];
int n =read_file(data);  //读取文件里面的内容并存到data中
printf("data of file is : %s. \n",data);//打印出来
}
}
else  //father
{
exit(0);
}
}

return 0;
}


消息队列

消息队列提供了一种由一个进程向另一个进程发送块数据的方法。

每一个数据块被看作有一个类型,接收进程可以独立接收具有不同类型的数据块。

基础知识

消息队列

消息的链接表,简称:队列

标识符:队列ID

队列中消息的结构:



函数介绍

int msgget(key_t  key, int flag)


头文件:
<sys/msg.h>


功能:打开一个队列或创建一个新队列

返回值:一个队列id

参数:

key:标示符,也称为键。可理解为唯一的端口获取key的方法:

函数
ftok(const char* path,int id)


ftok的功能由一个路径名和项目id产生一个键

如:key = ftok(“.”, ‘t’);

flag:控制标记,指定以何种方式创建

如:0660 | IPC_CREAT

int msgsnd(int  msqid, const void * ptr, size_t  nbytes, int flag)


头文件:
<sys/msg.h>


功能:往消息队列写消息,即发送消息。

参数:

msqid:队列id

ptr:要发送消息的结构指针,指向消息的地址

nbytes:发送的字节数

flag:控制标记,一般指定为IPC_NOWAIT

int msgrcv(int  msqid, const void * ptr, size_t nbytes ,long  type, int flag) ;


头文件:
<sys/msg.h>


功能:从消息队列读消息,即接收消息。

参数:

msqid:队列id

ptr:接收的消息保存到该指针指向的消息结构

nbytes:接收的字节数

type

==0返回队列第一个消息

>
0返回队列中类型为type的第一个消息

<0返回小于或等于type绝对值的消息

flag:控制标记,一般指定为IPC_NOWAIT

练习内容

父进程创建消息队列和两个子进程p1和p2
子进程p1打开给定文件(如果没有,则创建文件),并向文件中写数据,写完关闭文件,然后向消息队列写入一条消息“ok”,目的是通知进程p2可以读取文件内容了。
子进程p2从消息队列读取消息,如果收到消息“ok”,则打开文件,读取文件内容,并将其输出道屏幕上,关闭文件。


实现代码

/*父进程创建消息队列和两个子进程p1和p2
子进程p1打开给定文件(如果没有,则创建文件),并向文件中写数据,写完关闭文件,然后向消息队列写入一条消息“ok”,目的是通知进程p2可以读取文件内容了。
子进程p2从消息队列读取消息,如果收到消息“ok”,则打开文件,读取文件内容,并将其输出道屏幕上,关闭文件。*/
#include<stdio.h>
#include<sys/msg.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#define NUM 100
//消息通信
//消息队列提供了一种由一个进程向另一个进程发送块数据的方法。
//每一个数据块被看作有一个类型,接收进程可以独立接收具有不同类型的数据块。
struct mymsg
{
long mytype; //存储消息类型
char mytext[NUM];//存储消息内容
};

int main()
{
FILE *f; //文件指针
pid_t p1,p2;// 两个进程
key_t key;
key = ftok(".", 't');
//系统IPC键值的格式转换函数
char s[20];
int mgsid;
if((mgsid=msgget(key,IPC_CREAT|0666))==-1)//打开一个队列或创建一个新队列

{
printf("creat error\n");
return -1;
}

p1=fork();
if(p1<0)
{
fprintf(stderr,"fork failed");
exit(-1);
}
else if(p1==0)
{
printf("p1 pid is:%d\n",getpid()); //p1id
printf("sending the massage...\n");
sleep(1); //本线程休眠1毫秒
struct mymsg msg1;
msg1.mytype=getppid(); //父进程id
if((f=fopen("hello.txt","w"))==NULL) //打开文件失败
{
printf("the file %s not open\n","hello.txt");
return ;
}
fputs("hello!",f);//送一个字符到一个流中
fclose(f);
strcpy(msg1.mytext,"ok");
if(msgsnd(mgsid,&msg1,sizeof(msg1.mytext),0)<0)//往消息队列写消息,即发送消息。

{
printf("sending error!\n");
exit(-1);
}
else
{
printf("complete sending !\n");
exit(0);
}

}
else
{
wait(NULL);
p2=fork();
if(p2<0)
{
printf("fork creat error!\n");
exit(1);
}
else if(p2==0)
{
printf("p2 pid is:%d\n",getpid());
printf("Receiving the massage...\n");
sleep(1);//本线程休眠1毫秒
struct mymsg msg2;
msg2.mytype=getppid();
if(msgrcv(mgsid,&msg2,NUM,getppid(),0)<0)//从消息队列读消息,即接收消息
{
printf("receving error!!!\n");
exit(1);
}
else
{
printf("complete receving \n");

if(strcmp("ok",msg2.mytext)==0)
{
if((f=fopen("hello.txt","r"))==NULL)
{
printf("the file %s no opend.\n","hello.txt");
return;
}
while((fgets(s,20,f))!=NULL)//从流中读一行或指定个字符
{
printf("the massage is:%s\n",s);
}
fclose(f);
}
}

}
else
{
wait(NULL);
exit(0);
}
}
return 0;
}


本博客内容到此介绍,欢迎指正!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息