【Linux】进程间通信之管道

【Linux】进程间通信之管道

  • 进程间通信
    • 进程间通信目的
    • 进程间通信的方式
  • 管道(内核维护的缓冲区)
    • 匿名管道(用于父子间进程间通信)
      • 简单使用
      • 阻塞状态读写特征
      • 非阻塞状态读写特征
    • 匿名管道特点
    • 命名管道
  • 匿名管道与命名管道的区别

进程间通信

进程之间具有独立性,进程间通信必定需要一块公共的区域用来作为信息的存放点,操作系统需要直接的或间接给通信进程双方提供内存空间。
如果内存空间是文件系统提供的,那么就是管道通信
如果OS提供的就是共享内存
通信的本质就是让不同的进程看到同一份内存空间

进程间通信目的

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

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

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

进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程)

进程间通信的方式

管道: 匿名管道命名管道
System V:System V 消息队列;System V 共享内存;System V 信号量
POSIX:消息队列;共享内存;信号量;互斥量;条件变量;读写锁

管道(内核维护的缓冲区)

管道是基于文件系统的通信方式

任何一个文件包括两套资源:
1.file的操作方法
2.有属于自己的内核缓冲区,所以父进程和子进程有一份公共的资源:文件系统提供的内核缓冲区,父进程可以向对应的文件的文件缓冲区写入,子进程可以通过文件缓冲区读取,此时就完成了进程间通信,这种方式提供的文件称为管道文件。管道文件本质就是内存级文件,不需要IO。

匿名管道(用于父子间进程间通信)

通过父进程fork创建子进程,让子进程拷贝父进程中的文件描述符表,两个进程便能看到同一个管道文件,这个管道文件是一个内存级文件,并没有名字,所以被称为匿名管道
在这里插入图片描述

匿名管道中,父子进程必须关闭一个文件描述符,形成单向通信

简单使用

#include <unistd.h>
int pipe(int pipefd[2]);
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
成功时返回零,错误时返回 -1
#include <iostream>
#include <unistd.h>
#include <cassert>
#include <cstring>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;
//父进程进行读取,子进程负责写入
int main()
{
    //第一步:创建管道文件,打开读写端
    //fds[0]读   fds[1]写
    int fds[2];
    int n=pipe(fds);
    assert(n==0);

    //fork
    pid_t id=fork();
    assert(id>=0);
    if(id==0)
    {
        //子进程写入,先关闭读
        close(fds[0]);

        //子进程通信代码
        const char* s="我是子进程,我正在给你发信息";
        int cnt=0;
        while (true)
        {
            cnt++;
            char buffer[1024];//只有子进程能访问
            //格式化写入
            snprintf(buffer,sizeof(buffer),"chile->parent say: %s[%d][%d]",s,cnt,getpid());
            //子进程向【fd[1]】不断写,父进程【fd[0]】延迟读,导致写阻塞
            write(fds[1],buffer,strlen(buffer));

            //cout<<"count"<<cnt<<endl;
            //sleep(5);//延迟写,父进程read阻塞

            //break;//写一次直接关闭读端,读端read返回0,

        }
        //出来还是子进程
        close(fds[1]);//子进程关闭写端fd
        cout<<"子进程关闭了自己的写端"<<endl;
        exit(0);
        
    }

    //父进程读取
    close(fds[1]);
    //父进程通信代码
    while(true)
    {
        //sleep(3000);//写端不断写,读端延迟读,写端阻塞
        //sleep(2);//写端不断写,读端缓读,数据积累在一起被读出
        char buffer[1024];

        //如果管道中没有数据,读端在读,默认阻塞当前读进程由R->S状态
        //cout<<"parent_read_begin..."<<endl;
        ssize_t s= read(fds[0],buffer,sizeof(buffer)-1);//阻塞式调用
        //cout<<"parent_read_end..."<<endl;

        if(s>0)
        {
            buffer[s]=0;
            cout<<"父进程读到了# "<<buffer<<" | my_pid: "<<getpid()<<endl;
        }
        else if(s==0)
        {
            //读到了文件末尾
            cout<<"read: over "<<s<<endl;
            break;
        }
        break;//读一次直接退出,父进程准备关闭读端,os会发送信号杀掉写进程
    }
    //读未关闭
    // n=waitpid(id,nullptr,0);
    // assert(n==id);
    // close(fds[0]);



    //关闭读端,程序无效
    close(fds[0]);
    cout<<"父进程关闭写端"<<endl;
    int status=0;
    n=waitpid(id,&status,0);

    获取进程终止信号
    cout<<"waitpid->"<<n<<" : "<<(status&0x7f)<<endl;

    
    return 0;
}

