进程间通信之匿名管道和命名管道的理解和实现【Linux】

news2024/10/5 16:00:31

进程间通信之匿名管道和命名管道的理解和实现

  • 进程间通信
  • 什么是管道
    • 匿名管道
    • 代码实现
    • 管道的读写规则
    • 管道特点
  • 命名管道
    • 创建命名管道
    • 代码实现

进程间通信

进程间通信的目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

进程间通信的本质理解

  • 进程间通信的前提,首先需要让不同的进程都能够看到同一块“内存”。
  • 这个“内存”不能隶属于任何进程应该强调共享。

进程间通信的必要性
  单进程无法使用并发能力,更加无法实现多进程协同。

什么是管道

管道是进程通信的其中一种,管道分为两种:

管道
匿名管道pipe
命名管道
  • 管道是Unix中最古老的进程间通信的形式。
  • 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。
  • 管道只能单向通信。

在这里插入图片描述

  父进程fork之后,父进程会copy一份files_struct给子进程,但是父子进程的文件描述符依旧是指向同一份文件,而这个文件就叫做管道。
  管道某种意义上来说不是文件,是一种方式,进行交流的数据作为临时数据放在内存,实际上是由内核维护的一个缓冲区,在内核中被称为管道缓存,当交流结束后,临时数据就会被释放。所以也可以把管道看作是一个缓冲区。

匿名管道

#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码

管道的实现
  1. 分别以读写的方式打开同一个文件。
  1. fork创建子进程。
  1. 双方进程各自关闭自己不需要的文件描述符(为了实现单向通信,但是如果想实现全双工通信,可以创建多个管道来实现)。

如何做到让不同的进程看到同一份资源?
:fork后让子进程继承—>能够让具有血缘关系的进程进行进程间通信—>常用于父子进程

在这里插入图片描述

代码实现

下面这是一个简易的匿名管道的实现,当使用pipe函数时,管道文件是通过 pipe 系统调用创建的。具体来说,pipe 系统调用会在内核中创建一个管道,并返回两个文件描述符,这两个文件描述符分别用于读取和写入管道。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <cstdlib>
int main()
{

    int fd[2];

    int retpp = pipe(fd);
    if (retpp == -1)
    {
        perror("pipe");
        exit(1);
    }

    int fk = fork();
    if (fk < 0)
    {
        perror("fork");
        exit(2);
    }

    
    if (fk == 0)
    {
        // child: 作读端,首先得关闭写端
        close(fd[1]);
        char buf[1024];
        read(fd[0], buf, sizeof(buf));
        printf("child: %s\n", buf);
    }
    if (fk > 0)
    {
        // parent:作写端,首先得关闭读端
        close(fd[0]);
        char *bufp = "qwweereewrrere";
        write(fd[1], bufp, strlen(bufp));
        return 0;
    }
}

可不可以改变管道文件呢?
下面的代码将文件t.txt作为管道文件。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{

    int fd[2];

    int retpp = pipe(fd);
    if (retpp == -1)
    {
        perror("pipe");
        exit(1);
    }

    int fk = fork();
    if (fk < 0)
    {
        perror("fork");
        exit(2);
    }
    int fd1 = open("F:\\t.txt", O_RDWR | O_CREAT, 0666);
    if (fd1 < 0)
    {
        perror("open");
        exit(3);
    }
    if (fk == 0)
    {
        // child: 作读端,首先得关闭写端
        close(fd[1]);
        char buf[1024];
        dup2(fd[0], fd1);
        read(fd[0], buf, sizeof(buf));
        printf("%s\n", buf);
    }
    if (fk > 0)
    {
        // parent:作写端,首先得关闭读端
        close(fd[0]);
        const char *bufp = "qwweereewrrere";
        dup2(fd[1], fd1);
        write(fd[1], bufp, strlen(bufp));
        return 0;
    }
}

用fork来共享管道原理
在这里插入图片描述
站在文件描述符角度-深度理解管道
在这里插入图片描述

站在内核角度-管道本质
在这里插入图片描述

管道的读写规则

  • 当没有数据可读时
    • O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
    • O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
  • 当管道满的时候
    • O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
    • O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
  • 如果所有管道写端对应的文件描述符被关闭,则read返回0
  • 如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程
    退出
  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

管道提供了访问控制

  • 写端快,读端慢, 写端满了就不能再写了。
  • 写端慢,读端快, 管道没有数据时,读端必须等待。
  • 写端关闭,读端没有数据可读,标识读到了文件结尾。
  • 读端关闭,写端继续写,OS终止写端进程。

管道特点

  • 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创
    建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
  • 管道提供流式服务
  • 一般而言,进程退出,管道释放,所以管道的生命周期随进程
  • 一般而言,内核会对管道操作进行同步与互斥
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
    在这里插入图片描述

