小工具知识网

当前位置: 首页 > 行业知识 > 波奇学Linux:进程通信之命名管道

行业知识

波奇学Linux:进程通信之命名管道

2026-10-30 16:07:17 来源:互联网转载

进���通信的前提:让不同的进程看到同一份文件

匿名管道只能具有血缘关系的进程,毫不相关的进程通信得要命名管道

管道文件不需要刷盘,基于内存级文件

命名管道通过路径+文件名确定打开同一个文件,在匿名管道中利用父子进程。

创建命名管道进程

管道大小为0,意味在磁盘占据的大小为0,所以它数据不刷盘

把数据重定向到有名管道,进程阻塞直到,数据被提取 

 当myfifo的数据被提取此时进程才结束阻塞

在上面的例子中cat 和echo相当于两个bash进程,myfifo在两个进程中实现通信

如果是两个手写的可执行程序该如何实现通信,红色的线代表不存在

结构上我们依然使用和匿名管道一样的结构,只是两个进程和file的关系要手动建立

手写程序实现进程通信

利用mkfifo创建管道文件

实现思路

1.创建管道文件

2.通过相同的文件名,client端写入信息到文件,server端从文件中读取信息。(上面的信息client端和server的红黑线画反了)

client代码

#include "comm.hpp"int main(){    //创建管道文件    int result=mkfifo(Pathname,Mode);    if(result)    {        perror("Create mkfifo error\n"); // Create mkfifo error: xxx    }    printf("successfully create file\n");    // 进程连接管道    int fd=open(Pathname,O_WRONLY);    if (fd==-1)    {        perror("open error\n");    }    // 写入文件    string line;    while (1)    {        printf("please enter your message: \n");        getline(cin,line);        printf("\n");        printf("client send message: %s\n",line.c_str());        ssize_t num=write(fd,line.c_str(),line.size());//count =size是因为char型是一个字节        if(num==-1)        {            perror("Write error\n");            break;        }        if(num==0)        {            break;        }    }    // 删除管道 当写端删除时,读端读到0,自动接结束    close(fd);    printf("Delete file\n");    int n=unlink(Pathname);    if(n!=0)    {        perror("Delete file error\n");    }    return 0;}

server代码

#include"comm.hpp"int main(){    int fd=open(Pathname,O_RDONLY);    if (fd==-1)    {        perror("open error\n");    }    //读取    char* buff[Size];        while(1)    {           ssize_t num=read(fd,buff,Size); //会等待write输入         if(num==0)        {            printf("client quit now, me too\n");            break;        }        else if(num==-1)        {            //num == -1            printf("read error\n");            break;        }        else {            buff[num]=0;            printf("receive message %s from client\n",buff);        }    }   return 0;}

头文件comm.hpp代码 

//引用共同的同文件可以使得管道名共享#pragma once#include#include#include #include#include#include#include#define Mode 0664#define Pathname "./myfifo"#define Size 1024using namespace std;

代码细节点

在文件写入的printf要\n刷新,在server读取前就强制刷新出来。

unlink的作用是删除管道文件,不然再次运行时会触发mkfifo创建失败,理由是file exists

在client close(fd)关闭写端,读端读取到0,自动退出

getline的作用是读取空格

写端直接输入换行符刷新缓冲区,读端读取到0

Pathname表示文件路径+文件名 ./myfifo 和myfifo 等架 ../myfifo则在上一级目录中生成

read函数会处于阻塞状态,直到write函数写入文件或直接刷新缓冲区

代码改进点

1.可以利用类对象析构函数的自动调用,来保证unlink函数调用。

2.当触发错误,此时应退出进程,所以可以自定义错误码并用exit函数退出。

3.管道不应该由client创建,而是server服务端创建,用户不该创建管道,而是由软件的服务端创建好等待用户输入。 

修改后部分的代码

comm.hpp代码

enum{    OPEN_FILE_ERROR=1, //由1开始向后排    CREATE_FILE_ERROR,    DELETE_FILE_ERROR,    READ_FILE_ERROR,    WRITE_FILE_ERROR    };class Pipe{public:    Pipe()    {        //创建管道文件    int result=mkfifo(Pathname,Mode);    if(result)    {        perror("Create mkfifo error\n"); // Create mkfifo error: xxx        exit(CREATE_FILE_ERROR);    }    printf("successfully create file\n");    }    ~Pipe()    {        printf("Delete file\n");        int n=unlink(Pathname);        if(n!=0)        {            perror("Delete file error\n");            exit(DELETE_FILE_ERROR);        }    }  };

server.cc代码

#include"comm.hpp"int main(){    Pipe p();    int fd=open(Pathname,O_RDONLY);    if (fd==-1)    {        perror("open error\n");        exit(OPEN_FILE_ERROR);    }    //读取    char* buff[Size];        while(1)    {           ssize_t num=read(fd,buff,Size); //会等待write输入         if(num==0)        {            printf("client quit now, me too\n");            break;        }        else if(num==-1)        {            //num == -1            printf("read error\n");            exit(READ_FILE_ERROR);            break;        }        else {            buff[num]=0;            printf("receive message %s from client\n",buff);        }    }   return 0;}

上一篇:mac怎么传ftp服务器?

下一篇:揭秘高效、稳定、灵活的pgsql数据库数据存储利器