阻塞状态读写特征

1)如果管道中没有数据,读取端进程再进行读取,会阻塞当前正在读取的进程

2)如果写入端写满了,再写就会对该进程进行阻塞,需要等待对方对管道内数据进行读取

3)如果写入进程关闭了写入fd,读取端将管道内的数据读完后read的返回值为0,正常退出

4)如果读关闭,写就没有意义了,操作系统会给写端发送13号信号SIGPIPE,终止写端。(信号参考)

非阻塞状态读写特征

创建的匿名管道默认是阻塞状态的,如果要设置成非阻塞状态,需要使用fcntl
第一种:写设置为非阻塞
1)读端不关闭,写端一直写,写满之后,wirte会返回-1,报错当前资源不可用
2)读端直接关掉,写端一直在写,当前进程会收到SIGPIPE信号,写端的程序直接被杀死,这种现象叫做管道破裂
第二种:读设置为非阻塞
1)写端不关闭,读端一直读,read调用返回-1,errno为EAGAIN
2)写端关闭,读端进行读,read是正常调用返回0,表示没有读到

匿名管道特点

1)只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道;

2)管道提供流式服务 ;

3)进程退出,管道释放,所以管道的生命周期随进程

4)内核会对管道操作进行同步与互斥

5)管道是半双工。需要双方通信时,需要建立起两个管道
6)管道的大小是64K(65536)
7)创建匿名管道返回的文件描述符属性默认是阻塞的

命名管道

上面说到,匿名管道只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信,两个不相关的进程如果要通信,需要借助命名管道

创建命名管道

int mkfifo(const char *pathname, mode_t mode);
pathname:命名管道文件所在路径
mode:文件权限

删除命名管道

#include <unistd.h>
int unlink(const char *path);

命名管道可用于无血缘关系的进程间通信,两个进程均打开同一路径下的同名文件(看到同一份资源);命名管道也是一个内存级文件


comm.hpp(提供命名管道的创建和销毁方法)

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define NAMED_PIPE "./myname_pipe"

bool CreateFifo(const std::string &path)
{
    umask(0);
    int n=mkfifo(path.c_str(),0600);
    if(n==0)
        return true;
    else
    {
        std::cout<<"errno: "<< errno <<"err string: "<<strerror(errno)<<std::endl;
        return false; 
    }    
}

void removeFifo(const std::string &path)
{
    int n=unlink(path.c_str());
    assert(n==0);
    std::cout<<"removeFifo success ...."<<std::endl;

    (void)n;//避免爆出waring:未引用的变量n
}

server.cc(读端,先运行)

#include "comm.hpp"

int main()
{
    bool r=CreateFifo(NAMED_PIPE);
    assert(r);
    (void)r;
    //先运行此文件,会在open时阻塞
    //管道文件要求读写都要打开文件才能执行后面的功能
    std::cout<< "server begin" <<std::endl;
    int rfd=open(NAMED_PIPE,O_RDONLY);
    std::cout<< "server end" <<std::endl;
    if(rfd<0) exit(1);

    //read
    char buffer[1024];
    while (true)
    {
        ssize_t s=read(rfd,buffer,sizeof(buffer)-1);
        if(s>0)
        {
            buffer[s]=0;
            std::cout<<"cilent ->server# "<<buffer<<std::endl;
        }
        else if(s==0)
        {
            std::cout<<"cilent quit,me tool "<<std::endl;
            break;
        }
        else
        {
            std::cout<<"err string: "<<strerror(errno)<<std::endl;
            break;
        }
    }
    close(rfd);
    sleep(10);
    removeFifo(NAMED_PIPE);
    return 0;
    
}

client.c(写端)

