【Linux】进程间通信 —— 管道
文章目录
- 📕 进程间通信介绍
- 📕 匿名管道
- 原理
- 使用
- 读写规则
- 特点
- 📕 命名管道
- 原理
- 使用
- 匿名管道和命名管道的区别
📕 进程间通信介绍
进程间通信,顾名思义,就是两个进程之间的 “交流” ,我们知道,进程是相互独立的,也就是说,正常情况下,两个进程之间无法传递消息,但是有时候又需要 进程间通信,如下,这是进程间通信的目的。
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 资源共享:多个进程之间共享同样的资源。
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
- 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
要达到这些目的,就需要使用一些技术来完成进程间通信。以下是三类技术,管道 、System V、POSIX 。本篇文章主要讲解管道的方法。
- 管道
- 匿名管道pipe
- 命名管道
- System V IPC
- System V 消息队列
- System V 共享内存
- System V 信号量
- POSIX IPC
- 消息队列
- 共享内存
- 信号量
- 互斥量
- 条件变量
- 读写锁
那么什么是管道呢?
管道是Unix中最古老的进程间通信的形式。我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”。
听起来很抽象,可以了解一下管道的原理,就明白了!
📕 匿名管道
原理
如下图,一个进程可以打开多个文件,其 file_struct 里面的文件描述符 0、1、2 分别指向了内存里的 三个 struct file,在文件描述符那篇文章有提到,每一个 struct file 都有对应的缓冲区,是内存级别的。(要理解这里的内容,首先要了解文件描述符哦,可以看一下这篇文章:【Linux】文件描述符)
所以,我们可以用操作系统创建管道的系统调用,打开一个文件(下图中绿色部分),分别以 读、写 方式打开(具体原因下文会讲),然后这个打开的文件会对应有 struct file 和 缓冲区。这些都是操作系统的行为,所以即使磁盘上没有对应的文件,也可以这样打开。当不需要使用管道的时候,直接将内存里的对应 struct file 和缓冲区释放即可。
所以,管道就是一个妥妥的内存级的文件!!
此时,我们再 fork() ,产生一个子进程,子进程会继承父进程的绝大多数内容,包括 file_struct ,但是子进程并不会创建新的文件,因为这是属于文件系统范畴了, fork() 只是创建子进程。此时,子进程同样地也以读写的方式打开了父进程创建的管道文件。
然后,由于管道是半双工的,所以要在父子进程里面,一个关闭读端,一个关闭写端,就可以完成进程间通信的基本条件。
比如这里,我需要父进程写入信息,子进程读取父进程的信息,那么就把父进程的读端关闭,子进程的写端关闭,这样子就是父进程写、子进程读!
使用
创建匿名管道需要用到 pipe() 系统调用。
头文件:#include <unistd.h>
功能:创建一个匿名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码
使用匿名管道需要注意几点,首先是要在创建子进程之前,创建管道。否则子进程无法继承父进程的文件描述符。
其次,不要忘记关闭读端或者写端。
#include <iostream>
#include <unistd.h>
#include <cerrno>
#include <string.h>
#include <stdio.h>using std::cout;
using std::endl;int main()
{int pipefd[2] = {0};// 1.创建管道int n = pipe(pipefd); // pipefd[0] 写端 pipefd[1] 读端if (n == -1){cout << "create pipe error," << errno << strerror(errno) << endl;exit(1);}// cout<<pipefd[0]<<endl<<pipefd[1];// 2.创建子进程pid_t pid = fork();// 子进程if (pid == 0){// 3.子进程写,那么关闭读端close(pipefd[0]);// 4.通信char buf[128];int cnt = 1;while (true){snprintf(buf, sizeof buf, "我是子进程,cnt:%d ,pid: %d\n", cnt++, getpid());write(pipefd[1], buf, strlen(buf));}close(pipefd[1]); // 管道声明周期随进程,所以不手动关闭,进程结束后也会自动关闭exit(1);}// 父进程// 3.父进程读,关闭写端close(pipefd[1]);// 4.通信char buf[128];while (true){sleep(3);int n = read(pipefd[0], buf, sizeof(buf) - 1); // 留出一个位置放\0if (n > 0){buf[n]='\0';cout << "我是父进程,子进程给的信息:" << buf << endl;}}close(pipefd[0]);return 0;
}
上面的 写入、读取 代码可以看出,写入速度快,读写数据慢。 写入了很多次,才读取一次。但是下图运行结果,并不是写入一次,就读取一次,而是可以写入多次,然后一次性读取。
从这里可以看出,读写不是强相关的。(这里指的是读写次数的多少)
读写规则
- 如果读端读取了管道内的所有数据,而写端不写入,那么只能等待。
- 如果写端将管道写满了,读端没有读取数据,那么就无法写入。
- 如果写端关闭,读端依然打开,在读取完管道内剩余的数据之后,再次读取数据,则 read 返回0。
- 如果读端关闭,而写端还向管道写入数据,毫无疑问这是没有意义的,操作系统不会运行这样的事情发送。所以,write 操作会产生信号SIGPIPE,进而可能导致write进程退出。
特点
- 单向通信(半双工)
- 管道的本质是文件,因为 fd 的生命周期随进程,所以管道的生命周期也是随进程的
- 管道通信,通常用来 对具有“血缘”关系的进程,进行进程间通信。常用于父子通信 – pipe 打开管道,并不清楚管道的名字(匿名管道)。
- 在管道通信中,写入的次数 和 读取的次数是不严格匹配的,读写次数的多少没有强相关,读取是按照字节流来读取的。
- 根据上面的读写规则,可以知道,管道具有一定的协同能力,能让 reader 和 writer 按照一定的步骤进行通信——自带同步机制。
📕 命名管道
原理
如下,一个进程打开了磁盘上的一个文件。当另一个进程(和之前的进程没有血缘关系),也打开同一个文件的时候,操作系统不会重新创建一个 struct file 对象,而是直接使用之前的。
那么,此时,两个进程就看到了同一个文件,也就可以进行进程间通信。
但是,如果这个文件是普通文件,那么数据会定期刷新的磁盘里。可是如果我们要进行进程间通信,数据就不应该被刷新到磁盘里面,而是在进程之间进行传输。所以,这就要求创建的文件是一个内存级别的文件,由此诞生了命名管道文件!不需要维护其 datablock,只需要告诉操作系统,这个文件存在!以后 两个进程可以打开这个文件,打开文件就会在内核匹配对应的缓冲区,所以两个进程就看到同一份资源!!
这个原理和上面匿名管道的原理其实是一样的!!
使用
命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
- $ mkfifo filename
命名管道也可以从程序里创建,相关函数有:
- int mkfifo(const char *filename,mode_t mode);
如下是在程序里面实现命名管道,以及进程间通信。 server 进程和 client 进程进行交互。
comm.hpp 头文件
#pragma once#include<iostream>
#include<string>#define NUM 1024const std::string filename="./fifo";mode_t mode=0666;
server.cc 文件
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include<fcntl.h>
#include <cerrno>
#include <cstring>
#include<unistd.h>#include"comm.hpp"using std::cin;
using std::cout;
using std::endl;int main()
{// 1.创建命名管道umask(0);int n = mkfifo(filename.c_str(),mode);if(n < 0){cout<<"create fifo error:"<<errno<<":"<<strerror(errno)<<endl;return 1;}cout<<"create fifo success"<<endl;// 2.开启管道文件int rfd=open(filename.c_str(),O_RDONLY);if(rfd < 0){cout<<"open fifo file error:"<<errno<<":"<<strerror(errno)<<endl;return 1;}cout<<"open fifo file success"<<endl;// 3. 开始通信char buf[NUM];while(true){// 先将缓冲区置0buf[0]=0;size_t n=read(rfd,buf,sizeof(buf)-1); // 读取是按字节流,所以去掉 \0if(n > 0){buf[n]='\0';cout<<"client#"<<buf<<endl;fflush(stdout);}else if(n==0){cout<<"client quit,i will quit either"<<endl;break;}else{cout<<errno<<":"<<strerror(errno)<<endl;}}close(rfd);unlink(filename.c_str());return 0;
}
client.cc 文件
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include<cassert>#include "comm.hpp"using std::cin;
using std::cout;
using std::endl;int main()
{// 打开fifoint wfd=open(filename.c_str(),O_WRONLY);if(wfd < 0){cout<<"open fifo error"<<errno<<":"<<strerror(errno)<<endl;return 1;}cout<<"open fifo success"<<endl;// 写入char buf[NUM];while(true){cout<<"请输入你的信息#";char* msg=fgets(buf,sizeof(buf),stdin); // C库的函数,会默认加上 \0assert(msg);(void*)msg;size_t n=write(wfd,buf,sizeof(buf)-1);if(strcasecmp(buf,"quit") == 0) break;}close(wfd);return 0;
}
如下,server 是读端, client 是写端,只有 client 写入消息, server 才会读取,实现进程间通信。
如果单单在 server 写入消息, client 是不会读取的,因为 server是读端, client 是写端。
匿名管道和命名管道的区别
- 匿名管道由pipe函数创建并打开。
- 命名管道由mkfifo函数创建,打开用open
- FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。
相关文章:

