进程间通信I·匿名管道

news2025/5/25 9:48:19

目录

进程间通信(IPC)

含义

目的

分类

匿名管道

原理

创建过程

特性

四大情况

close问题

代码练习

简单通信

进程池

小知识


进程间通信(IPC)

含义

就是让不同的进程能看到同一份资源,实现数据交流。

目的

1.数据传输:一个进程需要将它的数据发送给另一个进程


2.资源共享:多个进程之间共享同样的资源


3.通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)


4.进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并且能够及时知道它的状态改变

分类

主要分三大类

1.管道:基于文件的通信方法,一般为单向通信的信道。

        - 匿名管道

        - 命名管道

2.System V IPC :操作系统中单独设计的通信模块(只能进行本地通信)

        - System V 消息队列

        - System V 共享内存

        - System V 信号量

3.POSIX IPC:网络间进程通信

        - 消息队列

        - 共享内存

        - 信号量

        - 互斥量 

        - 条件变量

        - 读写锁

匿名管道

原理

fork子进程时,PCB要拷贝,关联的文件的struct file结构体也会拷贝,父子进程的struct file通过自己的文件读写位置区访问文件属性和内容;为了保证父子读写不会冲突(同一个文件读写位置只有一个),所以要么父进程读,子进程写,要么子进程读,父进程写,这里父子进程就可以看到同一份资源了,这就是管道的原理。

创建过程

父进程以读写两种方式打开同一个管道文件,然后父进程创建子进程,子进程继承父进程,最后父进程和子进程关闭自己不需要的读或写方式。

 

1.父进程为什么以读写方式打开:为了让子进程也继承读写。

2.为什么关闭读写端:管道是单向通信的。

3.为什么单向通信:就是这样设计的。

pipe :创建匿名管道的系统调用

 1.pipifd是输出型参数,会将创建的读写方式对应的文件描述符放到这个数组里,[0]为读,[1]为写。

2.成功返回0,失败返回-1,同时errno 变量会被设置为相应的错误码,以指示具体的错误原因。

创建之后我们可以得知,匿名管道不存在文件名和路径,就不存在向磁盘中刷新数据,是由操作系统进行开辟和管理的内核中的一块缓冲区,是一个内存级的基于文件系统的文件。

特性

1.用于有血缘关系这样的进程进行进程通信(IPC)

2.单向通信

3.管道的生命周期随进程

4.面向字节流

5.管道自带同步机制

四大情况

1.写端不关,写端不写 管道里没有数据,读端会被阻塞(造就同步机制)


2.读端不关,读端不读 写满了(64kb,65536字节)就不会再写了


3.读端不关,写端先关 读端返回值会为0,表示读到文件结尾


4.写端不关,读端先关 OS会自动杀掉写进程,通过发送13号信号

close问题

如果多个子进程继承同一父进程时,在第一个之后的子进程会将父进程与之前的子进程建立的写端(读端)的fd也继承,让其引用计数++,这样如果最后按顺序进行close(fd)+wait 的话,是会出问题的。

例:父进程写,子进程读,创建第一个子进程时,父进程使用文件描述符表中3/4作为读端和写端,第一个子进程继承到3/4,然后父进程关闭3,子进程关闭4;接下来创建第二个子进程,父进程pipe会以3/5作为读端/写端,这时子进程继承,4也会被继承,这样这个子进程也就即指向了之前的管道,又指向它的管道,创建之后的子进程同理。

这样越先创建的管道,指向它的就越多,引用计数就越大,当父进程退出不写后,指向管道的写端的引用计数不为0,子进程认为仍有写端可以进行写,不会关闭。

解决方法:1.倒着关闭

2.在子进程创建时关闭之前子进程的继承来的fd。

代码练习

简单通信

main.cpp

#include "together.hpp"

int main()

{

    int pipefd[2] = {0};

    int piperet = pipe(pipefd);

    if(piperet<0)

    {

        ERR_EXIT("pipe");

    }

    pid_t pid = fork();

    if(pid<0)

    {

        ERR_EXIT("fork");

    }

    else if(pid==0)

    {

        close(pipefd[1]);

        char buff[1024] = {'\0'};

        while(1)

        {

            ssize_t readret = read(pipefd[0],buff,sizeof(buff)-1);

            if(readret<0)

            {

                ERR_EXIT("read");

            }

            else if(readret==0)

            {

                cout<<"写端下班,我也下班喽"<<endl;

                break;

            }

            else

            {

                buff[readret] = '\0';

                cout<<"父进程说:"<<buff<<endl;

            }

        }

        exit(0);

    }

    close(pipefd[0]);

    ssize_t writeret = write(pipefd[1],"i am father",11);

    sleep(2);

    return 0;

}

