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 下最经典、最基础的进程间解耦通信方式之一。

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

相关推荐
进击的雷神3 小时前
想法直接变视频,角色还不崩?我上手测了下这个多智能体框架 ViMax
ai·视频·短剧·vimax
筝筝ba3 小时前
openBMC功耗封顶GUI测试
服务器
镜象科技4 小时前
AI临床心理大模型:离临床最近的AI长什么样?
人工智能
zhanghaha13144 小时前
AI Agent_4 Agent工作原理详解:从“会聊天“到“会办事“
人工智能
DeepAgent4 小时前
AI Agent 工程实践(49):一次真实优化——从 Agent v1 到 v2
开发语言·人工智能·agent
分布式存储与RustFS4 小时前
纠删码到底吃掉了多少容量:EC:4、EC:8 的利用率与容错怎么算
运维·云原生·开源·对象存储·分布式存储·s3·性能基准
小蒋观天下4 小时前
两轮车检测AI摄像头——2026-2030年未来市场规模、增长逻辑与行业天花板
大数据·人工智能·安全·计算机视觉·ai大模型
美狐美颜sdk4 小时前
直播APP接入美颜SDK后出现卡顿怎么办?从性能瓶颈寻找解决方案
android·人工智能·音视频·美颜sdk·直播美颜sdk
遇见小修修4 小时前
3步解决打印机0x00000709报错 ,广州深圳程序员可自行排查
运维·打印机·维修·打印机报错·打印机运维·打印机故障自查
m0_587383004 小时前
深圳24小时自助健身房解决方案实战:从系统架构到部署指南
java·人工智能·spring boot·spring·系统架构·需求分析