【Linux】进程间通信 —— 管道
文章目录 📕 进程间通信介绍📕 匿名管道原理使用读写规则特点 📕 命名管道原理使用匿名管道和命名管道的区别 📕 进程间通信介绍 进程间通信,顾名思义,就是两个进程之间的 “交流” ,我们知道&…...

知识管理在企业中的重要性
随着经济全球化和信息化的快速发展,企业面临着越来越多的竞争和挑战。如何把握市场动态、满足客户需求、提高产品质量和效率等,成为了企业发展中亟待解决的问题。而知识管理作为一种新兴的管理方式,逐渐引起了企业们的重视。本文将从以下几个…...
Socks5、网络安全、代理IP技术详解
随着互联网的发展,网络安全问题越来越受到人们的关注。为了保护个人隐私和网络安全,使用代理服务器成为了一种普遍的选择。其中,Socks5协议是一种常见的代理协议,而代理IP是使用代理服务器时经常需要考虑的问题。本文将深入探讨So…...

C++学习day--09 字符串比较、运算符
1、项目练习 第 1 节 项目需求、项目实现 项目实现: #include <iostream> #include <Windows.h> #include <string> using namespace std; int main( void ) { string name; string pwd; std::cout << " 请输入账号&am…...

缓存和数据库一致性问题
如何保证缓存和数据库一致性,这是一个老生常谈的话题了。 但很多人对这个问题,依旧有很多疑惑: 到底是更新缓存还是删缓存? 到底选择先更新数据库,再删除缓存,还是先删除缓存,再更新数据库&am…...