together.hpp

#pragma once

#include <iostream>

#include <fcntl.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

using namespace std;

#define ERR_EXIT(s)\

do{\

    perror(s);\

    exit(EXIT_FAILURE);\

}while(0)    

Makefile

ano_pipe:main.cpp

    g++ -o $@ $^ -std=c++11

.PHONY:clean

clean:

    rm -f ano_pipe

进程池

main.cpp

#include "pro_pool.hpp"

#define NUM 3

int main()

{

    srand(time(nullptr));

    //创建进程池

    ProPool pool;

    //初始化进程池

    pool.InitProPool(NUM,[](int fd){

        while(true)

        {

            int code = 0;

            ssize_t n = read(fd,&code,sizeof(code));

            if(n==sizeof(code))

            {

                if(code>=0&&code<NUM)

                {

                    _task._t[code]();

                }  

                else

                {

                    std::cout<<"任务码错误 "<<code<<std::endl;

                }

            }

            else if(n==0)

            {

                std::cout<<"子进程"<<getpid()<<"退出"<<std::endl;

                break;

            }

            else

            {

                std::cout<<"read error"<<std::endl;

                break;

            }

        }

    });

    //唤醒进程池

    pool.Awake_Pro();

    //结束进程池

    pool.WaitPro();

    return 0;

}

Makefile

pro_pool:main.cpp

    g++ -o $@ $^ -std=c++11

.PHOMY:

clean:

    rm -rf  pro_pool

pro_pool.hpp

#ifndef PRO_POOL_HPP

#define PRO_POOL_HPP

#include <iostream>

#include <string>

#include <vector>

#include <functional>

#include <cstdio>

#include <sys/types.h>

#include <sys/wait.h>

#include <unistd.h>

#include <time.h>

#include "task.hpp"

using namespace std;

using func_t = std::function<void(int fd)>;

Task_ _task;

class Channel

{

    int _wfd;

    std::string _name;

    pid_t _pid;

public:

    Channel() {}

    Channel(int wfd, const std::string &name, pid_t pid) : _wfd(wfd), _name(name), _pid(pid) {}

    ~Channel() {}

    int FD() { return _wfd; }

    std::string Name() { return _name; }

    pid_t Pid() { return _pid; }

    void Close() { close(_wfd); }

    void Wait()

    {

        pid_t ret = waitpid(_pid, nullptr, 0);

        (void)ret;

    }

};

class ProPool

{

    std::vector<Channel> _channels;

    int _progressnum = 0;

public:

    ProPool() {}

    ~ProPool() {}

    void InitProPool(int num, func_t cb)

    {

        _progressnum = num;

        for (int i = 0; i < num; ++i)

        {

            int fdnum[2];

            int ret = pipe(fdnum);

            if (ret < 0)

                exit(2);

            pid_t pid = fork();

            if (pid < 0)

                exit(1);

            else if (pid == 0)

            {

                close(fdnum[1]);

                // 子进程执行任务

                cb(fdnum[0]);

                exit(0);

            }

            close(fdnum[0]);

            std::string task_name = "channel " + to_string(i);

            _channels.emplace_back(fdnum[1], task_name, pid);

        }

    }

    void Awake_Pro()

    {

        for (int index = 0;; index++)

        {

            index %= _channels.size();

            int code = rand() % _task.size();

            std::cout << "唤醒" << _channels[index].Name() << std::endl;

            ssize_t ret = write(_channels[index].FD(), &code, sizeof(code));

            sleep(1);

        }

    }

    void WaitPro()

    {

        while (_progressnum--)

        {

            _channels[_progressnum].Close();

            _channels[_progressnum].Wait();

        }

    }

};

#endif

task.hpp

#include <iostream>

#include <vector>

#include <functional>

using namespace std;

using task_f = std::function<void ()>;

void download()

{

    std::cout<<"download ing..."<<std::endl;

}

