命名管道,让无血缘进程通信

命名管道:

是有标识符的管道,其他进程可以通过管道标识符寻找到管道,以p开头的文件
匿名的是竖划线

  • 匿名管道通过子进程继承父进程做到的
    fork函数中,父子进程通过管道通信的实质是fork会 继承 文件描述符表的特性做到的
  • 命名管道通过管道文件,文件在磁盘上有唯一的路径,通过路径找到对应的资源
    命名管道中,两个进程打开磁盘上的文件在内存中只打开了一份,通信时候数据不会刷新到磁盘上,磁盘上的文件进行了符号处理,识别成管道文件
    通过文件的路径,路径具有唯一性,多个进程可以通过路径打开同一个文件,同一个文件中的inode,就是同一个缓冲区

两个进程要通信先让他们看到同一个路径
一个进程把管道文件创建好了,另一个进程不需要创建

mkfifo函数

  • 第一个参数文件路径
  • 第二个参数文件权限

函数创建,mkfifo(库函数)

命名管道 android 命名管道文件_linux

对端不写且退出,客户端是写端如何退出
不加1,\0是c的标准

头文件

#pragma once

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define IPC_PATH "./.fifo" //命名管道的路径

服务端

#include "comm.h"

using namespace std;

int main()
{
    umask(0);
    if(mkfifo(IPC_PATH, 0600) != 0)
    {
        cerr << "mkfifo error" << endl;
        return 1;
    }

    int pipeFd = open(IPC_PATH, O_RDONLY);

    //正常的通信过程
    char buffer[1024];
    while(true)
    {
        ssize_t s = read(pipeFd, buffer, sizeof(buffer)-1);
        if(s > 0)
        {
            buffer[s] = '\0';
            cout << "客户端->服务器# " << buffer << endl;
        }
        else if(s == 0)//如果管道中没有数据,那就阻塞了,如果返回0,证明对端关闭了
        {
            cout << "服务的退出";
            break;
        }
        else
        {
            //do nothing
            cout << "read: " << strerror(errno) << endl;
            break;
        }
    }


    close(pipeFd);
    cout << "服务端退出啦" << endl;
    unlink(IPC_PATH);
    return 0;
}

客户端进程

#include "comm.h"

using namespace std;

int main()
{
    int pipeFd = open(IPC_PATH, O_WRONLY);
    if(pipeFd < 0)
    {
        cerr << "open: " << strerror(errno) << endl;
        return 1;
    }

#define NUM 1024
    char line[NUM];
    while(true)
    {
        printf("请输入你的消息# ");
        fflush(stdout);
        memset(line, 0, sizeof(line));
        // fgets -> C -> line结尾自动添加\0
        if(fgets(line, sizeof(line), stdin) != nullptr)
        {
            //abcd\n\0
            line[strlen(line) - 1] = '\0';
            write(pipeFd, line, strlen(line));
        }
        else
        {
            break;
        }
    }
    close(pipeFd);
    cout << "客户端退出啦" << endl;
    return 0;
}

命令创建:

mkfifo +文件名 → 创建一个管道文件,文件类型标识符是p,p代表管道文件

命名管道 android 命名管道文件_#include_02

管道文件的性质:

  • 管道是一块内存,并不是进程操作文件往文件中去读写,
  • 管道文件作用就是标识内核中的buffer;
  • 或者说,不同进程就能通过管道文件内核中的管道
用read函数操作管道文件和普通文件操作一样

命名管道 android 命名管道文件_管道文件_03

先左边读,右边写,左边可执行程序在open时被阻塞

命名管道 android 命名管道文件_#include_04

左边后读,右边先写运行成功

命名管道 android 命名管道文件_linux_05

  • 这个管道只适用于同一主机下的进程间通信,不适于不同主机下的进程间通信;
  • 不同主机间的通信就是用网络实现的

向管道中写的时候,小于pipe_buf(4096)会保证原子性(多线程)