4月京东生鲜水果行业数据报告:榴莲销量增长400%,市场格局剧变
众所周知,今年水果领域的一个重磅消息就是:榴莲价格暴跌。目前全国多地线下水果专卖店、农贸市场的榴莲价格都在下滑,有的地区在4月底甚至已经降至最低每斤20元左右。预测在5月的销售旺季,价格还有望一路向下。 •榴莲逆袭苹果&am…...

Windows无法完成格式化怎么办?正确的3个解决方法!
案例:Windows无法完成格式化怎么办 【由于我的U盘使用时间过长,很多文件都是不需要的,我想将其格式化,但插入电脑后,Windows根本无法完成格式化,这是为什么呢?我应该怎么做呢?求答案…...

基于aspnet个人博客网站dzkf6606程序
系统使用Visual studio.net2010作为系统开发环境,并采用ASP.NET技术,使用C#语言,以SQL Server为后台数据库。 1.系统登录:系统登录是用户访问系统的路口,设计了系统登录界面,包括用户名、密码和…...

不黑艺术学社京藏行——参观五台山孙溟㠭为五台山红英师治印
不黑学社社长孙溟㠭先生与五台山菩萨顶主事红英师 不黑学社京藏行,路经五台把佛拜。 巍巍五台清凉境,参访伊始菩萨顶。 感恩“天珠”刘诗语,芬芳佛语满香华。 感恩慈悲红英师,带众参拜大白塔。 菩萨顶上如意宝,莲…...

