Linux命名管道(FIFO)通信:从原理到实操,一文搞懂跨进程通信

Linux命名管道(FIFO)通信:从原理到实操,一文搞懂跨进程通信

🔥个人主页:Cx330🌸

❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》

《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔

《Git深度解析》:版本管理实战全解

🌟心向往之行必能至


🎥Cx330🌸的简介:


目录

前言:

一、先搞懂:命名管道(FIFO)是什么?

1. 命名管道的本质

2. 命名管道的核心特点

3. 命名管道与匿名管道的对比

二. 命名管道的创建方式

2.1 命令行创建(mkfifo 命令)

2.2 代码创建(mkfifo 函数)

2.3 命名管道的打开规则

三、实操实现:手搓命名管道通信

3.1 前置准备(Makefile && comm.h)

3.2 服务端程序(server.c)

3.3 客户端程序(client.c)

3.4 编译运行

四、命名管道的实际应用场景

五、常见坑点总结(必看)

六、总结


前言:

在Linux进程间通信(IPC)中,管道是最基础、最常用的通信方式之一,但我们平时接触的“匿名管道”有一个致命缺陷——只能用于有血缘关系的进程(父进程与子进程、兄弟进程),无法实现无关联进程间的通信。

命名管道(简称FIFO),正是为解决这个问题而生。它给管道赋予了一个“文件名”,通过文件系统作为媒介,让任意两个进程(无论是否有血缘关系)都能通过这个“命名”实现数据交互。今天这篇博客,就从原理到实操,手把手教你搞懂Linux命名管道,亲手实现跨进程通信。

一、先搞懂:命名管道(FIFO)是什么?

1. 命名管道的本质

命名管道和匿名管道的核心通信逻辑完全一致——都是基于“字节流”的半双工通信(同一时刻只能单向传输数据),底层都是内核中的缓冲区。

两者的核心区别的是:命名管道有一个可见的文件系统入口(一个以.fifo或.pipe为后缀的文件),这个文件只是一个“标识”,不存储实际数据(数据仍在内存缓冲区中);而匿名管道没有文件入口,只能通过父子进程继承文件描述符来通信

简单来说:匿名管道是“隐式的、血缘专属”,命名管道是“显式的、通用的”,只要知道FIFO文件的路径,任意进程都能与之通信。

2. 命名管道的核心特点

  • 跨进程通信:无血缘关系的进程(如两个独立的程序),只要知道FIFO文件路径,就能通过它通信;
  • 半双工通信:数据只能单向流动,若要实现双向通信,需要创建两个FIFO(一个用于A→B,一个用于B→A);
  • 基于文件标识:FIFO文件存在于文件系统中,进程通过打开这个文件,获取读写文件描述符,进而实现通信;
  • 阻塞特性:默认情况下,打开FIFO的读端会阻塞,直到有进程打开写端;打开写端也会阻塞,直到有进程打开读端(可通过O_NONBLOCK设置为非阻塞);
  • 生命周期:FIFO文件不会随进程退出而自动删除,需要手动删除(rm命令或unlink函数),否则会一直存在于文件系统中。

3. 命名管道与匿名管道的对比

为了更清晰区分,做一个简单对比表,避免混淆:

对比维度

匿名管道(pipe)

命名管道(FIFO)

通信范围

有血缘关系的进程(父子、兄弟)

任意进程(无血缘关系也可)

标识方式

无文件标识,通过文件描述符继承

有文件系统入口(FIFO文件)

创建方式

通过pipe()系统调用创建

通过mkfifo()系统调用或mkfifo命令创建

生命周期

随进程退出而销毁(文件描述符关闭)

随FIFO文件删除而销毁,进程退出不影响文件

使用场景

父子进程间简单通信(如shell管道)

无关联进程间通信(如两个独立程序交互)


二. 命名管道的创建方式

命名管道有两种创建方式:命令行创建和代码创建,本质都是在文件系统中生成一个 FIFO 类型的文件。

2.1 命令行创建(mkfifo 命令)

直接通过mkfifo命令创建命名管道,语法简单,适合快速测试:

# 创建名为myfifo的命名管道 mkfifo myfifo # 查看管道文件(类型标识为p) ls -l myfifo 
我们可以发现,这里创建的管道文件(最前面的标识是p开头),就算我们是不停的在往里面进行写操作,但是它的文件大小是一直不变的,验证了我们上面图中所说的它不是一个普通文件。

2.2 代码创建(mkfifo 函数)

通过 mkfifo()系统调用在代码中创建命名管道,需指定管道路径和权限,原型如下:

