解析命名管道:实现进程间通信的无名英雄

发布于:2024-05-09 ⋅ 阅读:(24) ⋅ 点赞:(0)

序言

让无血缘关系的进程间通信

(所谓血缘关系就是上篇blog所讲的父子进程,子进程的子进程,父进程的两个子进程之间的关系等这种即为血缘关系).

linux中命名管道叫做fifo

利用指令创建

在这里插入图片描述
演示结果:

输出信息,实现通信
在这里插入图片描述

本来输出到次终端的信息,到了另一个终端
循环实现
在这里插入图片描述echo和cat两个无相关的进程实现了通信
实现上述操作的时候再开一个终端发现,管道的大小一直是0
在这里插入图片描述

这个管道是创建出来的磁盘级的一个符号,实际通信不会向磁盘刷新,也没必要

命名管道本质

现在存在一个命名管道,A,B两个进程分别用读和写的方式打开,他俩看到了一份公共的资源

为什么能看到同一份资源?

路径具有唯一性,使用路径+文件名的方式,来唯一的让不同的进程看到同一份资源

在这里插入图片描述

现在A进程的实际执行情况是,打开了这个
文件,也有对应的文件缓冲区指向这个文件
B进程打开就不会再重新加载这个文件到
缓冲区之类的
但毕竟是不同的进程打开
他们各自进程指向的文件中的位置不一样

现在这个文件不是普通文件,而是mkfifo创建的管道文件,
所以此时文件不会指向对应的缓冲区,而是直接让两个进程在缓冲区进行交流

代码创建管道实现通信

makefifo创建命名管道文件
利用client和server两个进程来查看

.PHONY:all
all:server client

server:server.cc
	g++ -o $@ $^ -std=c++11
client:client.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f server client .fifo

通过代码在server创建管道
在这里插入图片描述

创建管道的发送服务端:
在这里插入图片描述

创建管道,创建失败返回错误信息
errno:向显示器输出错误信息(stderr)
strerror:strerror(errno): 接受错误码作为参数,并返回对应错误码的字符串描述

make生成

在这里插入图片描述

然后运行这个server服务创建管道:

在这里插入图片描述
read函数从指定文件中读取一个长度放回buf中,然回值是读取到的字符串的长度

在这里插入图片描述
读端代码(服务端)

在这里插入图片描述
写端代码(客户端)

在这里插入图片描述
最终两个.cc文件汇聚到一个.h内
为了让他们看到同一份资源
在这里插入图片描述

进行实践时,首先一定要打开读端,以前说过写端关闭,读端一直读,读端会读到文件的结尾,表示为0,即这时read的返回值为0.但是这里不会读到0,因为就要考虑这种情况,读端一直打开,写端没有时,会进行堵塞等待(个人猜测时mkfifo函数的功能导致阻塞),一定要先./server 然后再./client,再在./client端输入信息
在这里插入图片描述

这段代码是按下回车进行输出,通过这个例子可以通过某些函数接口实现实时信息输出(即按什么立刻显示什么,不用按回车)

上述代码不合理的地方,当fifo存在时会直接报错退出,现在实现不存在就创建,存在就不创建然后执行后续代码
在这里插入图片描述

完整代码

comm.h

#include<iostream>
#include<unistd.h>
#include<vector>
#include<cstring>
#include<cerrno>
#include <sys/types.h>
#include <sys/stat.h>
#include<fcntl.h>
#define FIFONAME ".fifo"//生成的文件默认为隐藏文件
using namespace std;

server.cc

#include "comm.h"
bool MakeFifo()
{
    int n = mkfifo(FIFONAME, 0666);//创建管道
    if(n < 0)
    {
        cerr << "errno:" << errno << ", errstring: " << strerror(errno) << endl;
        return false;
    }
    return true;
}
int main()
{
Strat:
    int rfd = open(FIFONAME,O_RDONLY);//打开管道
    if(rfd < 0)
    {
        if(MakeFifo()) goto Strat;
        cerr << "errno:" << errno << ", errstring: " << strerror(errno) << endl;
        return 1;
    }
    char buf[1024];
    while(true)//读端
    {
        ssize_t s = read(rfd,buf,sizeof(buf)-1);
        if(s > 0)
        {
            buf[s] = 0;
            cout << "Client say# " << buf << endl;
        }
        else if(s == 0)
        {
            cout << "client quit,server quit too!" << endl;
            break;
        }
    }

    close(rfd);
    return 0;
}

client.cc

#include"comm.h"
int main()
{
    int wfd = open(FIFONAME,O_WRONLY);//打开管道文件
    if(wfd < 0)
    {
        cerr << "errno:" << errno << ", errstring: " << strerror(errno) << endl;
        return 1;
    }
    //将信息写入管道
    string message;
    while(true)
    {
        cout << "Please Enter# " << endl;
        getline(cin,message);//从键盘获取message
        
        //将message写入管道
        ssize_t s = write(wfd,message.c_str(),message.size());
        if(s < 0)
        {
            cerr << "errno:" << errno << ", errstring: " << strerror(errno) << endl;
            break;
        }


    }

    close(wfd);
    return 0;
}

本期blog到此结束,有用请三连~~

欢迎随时交流~~