当前位置: 首页 > news >正文

Linux编程:5、进程通信-命名管道

一、命名管道的定义与用途

  • 定义:命名管道是一种特殊的文件,不同于匿名管道,它有文件名,可在文件系统中看到。
  • 用途:解决匿名管道只能在有血缘关系进程(如父子进程或者有共同祖先的进程)间通信的限制,允许无血缘关系的进程间通信。

二、命名管道的创建方式

(一)命令行方式

  • 命令mkfifo 管道名,例如mkfifo myFifo
  • 查看属性:使用ll -l命令,管道文件类型标记为p,末尾可能有|符号。
  • 直接读命名管道: 若没有其它进程将数据写入命名管道的操作就会阻塞
  • 直接写命名管道: 若没有其它进程从命名管道读入数据的操作也会阻塞
  • 一个进程读、一个进程写:

    单纯的读或写命名管道都会被阻塞,必需在写操作的时候存在另一个进程在读命名管道才不会被阻塞

(二)编程方式

  • 函数原型int mkfifo(const char *pathname, mode_t mode);
  • 参数说明
    • pathname:命名管道的路径。
    • mode:管道文件的权限(如0666表示读写权限)。
  • 结果:创建一个命名管道文件
  • 返回值:成功返回 0,失败返回 - 1。
  • 代码演示:
    // test1.cpp的代码#include <cstdlib>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>#define FIFO_NAME "/root/test/my_fifo"      // 这里记得改成你自己的路径int main()
    {if(access(FIFO_NAME, F_OK) == -1){// 不存在int ret = mkfifo(FIFO_NAME, 0660);if(ret != 0) {fprintf(stderr, "创建命名管道(%s)失败.\n", FIFO_NAME);exit(-1);}}printf("进程(%d)正在已读的方式打开管道(%s)...\n", getpid(), FIFO_NAME);int fd = open(FIFO_NAME, O_RDONLY);if (fd == -1) {fprintf(stderr, "进程(%d)打开管道 %s 失败\n", getpid(), FIFO_NAME);} else {printf("进程(%d)打开管道 %s 成功\n", getpid(), FIFO_NAME);}
    }
    // test2.cpp的代码#include <cstdlib>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>#define FIFO_NAME "/root/test/my_fifo"  // 这里记得改成你自己的路径int main()
    {if(access(FIFO_NAME, F_OK) == -1) {int ret = mkfifo(FIFO_NAME, 0660);if(ret != 0) {fprintf(stderr, "创建命名管道(%s)失败", FIFO_NAME);exit(-1);}}printf("进程(%d)正在以写的方式打开命名管道(%s)...\n", getpid(), FIFO_NAME);int fd = open(FIFO_NAME, O_WRONLY);if (fd == -1) {fprintf(stderr, "进程(%d)打开管道 %s 失败\n", getpid(), FIFO_NAME);}else {printf("进程(%d)打开管道 %s 成功\n", getpid(), FIFO_NAME);}return 0;
    }
  • 运行结果:
    只运行了读操作


    再运行写操作


三、命名管道的打开模式

(一)阻塞模式(默认)

  • 读模式打开:若没有写进程打开管道,读操作会阻塞。
  • 写模式打开:若没有读进程打开管道,写操作会阻塞。

(二)非阻塞模式(O_NONBLOCK)

  • 组合使用
    • 读模式 + O_NONBLOCK:打开时不阻塞,直接执行后续代码。
    • 写模式 + O_NONBLOCK:打开时不阻塞,直接执行后续代码。

四、命名管道的读写特性

(一)读操作特性

  • 若无数据可读,读操作阻塞,直到有数据写入。
  • 若管道写端被关闭,读操作返回 0。

(二)写操作特性

  • 若管道已满,写操作阻塞,直到有空间可用。
  • 若管道读端被关闭,写操作会导致错误。
  • 数据长度限制:
    • 若写入数据长度≤PIPE_BUF(通常 4KB),操作原子化,不会与其他写操作交错。
    • 若写入数据长度 > PIPE_BUF,可能部分写入或失败。

(三)管道大小

  • 命名管道的大小为 PIPE_BUF,由系统决定,一般为 4KB。

五、读写数据示例

1、从命名管道中读数据

// test1.cpp的代码
#include <fcntl.h>
#include <limits.h>
#include <linux/limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>#define FIFO_NAME "/root/test/my_fifo"  // 这里记得改成你自己的路径int main()
{if (access(FIFO_NAME, F_OK) == -1) {// 不存在int ret = mkfifo(FIFO_NAME, 0660);if (ret != 0) {fprintf(stderr, "创建命名管道(%s)失败.\n", FIFO_NAME);exit(-1);}}// 以读的方式打开这个命名管道printf("进程(%d)正在以读的方式打开管道(%s)...\n", getpid(), FIFO_NAME);int fd = open(FIFO_NAME, O_RDONLY);if (fd == -1) {fprintf(stderr, "进程(%d)打开管道 %s 失败\n", getpid(), FIFO_NAME);}else {printf("进程(%d)打开管道 %s 成功\n", getpid(), FIFO_NAME);}// 读操作的初始buff全都是 'a'std::string buff(PIPE_BUF, 'a');int  ret   = 0;int count = 0;do {ret = read(fd, buff.data(), buff.size());if (ret < 0) {fprintf(stderr, "读管道失败");exit(1);}count += ret;} while (ret > 0);close(fd);printf("进程(%d)读到 %d 字节\n", getpid(), count);// 读完后,buff全都是 'b'printf("buff = \n%s\n", buff.c_str());return 0;
}

2、从命名管道中写数据

// test2.cpp的代码#include <fcntl.h>
#include <limits.h>
#include <linux/limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>#define FIFO_NAME "/root/test/my_fifo"  // 这里记得改成你自己的路径int main()
{if (access(FIFO_NAME, F_OK) == -1) {// 不存在int ret = mkfifo(FIFO_NAME, 0660);if (ret != 0) {fprintf(stderr, "创建命名管道(%s)失败.\n", FIFO_NAME);exit(-1);}}// 以写的方式打开这个命名管道printf("进程(%d)正在以写的方式打开管道(%s)...\n", getpid(), FIFO_NAME);int fd = open(FIFO_NAME, O_WRONLY);if (fd == -1) {fprintf(stderr, "进程(%d)打开管道 %s 失败\n", getpid(), FIFO_NAME);}else {printf("进程(%d)打开管道 %s 成功\n", getpid(), FIFO_NAME);}std::string buff(PIPE_BUF, 'b');int         total = 10 * 1024 * 1024;int         count = 0;while (count < total) {int ret = write(fd, buff.c_str(), buff.size());if (ret < 0) {fprintf(stderr, "管道写入错误");exit(1);}count += total;}close(fd);return 0;
}

3、运行结果


六、注意事项

  1. 打开同步问题:默认阻塞模式下,单独打开读或写管道会阻塞,需同时有读和写进程配合。
  2. 原子性写入:多个进程同时写入时,确保每次写入数据长度≤PIPE_BUF,避免数据交错。
  3. 权限问题:创建管道时需注意权限设置,确保其他进程有读写权限。
  4. 管道生命周期:命名管道文件在文件系统中持续存在,需手动删除或通过程序处理。

相关文章:

  • 从流量为王到留量为王:开源链动2+1模式、AI智能名片与S2B2C商城小程序的协同创新路径
  • Skrill是什么?中国用户能用吗?安全吗?完整指南
  • Spring Boot + MyBatis + Vue:全栈开发的深度剖析与实践指南
  • WINUI/WPF——Button不同状态下图标切换
  • DM8故障分析工具-AWR报告
  • mysql导入大sql(比如10GB的sql文件)
  • Kubernetes核心技术原理详解
  • Linux文件元信息完全指南:权限、链接与时间属性
  • 文本分类与聚类:让信息“各归其位”的实用方法
  • 用Python实现安全封装EXE文件加密保护工具
  • Windows IOCP(I/O Completion Port)模型详解
  • TCP 三次握手与四次挥手全流程详解
  • Android Studio 打 APK 包报错 Invalid keystore format 的解决方法
  • Linux运维新人自用笔记(Ubuntu磁盘命名规则、新磁盘分区、主流文件系统类型、mkfs命令格式化文件系统、临时和永久挂载、挂载报错、dd指令)
  • C++11 std::thread 多线程编程详解
  • 18年磨一剑!开利科技启动数字化增量投资新时代
  • 常见应用层协议介绍
  • MCP入门实战(Python版)
  • [C++] traits机制
  • 领域驱动设计(DDD)【2】之项目启动与DDD基本开发流程
  • 南京软月网站建设公司/免费下载b站视频软件
  • wordpress登陆页面模板下载/个人做seo怎么赚钱
  • 电动车网站模板/新闻 近期大事件
  • 无毒手机网站/网络推广项目
  • 朝阳区建设委员会网站/网站seo优化课程
  • 网站建设制作设计seo优化南宁/网站统计分析工具的主要功能