#include <sys/stat.h> #include <sys/types.h> // 参数:pathname-管道路径;mode-权限(如0644) int mkfifo(const char *pathname, mode_t mode); 

代码示例(创建命名管道)

#include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <errno.h> #define FIFO_PATH "./myfifo" int main() { // 创建命名管道,权限0644(所有者读+写,其他读) int ret = mkfifo(FIFO_PATH, 0644); if (ret == -1) { // 若管道已存在,errno为EEXIST,可忽略该错误 if (errno != EEXIST) { perror("mkfifo error"); return 1; } printf("命名管道已存在\n"); } else { printf("命名管道创建成功\n"); } return 0; } 

2.3 命名管道的打开规则

命名管道的打开(open())行为与普通文件不同,核心是 “读端与写端的同步”—— 仅当管道的读端和写端都被打开后,通信才能正常进行,具体规则如下:

  • O_RDONLY:以只读方式打开(读端),默认阻塞,直到有进程以O_WRONLY方式打开;
  • O_WRONLY:以只写方式打开(写端),默认阻塞,直到有进程以O_RDONLY方式打开;
  • O_RDWR:以读写方式打开,不阻塞,直接打开(同时具备读和写权限,可实现单向通信的 “自我循环”)
注意:实际开发中,建议读端O_RDONLY打开,写端O_WRONLY打开,避免使用O_RDWR(可能导致通信逻辑混乱)。

三、实操实现:手搓命名管道通信

  • server.c:作为服务端,监听管道,接收客户端消息并打印;
  • client.c:作为客户端,向管道发送消息,实现双向交互。

3.1 前置准备(Makefile && comm.h)

Makefile:

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:

#pragma once #include <string> const std::string fifoname="fifo";

3.2 服务端程序(server.c)

#include <iostream> #include <cstdio> #include <sys/types.h> #include <sys/stat.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.打开管道文件 int rfd=open(fifoname.c_str(),O_RDONLY); if(rfd<0) { perror("open"); return 2; } 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) { // 写端关闭了 break; } else { perror("read"); break; } } // 4.关闭 close(rfd); // 5.删除管道文件 unlink(fifoname.c_str()); return 0; }

3.3 客户端程序(client.c)

#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() { int wfd=open(fifoname.c_str(),O_WRONLY); if(wfd<0) { perror("open"); return 1; } std::string outstring; while(true) { std::cout<<"Please Enter@ "; std::cin>>outstring; write(wfd,outstring.c_str(),outstring.size());// 要不要写'\0'? 不需要! } close(wfd); return 0; }

3.4 编译运行


四、命名管道的实际应用场景

命名管道因其“跨进程、简单易用”的特点,在Linux开发中应用广泛,常见场景包括:

  • 后台服务与前台程序通信:如后台守护进程(如日志服务),通过FIFO接收前台程序发送的日志指令,实现日志的动态控制;
  • 多个独立程序的数据交互:如两个不同的服务程序(如数据库服务和缓存服务),通过FIFO传递数据,无需复杂的IPC机制;
  • shell脚本间通信:在shell脚本中,通过mkfifo命令创建FIFO,实现两个脚本之间的数据传输(比匿名管道更灵活);
  • 简单的客户端/服务器模型:基于FIFO实现简单的C/S架构,客户端向FIFO写入请求,服务器从FIFO读取请求并处理,返回结果。

五、常见坑点总结(必看)

  • 忘记处理阻塞:打开FIFO时未设置O_NONBLOCK,导致进程一直阻塞,误以为程序卡死;
  • 忽略SIGPIPE信号:读端关闭后,写端继续写入,导致进程崩溃;
  • FIFO文件残留:未用unlink()删除FIFO,下次创建时报错;
  • 路径不一致:读端和写端使用的FIFO路径不同,导致无法通信;
  • 权限问题:mkfifo()设置的权限过低(如0600),导致其他进程无法打开FIFO,建议设置为0666;
  • 数据错乱:未处理字节流无边界问题,读端读取的数据不完整或拼接错误。

六、总结

命名管道(FIFO)是Linux跨进程通信的基础方式,核心是“通过文件系统标识,实现任意进程间的字节流通信”。它继承了匿名管道的简单性,又解决了匿名管道“只能用于血缘进程”的缺陷,是入门IPC开发的必学知识点。

本文通过“原理→API→实操→避坑”的流程,手把手教你实现命名管道的单方向和双向通信,代码可直接复用。重点掌握FIFO的阻塞特性、SIGPIPE信号处理和文件删除问题,就能避免绝大多数踩坑场景。

实际开发中,若需要更高效、更复杂的通信(如消息优先级、双向通信的更优实现),可后续学习消息队列、共享内存等IPC机制,但命名管道的核心思想(字节流、文件标识),是理解所有IPC机制的基础。

