【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);//forkpid_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]);//子进程关闭写端fdcout<<"子进程关闭了自己的写端"<<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);//readchar 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);//writechar 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(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【electron】electron安装过慢和打包报错:Unable to load file:

文章目录 一、安装过慢问题:二、打包报错&#xff1a;Unable to load file: 一、安装过慢问题: 一直处于安装过程 【解决】 #修改npm的配置文件 npm config edit#添加配置 electron_mirrorhttps://cdn.npm.taobao.org/dist/electron/二、打包报错&#xff1a;Unable to load…

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回)

目录 1. 用户登录权限校验 1.1 最初用户登录权限效验 1.2 Spring AOP 用户统⼀登录验证 1.3 Spring 拦截器 &#xff08;1&#xff09;创建自定义拦截器 &#xff08;2&#xff09;将自定义拦截器添加到系统配置中&#xff0c;并设置拦截的规则 1.4 练习&#xff1a;登录…

for macOS-21.1.0.3267中文直装版功能介绍及系统配置要求

FL Studio 21简称FL水果软件,全称是&#xff1a;Fruity Loops Studio编曲&#xff0c;由于其Logo长的比较像一款水果因此&#xff0c;在大家更多的是喜欢称他为水果萝卜&#xff0c;FL studio21是目前最新的版本&#xff0c;这是一款可以让你的计算机就像是一个全功能的录音室&…

最强自动化测试框架Playwright(10)- 截图

截图 捕获屏幕截图并将其保存到文件中&#xff1a; page.screenshot(path"screenshot.png")可将页面截图保存为screen.png import osfrom playwright.sync_api import Playwright, expect, sync_playwrightdef run(playwright: Playwright) -> None:browser p…

数学建模(二)线性规划

课程推荐&#xff1a;6 线性规划模型基本原理与编程实现_哔哩哔哩_bilibili 在人们的生产实践中&#xff0c;经常会遇到如何利用现有资源来安排生产&#xff0c;以取得最大经济效益的问题。此类问题构成了运筹学的一个重要分支&#xff1a;数学规划。而线性规划(Linear Program…

android Ndk Jni动态注册方式以及静态注册

目录 一.静态注册方式 二.动态注册方式 三.源代码 一.静态注册方式 1.项目名\app\src\main下新建一个jni目录 2.在jni目录下,再新建一个Android.mk文件 写入以下配置 LOCAL_PATH := $(call my-dir)//获取当前Android.mk所在目录 inclu

【Redis】Spring/SpringBoot 操作 Redis Java客户端

目录 操作 Redis Java客户端SpringBoot 操作Redis 步骤 操作 Redis Java客户端 1.Jedis 2.Lettuce(主流) <-Spring Data Redis SpringBoot 操作Redis 步骤 1.添加Redis 驱动依赖 2.设置Redis 连接信息 spring.redis.database0 spring.redis.port6379 spring.redis.host…

【Linux操作系统】深入理解系统调用中的read和write函数

在操作系统中&#xff0c;系统调用是用户程序与操作系统之间进行交互的重要方式。其中&#xff0c;read和write函数是常用的系统调用函数&#xff0c;用于在用户程序和操作系统之间进行数据的读取和写入。本文将深入介绍read和write函数的工作原理、用法以及示例代码&#xff0…

springboot异步任务

在Service类声明一个注解Async作为异步方法的标识 package com.qf.sping09test.service;import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service;Service public class AsyncService {//告诉spring这是一个异步的方法Asyncp…

使用gpt对对话数据进行扩增,对话数据扩增,数据增强

我们知道一个问题可以使用很多方式问&#xff0c;但都可以使用完全一样的回答&#xff0c;基于这个思路&#xff0c;我们可以很快的扩增我们的数据集。思路就是使用chatgpt或者gpt4生成类似问题&#xff0c;如下&#xff1a; 然后我们可以工程化这个过程&#xff0c;从而快速扩…

【Github】SourceTree技巧汇总

sourceTree登录github账户 会跳转到浏览器端 按照Git Flow 初始化仓库分支 克隆远程仓库到本地 推送变更到远程仓库 合并分支 可以看到目前的本地分支&#xff08;main、iOS_JS&#xff09;和远程分支&#xff08;origin/main、origin/HEAD、origin/iOS_JS&#xff09;目前所处…

【问题记录】antd icons报rev属性缺失错误

闲来无事将项目中的antd从v4升级到了v5&#xff0c;之前正常的页面中如有图标&#xff0c;如<PlusOutlined />&#xff0c;总是报以下错误&#xff1a; TS2741: Property rev is missing in type {} but required in type Pick<AntdIconProps, "name" …

如何实现Vue路由的二级菜单

目录 Vue路由&#xff08;一、二级路由&#xff09; 一级路由配置 二级路由配置 Vue中展示二级路由的默认模块/二级路由默认显示 Vue路由&#xff0c;二级路由及跳转 如何用vue实现二级菜单栏 ◼️ 相关参考资料 当朋友们看到这个文章时想必是想要了解vue路由二级菜单相…

React UI组件库

1 流行的开源React UI组件库 1 material-ui(国外) 官网: Material UI: React components based on Material Design github: GitHub - mui/material-ui: MUI Core: Ready-to-use foundational React components, free forever. It includes Material UI, which implements Go…

人大金仓助力某大型金融机构业务系统异地容灾优化升级

日前&#xff0c;人大金仓助力某大型金融机构应收账款融资服务平台异地容灾项目顺利上线&#xff0c;保证了平台系统运行的连续性和数据安全&#xff0c;为充分发挥平台的融资功能&#xff0c;缓解中小微企业融资难提供了强有力的保障。 “ 缓解中小微企业融资难 某大型金融机构…

Stephen Wolfram:意义空间和语义运动规律

Meaning Space and Semantic Laws of Motion 意义空间和语义运动规律 We discussed above that inside ChatGPT any piece of text is effectively represented by an array of numbers that we can think of as coordinates of a point in some kind of “linguistic feature …

【第二阶段】在函数中定义参数是函数的函数

1.理解&#xff1a;在一个函数中有一个参数a,这个参数a又属于一个函数&#xff0c;a即时参数又是函数 2.用kotlin实现登录 /*** You can edit, run, and share this code.* play.kotlinlang.org*/ fun main() {//调用传参//普通参数传入即可,针对在调用函数中的参数函数传入使用…

Android Studio实现Spinner下拉列表

效果图 点击下拉列表 点击某一个下拉列表 MainActivity package com.example.spinneradapterpro;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.Spinn…

关于使用pycharm遇到只能使用unittest方式运行,无法直接选择Run

相信大家可能都遇到过这个问题&#xff0c;使用pycharm直接运行脚本的时候&#xff0c;只能选择unittest的方式&#xff0c;能愁死个人 经过几次各种尝试无果之后&#xff0c;博主就放弃死磕了&#xff0c;原谅博主是个菜鸟 后来遇到这样的问题&#xff0c;往往也就直接使用cm…

差分升级在物联网水表上的实现与应用(学习)

摘要 当越来越多的物联网水表加入抄表系统后&#xff0c;实现了水表数据的信息化&#xff0c;并且当水表终端需要技术更新时&#xff0c;通过网络方式来升级产品可以高效修复设备面临的问题&#xff0c;减少用户损失&#xff0c;降低维护成本&#xff0c;但同时也对有限的网络…