【Linux】进程间通信方式之管道

🤖个人主页:晚风相伴-CSDN博客

💖如果觉得内容对你有帮助的话,还请给博主一键三连(点赞💜、收藏🧡、关注💚)吧

🙏如果内容有误的话,还望指出,谢谢!!!

✨下一篇文章:《进程间通信之共享内存》敬请期待💪

目录

理解进程间通信的本质

管道

管道的分类

匿名管道 

匿名管道如何实现进程间通信

 从文件描述符的角度理解匿名管道

用代码实现匿名管道

匿名管道读写的4个特殊情况 

管道的特点

利用匿名管道设计一个进程池

命名管道 

命名管道如何实现进程间通信 

命名管道的创建

代码实现命名管道 


理解进程间通信的本质

因为进程具有独立性,所以每个进程都只知道自己,而不知道有另外的进程存在,所以要实现不同进程间的通信,就要让不同的进程都能看到同一块资源,这块资源不属于任意一个进程,而是强调共享,利用这块资源就可以实现进程间通信了。

总结一下要点 

  1. 进程间通信的前提是要让不同的进程看到同一块资源
  2. 这一块资源不隶属于任何一个进程,而是被这些进程所共享

管道

管道想必大家都不陌生吧,在Linux命令行中我们可以通过管道( | )将一个进程输出连接到另一个进程的输入,从而实现数据的传输、连接、过滤和处理等功能。例如

管道也好理解就比如家里面的水管,一端进水,另一端出水。

管道的分类

  • 匿名管道
  • 命名管道 

🔥匿名管道 

匿名管道主要用于父子进程之间的通信。 

创建匿名管道的接口

 

参数:fildes是一个文件描述符数组,fildes[0]表示读端,fildes[1]表示写端

返回值:成功返回0,失败则返回-1并设置对应的错误码

🔥匿名管道如何实现进程间通信

  1. 父进程创建匿名管道,得到两个文件描述符,一个文件描述符用来读数据,另一个文件描述符用来写数据
  2. 调用fork创建子进程,创建出来的子进程会继承父进程创建管道时获得的那两个文件描述符
  3. 在父子进程中关闭不需要的文件描述符,比如子进程读取数据,父进程写入数据,那就在子进程中关闭写入的文件描述符,父进程中关闭读取的文件描述符,之后就可以进行相应的通信操作了。

 🔥从文件描述符的角度理解匿名管道

用代码实现匿名管道

#include <iostream>
#include <cstdio>
#include <cstring>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

int main()
{
    //1.创建管道
    int pipefd[2] = {0};//pipefd[0]:读端,pipefd[1]:写端
    int n = pipe(pipefd);
    assert(n != -1);//在Debug下才有用
    (void)n;//确保在release不报警告

//条件编译
#ifdef DEBUG
    cout << "pipefd[0]:" << pipefd[0] << endl;
    cout << "pipefd[1]:" << pipefd[1] << endl;
#endif

    //2.创建子进程并构建通信信道——父进程写入,子进程读取
    pid_t id = fork();
    assert(id != -1);
    if(id == 0)
    {
        //子进程
        close(pipefd[1]);//关闭子进程不需要的fd

        //读取数据
        char buffer[1024];
        while(true)
        {
            //sleep(10);
            ssize_t s = read(pipefd[0], buffer, sizeof buffer - 1);
            if(s > 0)
            {
                buffer[s] = 0;
                cout << "child get a massage [" << getpid() << "] father say:" << buffer << endl;
            }
        }

        close(pipefd[0]);
        exit(0);
    }
    //父进程
    close(pipefd[0]);//关闭父进程不需要的fd

    //发送数据
    string message = "我是父进程,我正在给你发送数据";
    int count = 0;//统计发送次数
    char send_buffer[1024];
    while(true)
    {
        //sleep(10);
        //将要发送的数据打入send_massage中
        snprintf(send_buffer, sizeof send_buffer, "%s[%d] : %d\n", message.c_str(), getpid(), count++);
        //写入数据
        write(pipefd[1], send_buffer, strlen(send_buffer));
        sleep(1);
    }

    //等待子进程退出
    pid_t ret = waitpid(id, nullptr, 0);
    assert(ret);
    (void)ret;

    close(pipefd[1]);
    return 0;
}

 运行结果