最后,附上本文所有实操代码的仓库链接,方便大家下载测试和修改:fifo

Read more

轨迹数据压缩的Douglas-Peucker算法(附代码及原始数据)

机场出租车调度问题:数学建模实战解析 大家好!今天咱们来聊聊一个特别接地气的数学建模题目——机场的出租车调度问题。这是2019年全国大学生数学建模竞赛的C题,题目看着简单,实际上藏着不少玄机。咱们一起拆解这个题目,看看怎么用数学模型来解决现实生活中的难题。 问题背景:机场出租车的那些事儿 想象一下你刚从飞机下来,拖着行李箱走到出租车候客区,发现有两条队:一条是"短途专用通道",另一条是普通队。为什么会有这样的设计?背后其实是一套复杂的调度系统在运作。 题目给我们几个核心信息点: 1.大多数机场出租车司机会在"蓄车池"排队等待 2.机场管理人员会采集乘客目的地信息 3.对于短途乘客(比如目的地小于某个阈值d),会给司机"补偿"或安排他们优先接客 4.司机可以自主选择是否去"短途专用通道"排队 核心问题就是要我们设计一套合理的调度方案,在乘客等候时间、司机收益和机场管理效率之间找到平衡。 技术原理:排队论与博弈论的双剑合璧

By Ne0inhk
动态规划 线性 DP 五大经典模型:LIS、LCS、合唱队形、编辑距离 详解与模板

动态规划 线性 DP 五大经典模型:LIS、LCS、合唱队形、编辑距离 详解与模板

文章目录 * 最长上升子序列 * 【模板】最长上升子序列 * 合唱队形 * 牛可乐和最长公共子序列 * 编辑距离 经典线性 dp 问题有两个:最⻓上升⼦序列(简称:LIS)以及最⻓公共⼦序列(简称:LCS),这两道题⽬的很多⽅⾯都是可以作为经验,运⽤到别的题⽬中。⽐如:解题思路,定义状态表⽰的⽅式,推到状态转移⽅程的技巧等等。 因此,这两道经典问题是需要我们重点掌握的。 最长上升子序列 题目描述 题目解析 本题介绍最长上升子序列的一般解法,当数据量不大时用这种解法。 在此之前,小编先区分一下子数组和子序列,子数组需要是连续的,而子序列可以是间断的。 1、状态表示 dp[i]表示以i结尾的所有子序列中,最长的上升子序列。

By Ne0inhk
【狂热算法篇】完全背包异次元冒险:容量魔法觉醒,价值风暴来袭!

【狂热算法篇】完全背包异次元冒险:容量魔法觉醒,价值风暴来袭!

欢迎拜访:羑悻的小杀马特.-ZEEKLOG博客 本篇主题:轻轻松松拿捏完全背包问题呀!!! 制作日期:2026.03.04 隶属专栏:美妙的算法世界 目录 一·问题定义: 二·具体问题演示:  三·动态规划解答完全背包: 3.1非装满状态: 3.1.1状态定义: 3.1.2状态转移方程:   3.1.3初始化: 3.1.4返回值: 3.1.5填充dp表: 3.1.6非装满状态代码总结: 3.1.7非装满状态滚动数组降维优化:  3.2装满状态: 3.2.1状态定义: 3.2.2状态转移方程:  3.

By Ne0inhk
【DeepSeek】一文详解GRPO算法——为什么能减少大模型训练资源?

【DeepSeek】一文详解GRPO算法——为什么能减少大模型训练资源?

GRPO,一种新的强化学习方法,是DeepSeek R1使用到的训练方法。 今天的这篇博客文章,笔者会从零开始,层层递进地为各位介绍一种在强化学习中极具实用价值的技术——GRPO(Group Relative Policy Optimization)。如果你是第一次听说这个概念,也不必慌张,笔者会带领你从最基础的强化学习背景知识讲起,一步步剖析其来龙去脉,然后再结合实例讲解 GRPO 在实际应用中的思路和操作示例,最后再和其他近似方法对比,看看它和当下主流的 PPO(近端策略优化)等方法究竟有何区别与联系。 强烈推荐看完此帖子后再阅读另一帖——适当练习,强化记忆:【DeepSeek】大模型强化学习训练GRPO算法,你学会了吗? GRPO原论文链接:https://arxiv.org/abs/2402.03300 GRPO中译文链接:https://blog.ZEEKLOG.net/qq_38961840/article/details/145384346 为什么需要关注强化学习与策略优化? 在正式开始介绍 GRPO

By Ne0inhk