Linux命名管道:跨进程通信实战指南|附源码

上篇文章:进程池构建:高效任务派发与隐藏BUG揭秘全解析

目录

引言:为什么需要命名管道?

1.创建一个命名管道

1.1命令行创建

1.2代码中创建

2.匿名管道与命名管道的区别

3.命名管道的打开规则与同步机制

[4.C++ 实战:编写一个简单的 Server-Client 聊天程序](#4.C++ 实战:编写一个简单的 Server-Client 聊天程序)

代码:

总结


引言:为什么需要命名管道?

在学习进程间通信(IPC)时,我们通常首先接触的是匿名管道 (Anonymous Pipe) 。匿名管道简单好用,但它有一个致命的限制:只能在具有共同祖先(具有亲缘关系,如父子进程)的进程间通信。

那么,如果我们在系统中运行了两个完全不相关的进程,它们也想通过管道交换数据,该怎么办呢?这时候,命名管道(Named Pipe,也叫 FIFO)就闪亮登场了!

命名管道是一种特殊类型的文件,它在文件系统中有一个真实存在的路径名。因此,任何进程只要有权限访问该路径,就可以通过它进行通信。

1.创建一个命名管道

命名管道可以通过命令行创建,也可以在代码中动态创建。

1.1命令行创建

在终端中,我们可以使用 mkfifo 命令来创建一个命名管道:

复制代码
$mkfifo my_fifo$ ls -l my_fifo 
prw-rw-r-- 1 user user 0 Oct 24 10:00 my_fifo

注意观察 ls -l 的输出,文件类型是 p,代表 Pipe(管道文件)。

1.2代码中创建

在 C/C++ 程序中,我们使用 mkfifo 函数:

cpp 复制代码
#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
  • pathname: 管道文件的路径/名字。

  • mode : 文件的权限(例如 0666),实际权限会受系统的 umask 影响。

  • 返回值 : 成功返回 0,失败返回 -1 并设置 errno

创建命名管道:

cpp 复制代码
int main(int argc, char *argv[])
{
    mkfifo("p2", 0644);
    return 0;
}

2.匿名管道与命名管道的区别

很多初学者会疑惑,命名管道既然是个"文件",那通信时是不是把数据写到磁盘上了?速度会不会很慢?

答案是:绝对不会! 它们之间的区别仅仅在于"打开方式",底层通信介质其实是一样的。

对比维度 匿名管道 (Pipe) 命名管道 (FIFO)
创建方式 pipe() 函数 mkfifo 命令或函数
打开方式 创建时自动打开 使用 open() 函数像普通文件一样打开
适用范围 仅限有亲缘关系的进程 任意两个进程(只要能访问同一路径)
底层原理 内存级别的缓冲区,不写磁盘 文件系统中存在路径节点,但数据仍然只存在于内存缓冲区,不写磁盘

3.命名管道的打开规则与同步机制

命名管道自带同步机制,什么是同步?简单来说,就是"互相等待"。在使用open()函数时打开命名管道,有非常严格的阻塞规则:

  1. 如果你是为了"读"而打开 (O_RDONLY):

    • 阻塞模式(默认) :程序会卡在 open() 这里阻塞住,直到有另一个进程为了"写"打开了这个管道,程序才会继续往下走。

    • 非阻塞模式 (O_NONBLOCK):立刻返回成功。

  2. 如果你是为了"写"而打开 (O_WRONLY):

    • 阻塞模式(默认) :程序会卡在 open() 这里阻塞住,直到有另一个进程为了"读"打开了这个管道。

    • 非阻塞模式 (O_NONBLOCK) :立刻返回失败,错误码为 ENXIO(因为没人读,你写了也没意义)。

总结一句话:写端必须等读端,读端必须等写端。双方都准备好(都 open 成功)了,才能愉快地通信!

4.C++ 实战:编写一个简单的 Server-Client 聊天程序

接下来我们用代码实现一个单向通信:Client 发送消息,Server 接收并打印。

结果:

代码:

Makefile:

cpp 复制代码
all:client server
client:client.cc
	g++ -o $@ $^ -std=c++11
server:server.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f client server

comm.h:

定义管道的名称,方便两边统一。

cpp 复制代码
#pragma once
#include <string>

const std::string fifoname = "fifo";

server.cc

服务器/读端,服务器负责创建管道、读取数据,并在结束后清理管道文件。

cpp 复制代码
#include <iostream>
#include <cstdio>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include "comm.h"

int main()
{
    // 1.创建管道文件
    umask(0);
    int n = mkfifo(fifoname.c_str(), 0666);
    if(n < 0)
    {
        perror("mkfifo");
        return 1;
    }

    // 2.打开管道文件
    std::cout << "open begin" << std::endl;
    int rfd = open(fifoname.c_str(), O_RDONLY);
    if(rfd < 0)
    {
        perror("open");
        return 2;
    }
    std::cout << "open end" << std::endl;

    char inbuffer[1024];
    // 3.进行通信
    while(true)
    {
        ssize_t n = read(rfd, inbuffer, sizeof(inbuffer)-1);
        if(n > 0)
        {
            inbuffer[n] = 0;
            std::cout << "client say# " << inbuffer << std::endl;
        }
        else if(n == 0)
        {
            // 写端关闭
            std::cout << "client quit, me too" << std::endl;
            break;
        }
        else
        {
            perror("read");
            break;
        }
    }

    // 关闭
    close(rfd);

    // 删除管道文件
    unlink(fifoname.c_str());

    return 0;
}

client.cc

客户端,直接打开以存在的管道文件,并发送终端输入的数据。

cpp 复制代码
#include <iostream>
#include <string>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "comm.h"

int main()
{
    std::cout << "open begin" << std::endl;
    int wfd = open(fifoname.c_str(), O_WRONLY);
    if(wfd < 0)
    {
        perror("open");
        return 1;
    }
    std::cout << "open end" << std::endl;

    std::string outstring;
    while(true)
    {
        std::cout << "Please Enter@ ";
        std::cin >> outstring;
        write(wfd, outstring.c_str(), outstring.size()); // 不需要写\0
    }
    close(wfd);

    return 0;
}

总结

通过这篇文章,我们了解到命名管道(FIFO)打破了匿名管道必须具有亲缘关系的限制 。虽然它以"文件"的形式存在于文件系统中,但其本质依然是内核中的一块内存缓冲区。掌握了命名管道的阻塞规则和使用方法,我们就掌握了 Linux 下最经典、最基础的进程间解耦通信方式之一。

如果你觉得这篇文章对你有帮助,欢迎点赞收藏!如有疑问,也欢迎在评论区留言讨论。

相关推荐
WiChP1 小时前
【V0.1B8】从零开始的2D游戏引擎开发之路
服务器·数据库·mysql
楼田莉子2 小时前
仿Muduo的高并发服务器:Http协议模块
linux·服务器·c++·后端·学习
斯班奇的好朋友阿法法8 小时前
中科方德(NFSChina)离线安装 RPM 包
运维
agicall.com8 小时前
座机通话双方语音分离技术解决方案详解
人工智能·语音识别·信创电话助手·座机语音转文字·固话座机录音转文字
AI机器学习算法8 小时前
《动手学深度学习PyTorch版》笔记
人工智能·学习·机器学习
milo.qu8 小时前
RockyLinux9.7 docker部署Jisti Meet
linux·docker·容器
GanGanGanGan_8 小时前
CentOS 7.9 glibc 2.17 源码编译升级到 glibc 2.31
linux·运维·centos·glibc
Goboy8 小时前
「我的第一次移动端 AI 办公」TRAE SOLO 三端联动, 通勤路上就把活干了,这设计,老罗看了都想当场退役
人工智能·ai编程·trae
qq_452396238 小时前
第二十篇:《UI自动化测试的未来:AI驱动的智能测试与低代码平台》
人工智能·低代码·ui