🔥匿名管道读写的4个特殊情况 

情况一:写端很快,读端很慢,写端将缓冲区写满后就不能再写了,必须等读端读取后才能写让子进程读端sleep(10)秒钟

情况二:写端很慢,读端很快,管道中没有数据的时候,读端必须等待写端写入数据之后才能读

让父进程写端sleep(10)秒钟

情况三:写端关闭,读端读到0时表示读到了文件末尾

现象就是它会阻塞在那

情况四:读端关闭,写端继续写,当将缓冲区写满后操作系统会终止写进程

🔥管道的特点

  1. 管道是用于具有亲缘关系的进程之间进行进程间通信(常用于父子进程)
  2. 管道可以让父子进程之间进行协同,并且提供了访问控制(上面的4种情况就是访问控制的体现)
  3. 管道提供的是面向流式的通信服务(面向字节流)
  4. 管道的生命周期是随进程的
  5. 管道是单向通信的,也就是半双工通信的一种特殊情况

完整代码链接:匿名管道

利用匿名管道设计一个进程池

原理:fork多个子进程,然后用个随机数式的负载均衡让多个子进程都有概率能被使用到

 

匿名管道版进程池完整代码链接:匿名管道版进程池

🔥命名管道 

匿名管道的一个限制就是只能在具有亲缘关系的进程间通信,但是如果我们想在不相关的进程之间实现通信,那么就需要用到命名管道。

🔥命名管道如何实现进程间通信 

要实现进程间通信本质是要让不同的进程看到同一块资源。命名管道其实是在磁盘上创建了一个管道文件,这个管道文件可以随意被命名,并且有对应的属性但是没有内容,当我们在一个进程中打开这个管道文件并且将数据写入这个文件中,另一个进程也就可以打开这个管道文件并且从这个文件中读取数据,不同的进程都能打开并使用这个管道文件,所以也就让不同的进程看到了同一块资源。并且在这读写过程中管道文件中的数据不会加载到磁盘中,所以管道文件的大小始终为0保持不变。

命名管道的创建

在命令行上可以使用mkfifo命令来创建一个命名管道

也可以使用系统接口来创建命名管道

参数

  • pathname:命名管道文件的路径     
  • mode:管道文件的权限

返回值:成功返回0,失败则返回-1

代码实现命名管道 

comm.hpp 

#ifndef _COMM_H_
#define _COMM_H_

#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "log.hpp"

using namespace std;

#define MODE 0666 // 文件权限
#define SIZE 128

string ipcPath = "./myfifo.ipc"; // 管道文件路径

#endif

server.cc

#include "comm.hpp"
#include <sys/wait.h>

static void getMessage(int fd)
{
    // 进行通信操作
    char buffer[SIZE];
    while (true)
    {
        memset(buffer, '\0', sizeof buffer);
        ssize_t s = read(fd, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            // 读取成功
            cout << "[" << getpid() << "] "
                 << "client say> " << buffer << endl;
        }
        else if (s == 0)
        {
            // 读到文件结尾
            cerr << "[" << getpid() << "] "
                 << "read end of file, client quit, server quit too!" << endl;
            break;
        }
        else
        {
            // 读取失败
            perror("read");
            break;
        }
    }
}

int main()
{
    // 创建管道文件
    if (mkfifo(ipcPath.c_str(), MODE) < 0)
    {
        perror("mkfifo");
        exit(1);
    }
    logMessage(NORMAL, "创建管道文件成功, step 1"); // 打印日志

    // 打开管道文件
    int fd = open(ipcPath.c_str(), O_RDONLY);
    if (fd < 0)
    {
        perror("open");
        exit(2);
    }
    logMessage(NORMAL, "打开管道文件成功, step 2");

    int nums = 5;
    for (int i = 0; i < nums; i++)
    {
        pid_t id = fork();
        if (id == 0)
        {
            // 子进程抢占式读取消息
            getMessage(fd);
            exit(1);
        }
    }

    // 等待子进程退出
    for (int i = 0; i < nums; i++)
    {
        waitpid(-1, nullptr, 0);
    }

    // 关闭文件
    close(fd);
    logMessage(NORMAL, "关闭管道文件成功, step 3");

    // 通信完成,将管道文件删除
    unlink(ipcPath.c_str());
    logMessage(NORMAL, "删除管道文件成功, step 4");
    return 0;
}

