管道和消息队列实现进程间通信
管道
基础知识
管道
就像现实中的水管,水就像数据。
管道是一种半双工的通信方式
数据只能单向流动,而且只能在具有共同祖先的进程间使用
函数介绍
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_CREATint msgsnd(int msqid, const void * ptr, size_t nbytes, int flag)
头文件:
<sys/msg.h>
功能:往消息队列写消息,即发送消息。
参数:
msqid:队列id
ptr:要发送消息的结构指针,指向消息的地址
nbytes:发送的字节数
flag:控制标记,一般指定为IPC_NOWAITint 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;
}
本博客内容到此介绍,欢迎指正!