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

linux多线(进)程编程——(4)进程间的传音术(命名管道)

前言(前情回顾)

进程君(父进程)在开发出匿名管道这门传音术后,解决了和自己孩子(子进程)间的沟通问题,父子关系趋于融洽。和孩子沟通后,进程君发现,自己脱离群众太久了,应该加强和群众的沟通。但是进程君与众位道友间没有血缘关系,无法使用匿名管道进行沟通。于是进程君决定改良匿名管道这种技术,让天下道友都能与自己畅通无阻的沟通,最终产生了一种无暇的传音技术——命名管道(FIFO)。

命名管道

我在上一篇文章中提到过:
“两个没有血缘关系的进程间可以同时打开相同的文件,进程内部分配对应的文件描述符,映射关系记录在PCB中,而两个进程间的fd分配时独立的,也就是fd在多进程中不是唯一的。当然我们也可以在进程1中向文件1写入数据,进程2从文件1中读取数据,构成一个伪管道。”
在这里插入图片描述
其实这里面的伪管道就是命名管道的意思。创建一个命名管道就是在Linux文件系统下创建一个特殊的fifo文件。与匿名管道的区别是,匿名管道也是文件,但它对文件系统不可见。而命名管道是一个可以在文件系统中看见的文件。

命名管道的创建:mkfifo()

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);

这里的filename是一个字符串,描述了命名管道在文件系统中的路径,当字符串中没有‘/’出现是则管道创建在当前目录下。mode一般为0777,对应一个权限掩码,这里不做重点。直接写入即可。
我们先测试一下这个函数,在这里我创建了三个命名管道分别为demo1,demo2,demo3,编译后运行:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

int main() {
    int res1 = mkfifo("demo1",0777);		// 创建于当前路径下
    int res2 = mkfifo("./demo2",0777);		// 创建于当前路径下
    int res3 = mkfifo("../demo3",0777);		// 创建于上一级路径下
    while(1);
    return 0;
}

观察运行前后的工程目录
在这里插入图片描述
可以发现运行后我们的工程目录下出现了三个新的文件,这三个文件就是我们的命名管道。这就是命名管道对文件系统是可见的这句话的含义。
相信一些脑洞大开的道友已经有了想法,既然命名管道在文件系统中可见,也就说明所有的进程都可以访问这个文件,那么只要我向这个进程中读写文件,是不是就完成了进程间的通信?
答案是:完全正确
如何实现对文件的读和写?不清楚的道友可以去看我之前写的一篇文章:linux多线(进)程编程——(1)前置知识
我们直接上代码,为了体现出命名管道与匿名管道的区别,这次我们要真正实现在两个程序间通信,所以我们要写两个C语言源文件。

proc1.c:创建命名管道并且向命名管道内写入数据

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main() {
    int res1 = mkfifo("fifo_demo", 0777);
    int wfd = open("fifo_demo", O_WRONLY);	// 只写
    char send_buf[20];
    bzero(send_buf, 20);
    memcpy(send_buf, "hello, world!", 14);
    while(1) {
        write(wfd, send_buf, 20);
        sleep(1);
    }
    return 0;
}

proc2.c:接收管道内的数据

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main() {
    usleep(500*1000);
    // int res1 = mkfifo("fifo_demo", 0777);
    int rfd = open("fifo_demo", O_RDONLY);	// 只读
    char recv_buf[20];
    
    while(1) {
        bzero(recv_buf, 20);
        read(rfd, recv_buf, 20);
        printf("%s\n", recv_buf);
        sleep(1);
    }
    return 0;
}

在命令行中运行程序,其中后面加一个&,表示在后台运行,让出终端

lol@qingfenfuqin:~/work/linux_study/pipe/fifo$ gcc -o proc1 proc1.c
lol@qingfenfuqin:~/work/linux_study/pipe/fifo$ gcc -o proc2 proc2.c
lol@qingfenfuqin:~/work/linux_study/pipe/fifo$ ./proc1&
[1] 9729
lockin@qingfenfuqin:~/work/linux_study/pipe/fifo$ ./proc2
hello, world!
hello, world!
hello, world!
hello, world!
...

注意:
(1)在两个程序中每次写入和读取的字节数量保持一致。
(2)在程序编写时就应该知道管道的名字。
(3)管道是单工通信,开发时不能又读又写,仅能以只读(O_RDONLY)或者只写(O_WRONLY)打开。

小结

这节课的知识点:
(1)命名管道的创建方法:mkfifo();
(2)命名管道与文件的关系,如何操作命名管道:read();write();
(3)如何在后台运行一个进程:&。
(4)命名管道与匿名管道的差异,以及对文件系统的可见性。

下一集我们将学习进程间第二中通信方式——共享内存的前置知识:linux多线(进)程编程——(5)虚拟内存与内存映射

结束语

“进程君开发出匿名管道与命名管道后,九天十地的道友终于可以畅通无阻的沟通交流了。”
听完这个修仙界传说,不知不觉间你的识海中也多了一道无暇神通,千里传音术——管道。
祝各位道友早日神功大成。

相关文章:

  • Android envsetup与Python venv使用指南
  • CST1017.基于Spring Boot+Vue共享单车管理系统
  • 【软考系统架构设计师】软件工程知识点
  • AI agents系列之全面介绍
  • 密码加密方式
  • 【基础算法】递推算法 - java
  • go之为什么学go?
  • 常用AI辅助编程工具及平台介绍
  • 数据集 handpose_x_plus 3D RGB 三维手势 - 手工绘画 场景 draw picture
  • 【无标题】四色拓扑模型与黑洞信息存储的统一性论证(猜想)——基于规范场论与全息原理的跨学科研究
  • 机器学习(5)——支持向量机
  • 基于ssm网络游戏推荐系统(源码+lw+部署文档+讲解),源码可白嫖!
  • stm32week11
  • ASR评测全方位指标解析:准确性与实时性的平衡-ASR评测
  • 啥是Spring,有什么用,既然收费,如何免费创建SpringBoot项目,依赖下载不下来的解决方法,解决99%问题!
  • Sentinel规则持久化pull模式核心源码解析
  • 多线程与Tkinter界面交互
  • transformer的基本结构和工作原理,多头自注意力机制的作用是什么,为什么使用位置编码?
  • 《算法笔记》3.6小节——入门模拟->字符串处理
  • 扩散模型 Diffusion Model 整体流程详解
  • 上海小学生暑(寒)托班会增设开办期数、延长办班时间吗?团市委回应
  • 第十届曹禺剧本奖上海揭晓,首次开放个人申报渠道
  • “80后”萍乡市安源区区长邱伟,拟任县(区)委书记
  • 美国失去最后的AAA主权评级,继标普、惠誉后再遭穆迪降级
  • 朝鲜称将在各领域采取反制措施,应对美国敌对挑衅
  • 世界数字教育大会发布“数字教育研究全球十大热点”