mysql数据之表管理-mysql高级管理
1. #创建表tt01 #对id字段设置零填充约束、主键约束、自增长约束 #对name字段设置非空约束、默认值约束 #对cardid字段设置非空约束、唯一键约束 插入数据记录: 1)因为id字段设置了自增长,如果不指定id字段值,则默认从1开始递…...

公司新来的00后真是卷王,工作没2年,跳槽到我们公司起薪18K都快接近我了
说00后躺平了,但是有一说一,该卷的还是卷。这不,前段时间我们公司来了个00后,工作都没两年,跳槽到我们公司起薪18K,都快接近我了。后来才知道人家是个卷王,从早干到晚就差搬张床到工位睡觉了。 …...

面试题30天打卡-day19
1、TCP 和 UDP 协议有什么区别,分别适用于什么场景? TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种常用的传输层协议,两者的区别比较如下: TCPUDP可靠性…...

ASEMI代理ADI亚德诺LTC6992IS6-1#TRMPBF车规级芯片
编辑-Z LTC6992IS6-1#TRMPBF参数描述: 型号:LTC6992IS6-1#TRMPBF 输出频率:3.81Hz 工作电源电压范围:2.25 - 5.5V 通电复位电压:1.95V 电源电流:105-365A SET引脚处的电压:1V 频率设置电…...
Oracle PL/SQL基础语法学习15:静态表达式
系列文章目录 Oracle PL/SQL基础语法学习12:短路求值 Oracle PL/SQL基础语法学习13:比较运算符 Oracle PL/SQL基础语法学习14:BOOLEAN表达式 文章目录 系列文章目录前言Oracle PL/SQL基础语法学习15:静态表达式Static Expression…...

B-Tree (多路查找树)分析-20230503
B-Tree (多路查找树)学习-20230503 前言 B-树是一类多路查询树,它主要用于文件系统和某些数据库的索引,如果采用二叉平衡树访问文件里面的数据,最坏情况下,磁头可能需要进行O(h)次对磁盘的读写,其中h为树的高度&…...

OpenGL光照教程之 透光物
引言 我们目前使用的所有光照都来自于一个单独的光源,这是空间中的一个点。它的效果不错,但是在真实世界,我们有多种类型的光,它们每个表现都不同。一个光源把光投射到物体上,叫做投光。这个教程里我们讨论几种不同的投…...
如何使用hook?
目标:将posix函数hook住 一个简单的例子 (连接mysql服务),连接成功则打印success mysql.c #include <mysql/mysql.h> #include <stdio.h> int main(){MYSQL* mysql mysql_init(NULL);if(!mysql){printf("my…...
双指针技巧秒杀七道链表题目
文档阅读 文档阅读 题目 141. 环形链表 https://leetcode.cn/problems/linked-list-cycle/ public class Solution {public boolean hasCycle(ListNode head) {ListNode fast head, slow head;while(fast ! null && fast.next ! null){fast fast.next.next;slo…...

在“裸奔”时代保护我们的隐私:网络攻击、数据泄露与隐私侵犯的应对策略与工具
摘要:随着信息技术的普及和发展,个人隐私和数据安全问题日益受到威胁。本文将讨论如何有效应对网络攻击、数据泄露和隐私侵犯,并提供一系列实用的技巧和工具,以帮助我们在“裸奔”时代更好地保护数据安全和隐私。 当今社会&#…...

如何写出高质量代码
你是否曾经为自己写的代码而感到懊恼?你是否想过如何才能写出高质量代码?那就不要错过这个话题!在这里,我们可以讨论什么是高质量代码,如何写出高质量代码等问题。无论你是初学者还是资深开发人员,都可以在…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...

Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...

【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...

Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...

DeepSeek越强,Kimi越慌?
被DeepSeek吊打的Kimi,还有多少人在用? 去年,月之暗面创始人杨植麟别提有多风光了。90后清华学霸,国产大模型六小虎之一,手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水,单月光是投流就花费2个亿。 疯…...