void unload()

{

    std::cout<<"unload ing..."<<std::endl;

}

void run_childtask()

{

    std::cout<<"run_childtask ing..."<<std::endl;

}

class Task_

{

public:

    std::vector<task_f> _t;

    Task_()

    {

        _t.push_back(download);

        _t.push_back(unload);

        _t.push_back(run_childtask);

    }

    size_t size(){return _t.size();}

};

小知识

1.C++中, .cpp   .cxx   .cc  都可以作为源文件的后缀    .hpp是头源结合,不过在编译时会被编译器当做头文件处理,所以单独的.hpp文件是不能形成正确的.exe的。


2.头文件的本质是函数和变量的声明的文本集合

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

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

相关文章

Ubuntu Linux系统的基本命令详情

1.Ubuntu Linux是以桌面应用为主的Linux发行版操作系统 2.Ubuntu的用户使用 在登录系统一般使用在安装系统时建立的普通用户登录&#xff0c;如果要使用超级用户权限 #sudo ---执行命令 sudo passwd ---修改用户密码 su - root ---切换超级用户 系统的不同&#xff0c;命令也不…

大数据治理:理论、实践与未来展望(二)

书接上文 文章目录 七、大数据治理的未来发展趋势&#xff08;一&#xff09;智能化与自动化&#xff08;二&#xff09;数据隐私与安全的强化&#xff08;三&#xff09;数据治理的云化&#xff08;四&#xff09;数据治理的跨行业合作&#xff08;五&#xff09;数据治理的生…

PCB布局设计

PCB布局设计 一、原理图到PCB转换前的准备工作 在将原理图转换为PCB之前&#xff0c;我们需要进行一系列准备工作&#xff0c;确保设计的正确性和完整性。这一步骤至关重要&#xff0c;可以避免后续PCB设计中出现不必要的错误。 // 原理图转PCB前必要检查步骤 // 1. 仔细检查…

esp32+IDF V5.1.1版本编译freertos报错

error: portTICK_RATE_MS undeclared (first use in this function); did you mean portTICK_PERIOD_MS 解决方法: 使用命令 idf.py menuconfig 打开配置界面配置freeRtos 使能configENABLE_BACKWARD_COMPATIBLITY

笔记本6GB本地可跑的图生视频项目(FramePack)

文章目录 &#xff08;一&#xff09;简介&#xff08;二&#xff09;本地执行&#xff08;2.1&#xff09;下载&#xff08;2.2&#xff09;更新&#xff08;2.3&#xff09;运行&#xff08;2.4&#xff09;生成 &#xff08;三&#xff09;注意&#xff08;3.1&#xff09;效…

SpringMVC实战:动态时钟

引言 在现代 Web 开发中&#xff0c;选择一个合适的框架对于项目的成功至关重要。Spring MVC 作为 Spring 框架的核心模块之一&#xff0c;以其清晰的架构、强大的功能和高度的可配置性&#xff0c;成为了 Java Web 开发领域的主流选择。本文将通过一个“动态时钟”的实战项目…

哈希表的实现(上)

前言 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可达到&#xff0c;即最差情况下需要比较红黑树的高度次&#xff0c;当树中的节点非常多时&#xff0c;查询效率也不理想。最好的查询是&#xff0c;进行很少的比较次数就能够将…

【Java高阶面经:微服务篇】1.微服务架构核心:服务注册与发现之AP vs CP选型全攻略

一、CAP理论在服务注册与发现中的落地实践 1.1 CAP三要素的技术权衡 要素AP模型实现CP模型实现一致性最终一致性(Eureka通过异步复制实现)强一致性(ZooKeeper通过ZAB协议保证)可用性服务节点可独立响应(支持分区存活)分区期间无法保证写操作(需多数节点可用)分区容错性…

实验7 HTTP协议分析与测量

实验7 HTTP协议分析与测量 1、实验目的 了解HTTP协议及其报文结构 了解HTTP操作过程&#xff1a;TCP三次握手、请求和响应交互 掌握基于tcpdump和wireshark软件进行HTTP数据包抓取和分析技术 2、实验环境 硬件要求&#xff1a;阿里云云主机ECS 一台。 软件要求&#xff1…

python:机器学习概述

