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

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

管道

基础知识

  • 管道

    就像现实中的水管,水就像数据。
    管道是一种半双工的通信方式
    数据只能单向流动,而且只能在具有共同祖先的进程间使用

函数介绍

  • 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;
}

本博客内容到此介绍,欢迎指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Summer tree

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值