#include "comm.hpp"

int main()
{
    //如果先运行此文件,由于文件还未创建,无法写入
    std::cout << "client begin" << std::endl;
    int wfd=open(NAMED_PIPE,O_WRONLY);
    std::cout<< "client end" <<std::endl;
    if(wfd<0) exit(1);

    //write
    char buffer[1024];
    while (true)
    {
        std::cout<<"Please Say#";
        fgets(buffer,sizeof buffer,stdin);
        if(strlen(buffer)>0)
            buffer[strlen(buffer)-1]=0;
        ssize_t n=write(wfd,buffer,strlen(buffer));
        assert(n==strlen(buffer));
        (void)n;    
    }
    close(wfd);
    return 0;
    
}

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

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

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

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

相关文章

时序预测 | MATLAB实现基于CNN卷积神经网络的时间序列预测-递归预测未来(多指标评价)

时序预测 | MATLAB实现基于CNN卷积神经网络的时间序列预测-递归预测未来(多指标评价) 目录 时序预测 | MATLAB实现基于CNN卷积神经网络的时间序列预测-递归预测未来(多指标评价)预测结果基本介绍程序设计参考资料 预测结果 基本介绍 1.Matlab实现CNN卷积神经网络时间序列预测未…

中国首款量子计算机操作系统本源司南 PilotOS正式上线

中国安徽省量子计算工程研究中心近日宣布&#xff0c;中国国产量子计算机操作系统本源司南 PilotOS 客户端正式上线。 如果把量子芯片比喻成人的“心脏”&#xff0c;那么量子计算机操作系统就相当于人的“大脑”&#xff0c;量子计算应用软件则是人的“四肢”。 据安徽省量子…

C++入门篇7---string类

所谓的string类&#xff0c;其实就是我们所说的字符串&#xff0c;本质和c语言中的字符串数组一样&#xff0c;但是为了更符合C面向对象的特性&#xff0c;特地将它写成了一个单独的类&#xff0c;方便我们的使用 对其定义有兴趣的可以去看string类的文档介绍&#xff0c;这里…

运维监控学习笔记3

DELL的IPMI页面的登录&#xff1a; 风扇的状态&#xff1a; 电源温度&#xff1a;超过70度就告警&#xff1a; 日志信息&#xff1a; 可以看到更换过磁盘。 iDRAC的设置 虚拟控制台&#xff1a;启动远程控制台&#xff1a; 可以进行远程控制。 机房工程师帮我们接远程控制&…

opencv 基础54-利用形状场景算法比较轮廓-cv2.createShapeContextDistanceExtractor()

注意&#xff1a;新版本的opencv 4 已经没有这个函数 cv2.createShapeContextDistanceExtractor() 形状场景算法是一种用于比较轮廓或形状的方法。这种算法通常用于计算两个形状之间的相似性或差异性&#xff0c;以及找到最佳的匹配方式。 下面是一种基本的比较轮廓的流程&…

Dynamic CRM开发 - 实体介绍

实体简介 在CRM中,实体(Entity)是数据的基本载体,也是构建业务逻辑网络的基础节点。 实体可以理解为数据库中的一张表(实体中的字段对应数据库表的字段),比如创建一个实体存储客户信息,创建一个实体存储产品信息,产品实体里可以创建一个查找类型的字段(类似表的外键)…

Json简述(C++)

目录 1.介绍 2.格式 3.底层 3.1数据对象表示 3.2序列化接口 3.3反序列化接口 4.使用 1.介绍 Json&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;其最早是为JavaScript编程语言设计的格式。不过发发展至今&#xff0c;Jso…

C++——缺省参数

缺省参数的定义 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数的时候&#xff0c;如果没有指定实参&#xff0c;则采用该形参的缺省值&#xff0c;否则使用指定的实参。 void Func(int a 0) {cout << a << endl; } int main() { Func()…

并查集的原理与实现

1.概念 2.生活中的例子 小弟-老大&#xff1b; 帮派识别 3.实现 3.1 初始化 3.2 中间过程 3.3合并 3.4 并查集路径优化 直接把下面的节点指向最终的老大。 3.5 伪代码实现 3.6JAVA实现 findRoot: 谁是帮派的老大。例如山鸡的老大是陈浩南 connected: 我们是不是同一个大…