命名管道

  • 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
  • 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
  • 命名管道是一种特殊类型的文件

创建命名管道

命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
mkfifo filename
命名管道也可以从程序里创建,相关函数有:
int mkfifo(const char *filename,mode_t mode);

比如我们可以这么创建命名管道:

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

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

  • 匿名管道由pipe函数创建并打开。
  • 命名管道由mkfifo函数创建,打开用open
  • FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。

命名管道的打开规则

  • 如果当前打开操作是为读而打开FIFO时
    • O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
    • O_NONBLOCK enable:立刻返回成功
  • 如果当前打开操作是为写而打开FIFO时
    • O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
    • O_NONBLOCK enable:立刻返回失败,错误码为ENXIO

代码实现

  下面的代码实现了客户端向命名管道中写入信息,服务端从命名管道中将数据读取出来并打印在终端屏幕上。

// server.cpp

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

int main()
{
    srand((unsigned int)time(NULL));
    if (mkfifo("shard", 0666) < 0)
    {
        perror("mkfifo");
        exit(1);
    }

    int sfd = open("shard", O_WRONLY, 0666);
    if (sfd < 0)
    {
        perror("server_open");
        exit(2);
    }

    while (1)
    {
        int a = rand() % 1000;
        char server_put[1024] = {'\0'};
        sprintf(server_put, "%d", a);

        printf("server: %s\n", server_put);

        write(sfd, server_put, sizeof(server_put));
        sleep(2);
    }
    return 0;
}
// client

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

int main()
{
    srand((unsigned int)time(NULL));
    int cfd = open("shard", O_RDONLY, 0666);
    if (cfd < 0)
    {
        perror("client_open");
        exit(2);
    }

    while (1)
    {
        char client_in[1024];
        read(cfd, client_in, sizeof(client_in));
        printf("client: %s\n", client_in);
        sleep(1);
    }
    return 0;
}
// makefile

.PHANY:all
all: server client

server:server.cpp 
	g++ -o $@ $^ 

client:client.cpp 
	g++ -o $@ $^

.PHANY:clean
clean:
	rm -f client server shard

在这里插入图片描述


    😄 创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看😄

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1366724.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java基于ssm框架的滁艺咖啡在线销售系统+vue论文

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装滁艺咖啡在线销售系统软件来发挥其高效地信息处理的作用&am…

对接讯飞聊天机器人接口--复盘

1、准备工作 1&#xff09;、进入以下平台进行注册&#xff0c;登录后&#xff0c;点击红框处 2&#xff09;、点击个人免费包&#xff08;会弹出实名认证&#xff0c;先进行实名认证&#xff09; 3&#xff09;、认证后&#xff0c;会进入以下界面&#xff0c;先添加应用 4&am…

特征工程(一)

特征工程&#xff08;一&#xff09; 什么是特征工程 简单来讲将数据转换为能更好地表示潜在问题的特征&#xff0c;从而提高机器学习性能 特征工程包含的内容 转换数据的过程特征更好地表示潜在问题提高机器学习性能 数据和机器学习的基础知识 数据基础 以下为数据的一…

三剑客前端教程

前端教程 结构层&#xff08;html&#xff09;表现层&#xff08;css&#xff09;行为层&#xff08;javascript&#xff09; HTML 超文本标记语言&#xff09; HTML&#xff08;超文本标记语言——HyperText Markup Language&#xff09;是构成 Web 世界的一砖一瓦。它定义…

ssm基于HTML5的交流论坛的设计与实现+vue论文

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自古…

ME11/ME12拷贝采购信息记录

注意点&#xff1a; ECC没有好用的修改/创建采购信息记录BAPI所以使用BDC处理&#xff0c; 因为BDC执行过程如果遇到黄色提示消息就会暂停&#xff0c;所以如果遇到黄色提示需要增强处理 还有就是价格的小数位数问题&#xff0c;如JPY不能使用小数位数问题处理 增强调整 如下…

软件测试|Linux基础教程:cp命令详解,复制文件或目录

简介 在Linux系统中&#xff0c;cp命令是一个非常常用且强大的命令&#xff0c;用于复制文件和目录。cp命令允许我们在不同目录之间复制文件或目录&#xff0c;并可以根据需求对文件复制的行为进行调整。在本文中&#xff0c;我们将详细解释cp命令的用法以及一些常见的选项。 …

spark的任务提交方式及流程

本地模式 local 测试用,不多赘述 分布式模式 standalone standalone集群是spark 自带的一个资源调度集群&#xff0c;分为两个角色&#xff0c;master/worker&#xff0c;master负责接收任务请求、资源调度&#xff08;监听端口7077&#xff09;&#xff0c;worker负责运行exec…