本文目录&#xff1a; 一、人工智能三大概念二、学习方式三、人工智能发展史**1950-1970****1980-2000****2010-2017****2017-至今** 四、机器学习三要素五、常见术语六、数据集的划分七、常见算法分类八、机器学习的建模流程九、特征工程特征工程包括**五大步**&#xff1a;特…

得力DE-620K针式打印机打印速度不能调节维修一例

基本参数: 产品类型 票据针式打印机(平推式) 打印方式 串行点阵击打式 打印宽度 85列 打印针数 24针 可靠性 4亿次/针 色带性能 1000万字符纠错 复写能力 7份(1份原件+6份拷贝) 缓冲区 128KB 接口类型 …

java基础(继承)

什么是继承 继承好处 提高代码的复用性 继承注意事项 权限修饰符 单继承、Object类 冲突&#xff1a; 方法重写 扩展&#xff1a; 其实我们不想看地址&#xff0c;地址看来没用&#xff0c;我们是用来看对象有没有问题 重写toString: 比如这个如果返回的是地址值&#xff0c;…

基于cornerstone3D的dicom影像浏览器 第二十二章 mpr + vr

系列文章目录 第一章 下载源码 运行cornerstone3D example 第二章 修改示例crosshairs的图像源 第三章 vitevue3cornerstonejs项目创建 第四章 加载本地文件夹中的dicom文件并归档 第五章 dicom文件生成png&#xff0c;显示检查栏&#xff0c;序列栏 第六章 stack viewport 显…

MySQL:游标 cursor 句柄

当我们select * from emp 可以查看所有的数据 这个数据就相当于一个数据表 游标的作用相当于一个索引 一个指针 指向每一个数据 假设说我要取出员工中薪资最高的前五名成员 就要用到limit关键字 但是这样太麻烦了 所以这里用到了游标 游标的声明&#xff1a; declare my…

二、ZooKeeper 集群部署搭建

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月24日 专栏&#xff1a;Zookeeper教程 我们这次教程将以 hadoop01 (192.168.121.131), hadoop02 (192.168.121.132), hadoop03 (192.168.121.133) 三台Linux服务器为例&#xff0c;搭建一个ZooKeeper 3.8.4集群。 一、下载…

<< C程序设计语言第2版 >> 练习1-14 打印输入中各个字符出现频度的直方图

1. 前言 本篇文章是<< C程序设计语言第2版 >> 的第1章的编程练习1-14, 个人觉得还有点意思, 所以写一篇文章来记录下. 希望可以给初学C的同学一点参考. 尤其是自学的同学, 或者觉得以前学得不好, 需要自己补充学习的同学. 和我的很多其它文章一样, 不建议自己还没实…

黑马点评双拦截器和Threadlocal实现原理

文章目录 双拦截器ThreadLocal实现原理 双拦截器 实现登录状态刷新的原因&#xff1a; ​ 防止用户会话过期&#xff1a;通过动态刷新Token有效期&#xff0c;确保活跃用户不会因固定过期时间而被强制登出 ​ 提升用户体验&#xff1a;用户无需频繁重新登录&#xff0c;只要…

港股IPO市场火爆 没有港卡如何参与港股打新?

据Wind资讯数据统计&#xff0c;今年1月1日至5月20日&#xff0c;港股共有23家企业IPO&#xff0c;较去年同期增加6家&#xff1b;IPO融资规模达600亿港元&#xff0c;较去年同期增长626.54%&#xff0c;IPO融资规模重回全球首位。 港股IPO市场持续火爆&#xff0c;不少朋友没有…

RESTful API 在前后端交互中的作用与实践

一、RESTful API 概述 RESTful&#xff08;Representational State Transfer&#xff09;API 是一种基于 HTTP 协议、面向资源的架构风格&#xff0c;旨在实现前后端的松散耦合和高效通信。它通过定义统一的资源标识、操作方法以及数据传输格式&#xff0c;为前后端提供了一种…

python打卡训练营打卡记录day35

知识点回顾&#xff1a; 三种不同的模型可视化方法&#xff1a;推荐torchinfo打印summary权重分布可视化进度条功能&#xff1a;手动和自动写法&#xff0c;让打印结果更加美观推理的写法&#xff1a;评估模式 作业&#xff1a;调整模型定义时的超参数&#xff0c;对比下效果 1…