client.cc 

#include "comm.hpp"

int main()
{
    //获取管道文件
    int fd = open(ipcPath.c_str(), O_WRONLY);
    if(fd < 0)
    {
        perror("open");
        exit(1);
    }

    //发送消息
    string buffer;
    while(true)
    {
        cout << "Please Enter Message Line > ";
        getline(cin, buffer);
        write(fd, buffer.c_str(), buffer.size());
    }

    //关闭文件描述符
    close(fd);
    return 0;
}

完整代码链接:命名管道

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

最新:Lodash 严重安全漏洞背后你不得不知道的 JavaScript 知识

可能有信息敏感的同学已经了解到&#xff1a;Lodash 库爆出严重安全漏洞&#xff0c;波及 400万 项目。这个漏洞使得 lodash “连夜”发版以解决潜在问题&#xff0c;并强烈建议开发者升级版本。 我们在忙着“看热闹”或者“”升级版本”的同时&#xff0c;静下心来想&#xf…

人工智能|推荐系统——工业界的推荐系统之冷启动

UGC的物品冷启有哪些 ⼩红书上⽤户新发布的笔记。 B站上⽤户新上传的视频。 今⽇头条上作者新发布的⽂章。 为什么要特殊对待新笔记&#xff1f; 新笔记缺少与⽤户的交互&#xff0c;导致推荐的难度⼤、效果差。 扶持新发布、低曝光的笔记&#xff0c;可以增强作者发布意愿…

在Ubuntu安装RPM文件

Ubuntu软件源包含数千个deb软件包&#xff0c;可以从Ubuntu软件中心或使用apt命令行安装。 Deb是所有基于Debian的Linux发行版&#xff0c;例如包括Ubuntu&#xff0c;Linux mint等发行版使用的安装包格式。 如果某些软件在Ubuntu软件源中不可用&#xff0c;可以通过启用适当的…

NOIP,CSP-J,CSP-S——函数

一、函数概念 /*函数返回类型 函数名(参数){语句 } */ int add(int x,int y){return x+y; } 调用这个函数add int main(){int x,y,z;scanf("%d%d",&x,&y);z=add(x,y);printf("%d",z); } 二、变量作用域 main函数的z只作用于第二个for语句…

Day3 | Java基础 | 4常见类

Day3 | Java基础 | 4 常见类 基础版Object类equalshashCode&#xff08;散列码&#xff09;hashCode和equals clone方法String类 问题回答版Object类Object类的常见方法有哪些&#xff1f;和equals()的区别是什么&#xff1f;为什么要有hashCode&#xff1f;hashCode和equals的…

【C++】适配器模式

文章目录 前言 1. 适配器的介绍2. 仿函数2.1 sort函数的模板参数2.2 priority_queue类的模板参数 3. priority_queue模拟实现3. stack & queue 模拟实现3.1 deque的介绍3.2 deque的优点与缺陷3.3 STL标准库中对于stack和queue的模拟实现 前言 C中的适配器是一种设计模式&am…

【强训笔记】day16

NO.1 代码实现&#xff1a; class StringFormat { public:string formatString(string A, int n, vector<char> arg, int m) {string ret;int j0;for(int i0;i<n;i){if(A[i]%){if(i1<n&&A[i1]s){retarg[j];i;}else {retA[i];}}else {retA[i];}}while(j&l…

wlan二层旁挂组网实验

实验拓扑图 代码&#xff1a; SW1 <Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]sysn sw1 [sw1]undo info-center enable Info: Information center is disabled. [sw1]vlan batch 10 20 30 Info: This operation may take a few seconds. …