Golang服务的请求调度

文章目录 1. 写在前面2. SheddingHandler的实现原理3. 相关方案的对比4. 小结 1. 写在前面 最近在看相关的Go服务的请求调度的时候&#xff0c;发现在gin中默认提供的中间件中&#xff0c;不含有请求调度相关的逻辑中间件&#xff0c;去github查看了一些服务框架&#xff0c;发…

软工导论知识框架(八)面向对象设计风格

一.面向对象实现 把面向对象设计结果翻译成面向对象程序测试并调试面向对象的程序 二.程序设计语言 所有语言都可完成面向对象实现&#xff0c;但效果不同。 使用非面向对象语言编写面向对象程序&#xff0c;则必须由程序员自己把面向对象概念映射到目标程序中。 1.将来能够占…

Ubuntu常用配置

文章目录 1. 安装VMware虚拟机软件2. 下载Ubuntu镜像3. 创建Ubuntu虚拟机4. 设置屏幕分辨率5. 更改系统语言为中文6. 切换中文输入法7. 修改系统时间8. 修改锁屏时间9. 通过系统自带的应用商店安装软件10. 安装JDK11. 安装 IntelliJ IDEA12. 将左侧任务栏自动隐藏13. 安装docke…

用户数据报协议UDP

UDP的格式 载荷存放的是:应用层完整的UDP数据报 报头结构: 源端口号:发出的信息的来源端口目的端口号:信息要到达的目的端口UDP长度:2个字节(16位),即UDP总长度为:2^16bit 2^10bit * 2^6bit 1KB * 64 64KB.所以一个UDP的最大长度为64KBUDP校验和:网络的传输并非稳定传输,…

css3背景渐变

1.线性渐变 <style>.box {width: 200px;height: 200px;border: 1px solid black;float: left;margin-left: 50px;}.box1 {background-image: linear-gradient(green, yellow, red);}/* 右上 */.box2 {background-image: linear-gradient(to right top, green, yellow, re…

SAP MM学习笔记16-在库品目评价

在库品目评价是指评估物料。具体比如物料价格&#xff0c;数量&#xff0c;保管场所等发生变化的时候&#xff0c;判断是否发生了变化&#xff0c;要不要生成 FI票&#xff0c;用哪个FI科目来进行管理等内容就叫在库品目评价。 在库品目评价有很多层级&#xff0c;这里先讲3兄弟…

基于Qlearning强化学习的路径规划算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 Q值更新规则 4.2 基于Q-learning的路径规划算法设计 4.3 Q-learning路径规划流程 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022A 3.部分核心程序 ..…

Mybatis三剑客(一)在springboot中自动生成Mybatis【generator】

1、pom.xml中新增plugin <plugin><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.3.7</version><configuration><overwrite>true</overwrite><…

分布式 - 服务器Nginx:一小时入门系列之动静分离

文章目录 1. 动静分离的好处2. 分离静态文件3. 修改 Nginx 配置文件4. location 命令修饰符优先级 1. 动静分离的好处 Apache Tocmat 严格来说是一款java EE服务器&#xff0c;主要是用来处理 servlet请求。处理css、js、图片这些静态文件的IO性能不够好&#xff0c;因此&…

IDEA关闭项目,但是后台程序没有关闭进程(解决方案)

最近遇到一个很奇怪的问题&#xff0c;idea关闭项目后&#xff0c;系统进程没有杀死进程&#xff0c;再次执行的时候会提示端口占用&#xff0c;并提示Process exited with an error: 1 (Exit value: 1) 错误原因&#xff1a;应用程序关闭后&#xff0c;进程不能同步关闭 解决方…

[vscode]vscode运行cmake时候exe不执行而且前面多一些字符

遇到一个奇怪问题,你单独打开cmd去执行vscode编译过程序没问题&#xff0c;但是你在vscode确不会执行&#xff0c;这是因为vscode没有读取到电脑环境变量导致加载DLL失败&#xff0c;但是在vscode终端不会给你提示少DLL&#xff0c;需要你自己把DLL复制到exe目录即可解决问题。…