深入了解鸿鹄工程项目管理系统源码:功能清单与项目模块的深度解析

工程项目管理软件是现代项目管理中不可或缺的工具&#xff0c;它能够帮助项目团队更高效地组织和协调工作。本文将介绍一款功能强大的工程项目管理软件&#xff0c;该软件采用先进的Vue、Uniapp、Layui等技术框架&#xff0c;涵盖了项目策划决策、规划设计、施工建设到竣工交付…

Java如何拷贝数据?

Java如何拷贝数据&#xff1f; 在 Java 中&#xff0c;数组和集合的深拷贝与浅拷贝的概念与复制对象的引用和内容相关。深拷贝是创建一个新对象&#xff0c;并递归地复制其所有内容&#xff0c;而浅拷贝则只是复制对象的引用。 数组的深拷贝与浅拷贝&#xff1a; 1. 深拷贝数…

金和OA C6 HomeService.asmx SQL注入漏洞复现

0x01 产品简介 金和网络是专业信息化服务商,为城市监管部门提供了互联网+监管解决方案,为企事业单位提供组织协同OA系统开发平台,电子政务一体化平台,智慧电商平台等服务。 0x02 漏洞概述 金和OA C6 HomeService.asmx接口处存在SQL注入漏洞,攻击者除了可以利用 SQL 注入漏洞…

量子革命的基础:激光冷却史(下)

本文是《激光冷却史》系列的最后一部分。 在20世纪的最后20年里&#xff0c;原子物理学家屡次打破宇宙中最冷温度的记录。这些成就有赖于一些进步&#xff0c;包括激光冷却&#xff08;《激光冷却史&#xff08;上&#xff09;》&#xff09;、磁光阱和西西弗斯冷却等技术&…

DES算法(Python实现)

一、具体描述 基于计算机高级语言&#xff08;如C语言&#xff09;实现DES算法 二、名词术语与相关知识 DES算法 DES&#xff08;Data Encryption Standard&#xff09;是一种对称加密算法&#xff0c;被广泛应用于数据加密领域。它使用64位密钥和64位明文&#xff0c;通过…

西门子WinCC的C脚本——对象的事件任务

1、 全局脚本编辑器&#xff1b; 2、 对象的属性任务&#xff1b; 3、 对象的事件任务。 本文探讨一下用C脚本来实现对象的事件任务。 一、例程说明引文&#xff1a;博途工控人平时在哪里技术交流博途工控人社群 如图1所示&#xff0c;为本例程的运行画面。本例程实现以下…

大数据 Hive - 实现SQL执行

文章目录 MapReduce实现SQL的原理Hive的架构Hive如何实现join操作小结 MapReduce的出现大大简化了大数据编程的难度&#xff0c;使得大数据计算不再是高不可攀的技术圣殿&#xff0c;普通工程师也能使用MapReduce开发大数据程序。 但是对于经常需要进行大数据计算的人&#xff…

没经验没资金,适合穷人创业项目的低成本生意

什么人可以赚到钱呢&#xff1f;不管你怎么都赚不到&#xff0c;那归根结底是因为你身边没有明白人。像我们普通人一没经验二没资金三没人脉&#xff0c;该如何创业呢&#xff1f; 第一点&#xff0c;如果你不知道干什么&#xff0c;就做黄牛&#xff0c;只当渠道&#xff0c;只…

Web前端篇——ElementUI之el-scrollbar + el-backtop + el-timeline实现时间轴触底刷新和一键返回页面顶部

ElementUI之el-scrollbar el-backtop el-timeline实现时间轴触底刷新和一键返回页面顶部。 背景&#xff1a;ElementUI的版本&#xff08;vue.global.js 3.2.36&#xff0c; index.css 2.4.4&#xff0c; index.full.js 2.4.4&#xff09; 废话不多说&#xff0c;先看动…

猫头虎分享已解决Bug || Error: ImagePullBackOff (K8s)

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通Golang》…

(二)Explain使用与详解

explain中的列 sql语句: EXPLAIN SELECT * from user WHERE userId=1340; 执行结果: 1. id列 id列的编号是 select 的序列号,有几个 select 就有几个id,并且id的顺序是按 select 出现的顺序增长的。 id列越大执行优先级越高,id相同则从上往下执行,id为NULL最后执行…

python股票分析挖掘预测技术指标知识之蜡烛图指标(6)

本人股市多年的老韭菜&#xff0c;各种股票分析书籍&#xff0c;技术指标书籍阅历无数&#xff0c;萌发想法&#xff0c;何不自己开发个股票预测分析软件&#xff0c;选择python因为够强大&#xff0c;它提供了很多高效便捷的数据分析工具包。 我们已经初步的接触与学习其中数…