基于Springboot的校园悬赏任务平台(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的校园悬赏任务平台&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

12 华三的二层链路聚合

12 华三的二层链路聚合 配置思路 1. 配置二层静态聚合组 (1) 进入系统视图。 system-view (2) 创建二层聚合接口&#xff0c;并进入二层聚合接口视图。 interface bridge-aggregation interface-number [ lite ] 创建二层聚合接口后&#xff0c;系统将自动生成…

二分查找向下取整导致的死循环69. x 的平方根

二分查找向下取整导致的死循环 考虑伪题目&#xff1a;从数组arr中查找出目标元素target对应的下标&#xff0c;如果数组中不存在目标元素&#xff0c;找 到第一个元素值小于target的元素的下标。 编写二分查找算法如下&#xff1a; Testvoid testBinarySearch(){int[] arr n…

LeetCode 142.环形链表Ⅱ

题目描述 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内…

CSS和JavaScript

CSS 在html中引入CSS 我们需要先在该项目先建立css文件 html引入CSS,在<head></head>中添加<link>标签 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" co…

JavaScript原理篇——理解对象、构造函数、原型、继承

对象:在JavaScript中&#xff0c;几乎所有的东西都是对象&#xff0c;包括基本数据类型的包装对象。对象是属性的集合&#xff0c;每个属性都有一个键和一个值。对象可以通过字面量、构造函数或Object.create()等方式创建。 构造函数:构造函数是用来创建对象的函数&#xff0c;…

5月9(信息差)

&#x1f30d; 可再生能源发电量首次占全球电力供应的三成 &#x1f384;马斯克脑机接口公司 Neuralink 计划将 Link 功能扩展至现实世界&#xff0c;实现控制机械臂、轮椅等 马斯克脑机接口公司 Neuralink 计划将 Link 功能扩展至现实世界&#xff0c;实现控制机械臂、轮椅等…

Python turtle绘制图形详解

Python 的 Turtle 模块是一个简单而直观的绘图工具&#xff0c;可以帮助初学者理解基本的图形绘制概念。 1.导入 Turtle 模块&#xff1a; import turtle 2.创建 Turtle 对象&#xff1a; t turtle.Turtle() 3.绘制图形&#xff1a; 4.移动Turtle对象&#xff1a;t.forward(di…

【PMP战报】2024.3.10 PMP考试成绩出炉

PMP成绩查询及电子版证书下载 https://mp.weixin.qq.com/s/HgWrZWjJ0cScEYs4w1b4iwPMP项目管理学习专栏https://blog.csdn.net/xmws_it/category_10954848.html?spm1001.2014.3001.5482 2024年3月中国大陆的认证考试已经顺利结束。 从2024年5月7日开始&#xff0c;大约一周内…

小程序如何注销

随着移动互联网的深入发展&#xff0c;管控也越来越严格。现在小程序都要求进行ICP备案&#xff0c;不管是新注册的还是以往注册的。很多商家的小程序本身处于无运营状态&#xff0c;现在要求备案&#xff0c;还不如直接注销。下面&#xff0c;将详细介绍小程序注销的步骤和注意…

报错(已解决):无法加载文件 D:\code\NodeJs\pnpm.ps1,因为在此系统上禁止运行脚本。

问题&#xff1a; 在vscode运行uniapp项目需要拉取全部依赖&#xff0c;需要使用到pnpm&#xff0c;在vscode终端运行命令&#xff1a;pnpm install后报错&#xff1a; 解决办法&#xff1a; 1&#xff1a;我未安装pnpm&#xff0c;首先打开电脑cmd&#xff0c;运行下列命令&a…

XXL-JOB定时任务

1. xxl-job初识 1.1 xxl-job介绍 xxl-job 是大众点评大佬徐雪里开源的一款分布式任务调度框架&#xff0c;具有简单易用、轻量级、可扩展的特点。相比于Spring Task, Quartz&#xff0c;xxl-job有记录执行日志和运行大盘&#xff0c;方便开发人员和运维人员更好的管理任务。 …
最新文章