[Linux]:进程间通信(上)

img

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:Linux学习
贝蒂的主页:Betty’s blog

1. 进程间通信介绍

1.1 进程间通信的概念

进程间通信简称IPC(Interprocess communication),进程间通信就是在不同进程之间传播或交换信息。

因为进程之间具有独立性,所以一个进程是无法与另一个进程进行交流的,但是有些情况下我们一个进程必须要

进程接受一个进程的信息,所以操作系统为其提供了特定的方式。

1.1 进程间通信的目的

  1. 数据传输:一个进程要把自己的数据交给另一个进程,让其继续进行处理。
  2. 资源共享:多个进程之间共享同样的资源。
  3. 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  4. 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

1.1 进程间通信的本质

在操作系统中,运行的进程具有独立性,主要体现在数据层面,代码逻辑则有私有和公有情况(父子进程)。这使得进程间通信颇具难度。为实现通信,进程需借助第三方资源,即操作系统提供的一段内存区域。

画板

所以本质上,进程间通信就是让不同进程看到同一份资源,如内存或文件内核缓冲等。因资源可由操作系统不同模块提供,所以就产生了多种进程间通信方式。

2. 匿名管道

2.1 管道的概念

管道Unix中最古老的进程间通信的形式,我们把从一个进程连接到另一个进程的数据流称为一个"管道"。

比如我说我们可以通过指令who | wc -l统计当前使用服务器的用户个数,其中指令who为查看当前服务器的登录用户,指令wc -l为统计当前行数,它们运行起来会成为两个进程。但是我们通过管道|将其链接起来,让指令wc -l读取who指令打印出的数据。

画板

2.2 匿名管道

在进程间通信中,匿名管道是一种单向通信机制。它通常用于具有“血缘关系”的进程之间,并且匿名管道只能在本地机器上使用,不能用于网络通信。

其原理就是,创建一个子进程,让父子进程都指向同一个文件,最后我们就可以让父进程向文件写入/读取数据,子进程向文件读取/写入数据。

画板

那么现在我们肯定有一个疑问,那就是创建子进程时,文件描述符数组fd_array会拷贝一份,但是指向的文件为什么不需要拷贝呢?

因为这个数组是为了让该进程能知晓已经打开的文件的个数,所以文件描述符数组fd_array是属于进程的, 既然属于进程,那子进程也需要拷贝一份,因为进程具有独立性。而这个文件是由我们操作系统所管理的,并不属于我们进程,所以子进程在拷贝时并不会再创建一份文件。

并且值得注意的是:

  • 因为父子进程共用的文件资源是由操作系统来维护的,所以当父子进程对该文件进行写入操作时,该文件缓冲区当中的数据并不会发生写时拷贝。比如说我们父子进程打印信息是在同一个屏幕打印的,而不是分别打印在两个屏幕上。
  • 虽然管道使用的是文件,但操作系统一定不会把进程进行通信的数据刷新到磁盘当中,因为这样做有IO参与会降低效率。也就是说,这种文件是一批不会把数据写到磁盘当中的文件,换句话说,磁盘文件和内存文件不一定是一一对应的,有些文件只会在内存当中存在,而不会在磁盘当中存在。

2.3 pipe函数

并且Linux系统中也为我们提供了创建匿名管道的函数——pipe函数,其使用方式如下:

  1. 函数原型:int pipe(int pipefd[2]);
  2. 返回值:创建成功返回0,否则返回-1。
  3. 参数:pipefd[2]是一个输出型参数,其中pipefd[0]代表的是管道读端的文件描述符,pipefd[1]代表的是管道写端的文件描述符。

然后我们就可以使用我们代码来实现我们的进程间通信,其中我们让父进程写入数据,子进程读取数据。

  1. 首先第一步父进程创建出一个管道。

画板

  1. 第二步父进程创建子进程。

画板

  1. 最后一步关闭对应读写端,实现单向通信。

画板

代码实现如下:

#include<cstdio>
#include<cstdlib>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<iostream>
#include<cstring>
#include<string>
using namespace std;
#define N 2
#define NUM 1024
void writer(int wfd)
{string str="hello child, i am father";pid_t id=getpid();int num=0;char buffer[NUM];while(true){buffer[0] = 0; // 字符串清空,只是为了提醒阅读代码的人,我把这个数组当做字符串了snprintf(buffer,sizeof(buffer),"%s-%d-%d",str.c_str(),id,num);write(wfd,buffer,strlen(buffer));sleep(1);num++;}}
void reader(int rfd)
{char buffer[NUM];while(true){ssize_t n=read(rfd,buffer,sizeof(buffer)-1);if(n>0){buffer[n]=0;}cout<<"child ("<<getpid()<<") get a message:"<<buffer<<endl;}
}
int main()
{int pipefd[2]={0};if(pipe(pipefd) < 0){perror("pipe:");return 1;}pid_t id = fork();if(id==0){//child->readclose(pipefd[1]);//关闭写端reader(pipefd[0]);close(pipefd[0]);}else if(id>0){//father->writeclose(pipefd[0]);writer(pipefd[1]);//进程等待pid_t ret=waitpid(id,nullptr,0);if(ret<0){perror("waitpid:");return 1;}close(pipefd[1]);}else{//fork errorperror("fork:");return 1;}return 0;
}

3. 命名管道

3.1 命名管道

匿名管道只能用于具有"亲缘关系"的进程间通信,所以匿名管道就具有局限性,如果我们想让两个毫不相关的进程间进行通信,就需要使用我们的命名管道。

命名管道与匿名管道都是只存在于内存中的文件,并不会向磁盘刷新,唯一不同的是匿名管道是通过父子进程看到同一份资源,而命名管道是通过路径与文件名的方式找到同一份文件资源,因为我们知道路径具有唯一性。

3.2 mkfifo函数

首先我们可以在命令行通过指令mkfifo 管道名创建一个命名管道,并且我们可以直接通过其进行通信。

然后我们可以在程序中mkfifo函数进行管道创建:

  1. 函数原型:int mkfifo(const char *pathname, mode_t mode);
  2. 返回值:创建成功返回0,否则返回-1。
  3. 参数:mkfifo函数的第一个参数是pathname,表示要创建的命名管道文件。若pathname以路径的方式给出,则将命名管道文件创建在pathname路径下。若pathname以文件名的方式给出,则将命名管道文件默认创建在当前路径下。mkfifo函数的第二个参数是mode,表示创建命名管道文件的默认权限,其守默认掩码umask的约束。

比如说我们可以通过该接口实现客户端client与服务端server间的通信。

首先是需要包含的头文件,以及为了方便管理管道,我们可以将管道文件名定义为宏。

//comment.h
#pragma once
#include<stdio.h>
#include<sys/stat.h>
#include<sys/stat.h> 
#include<sys/types.h> 
#include<fcntl.h> 
#include<unistd.h> 
#define MY_FIFO "./fifo"  //管道的创建路径

以下分别为server.cclient.c的实现:

//server.c
#include"comment.h"
int main()
{umask(0);//将文件默认掩码设置为0if(mkfifo(MY_FIFO,0666)<0)  //创建管道{perror("mkfifo:");return 1;}int fd = open(MY_FIFO,O_RDONLY);//以读方式打开命名管道文件if(fd<0){perror("open");return 2;}//处理业务逻辑,进行相应的读写while(true){char buffer[64] = {0};ssize_t s =  read(fd,buffer,sizeof(buffer)-1);if(s > 0){//读取成功buffer[s] = 0;//字符串末尾置\0printf("client send: %s\n",buffer);}else if(s == 0){//写端关闭,读取数据个数为0,printf("client close\n");break;}else{//读取错误perror("read:");break;}}close(fd);return 0;
}
#include"comment.h"
#include<string.h>   
int main()
{int fd = open(MY_FIFO,O_WRONLY);//以写方式打开命名管道文件if(fd<0){perror("open:");return 1;}//处理业务逻辑while(1){char buffer[64] = {0};//1.先从键盘读取内容printf("enter Message: ");fflush(stdout);//刷新缓冲区//从键盘读数据ssize_t s = read(0,buffer,sizeof(buffer)-1);if(s > 0){buffer[s -1]= 0;//提前置\0,把\n覆盖掉//向管道中写入数据write(fd,buffer,strlen(buffer));}}close(fd);return 0;
}

我们通过管道通信还可以实现很多功能,比如我们可以与进程替换相结合实现一个进程对另一个进程的控制等,当然我们这里就不在实现,感兴趣可以自己实现。

最后我们学习完管道相关知识之后,可能会提出以下疑惑:

命令行管道"|"究竟是匿名管道还是命名管道呢?

我们其实可以通过命令行来直接验证一下:

通过观察我们发现,三个sleep进程的PPID都是相同的,即这三个子进程都是"兄弟进程",所以我们命令行管道是匿名管道。

4. 管道的特点

管道具有以下几个特点:

  1. 自带同步与互斥机制

管道在同一时刻只允许一个进程进行写入或读取操作,属于临界资源。为避免多个进程同时操作管道导致同时读写、交叉读写及数据不一致等问题,内核会对管道操作进行同步与互斥。

  • 互斥保证一个公共资源同一时刻只能被一个进程使用,对于管道场景即两个进程不能同时操作,需相互等待。
  • 同步则要求两个或以上进程按预定先后次序运行,在管道场景中不仅不能同时操作,还需按特定次序操作。
  1. 生命周期随进程

管道本质上通过文件进行通信,依赖于文件系统。当所有打开该文件的进程都退出后,对应的管道文件也会被释放,所以管道的存在与使用进程紧密相关,其生命周期随进程。

  1. 提供流式服务

进程 A 写入管道的数据,进程 B 每次从管道读取的数据量是任意的,没有明确的数据分割,不分报文段,这种服务方式被称为流式服务。与数据报服务不同,数据报服务的数据有明确分割,按报文段进行处理。

  1. 半双工通信
  • 单工通信(Simplex Communication):单工模式的数据传输是单向的。通信双方中,一方固定为发送端,另一方固定为接收端。
  • 半双工通信(Half Duplex):半双工数据传输指数据可以在一个信号载体的两个方向上传输,但是不能同时传输。
  • 全双工通信(Full Duplex):全双工通信允许数据在两个方向上同时传输,它的能力相当于两个单工通信方式的结合。全双工可以同时(瞬时)进行信号的双向传输。
  1. 管道是面向字节流的,其大小最大为65536字节,即64KB。

5. 管道的特殊情况

在使用管道通信时,可能会出现以下四种特殊情况:

  1. 情况一:若写端进程不进行写入操作,而读端进程一直尝试读取。此时由于管道中没有数据可供读取,读端进程会被挂起,处于等待状态。只有当管道中有数据时,读端进程才会被唤醒,继续进行读取操作。
  2. 情况二:当读端进程不进行读取,而写端进程持续写入时,一旦管道被写满,写端进程就会被挂起。只有在管道中的数据被读端进程读取后,写端进程才会被唤醒,继续进行写入操作。
  3. 情况三:当写端进程将数据写完后关闭写端,读端进程在将管道中的数据全部读完后,此时read返回值为0,我们可以继续执行该进程之后的代码逻辑。
  4. 情况四:若读端进程关闭读端,而写端进程仍在持续向管道写入数据,此时操作系统会将写端进程杀掉。

前三种情况我们都可以很好理解,我们最后验证一下情况四,既然其会被操作系统杀死,我们就可以通过进程等待获取退出信息来验证:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{int fd[2] = { 0 };if (pipe(fd) < 0){ perror("pipe");return 1;}pid_t id = fork(); //创建子进程if (id == 0){//childclose(fd[0]); //子进程向管道写入数据const char* msg = "hello father, I am child";int count = 5;while (count--){write(fd[1], msg, strlen(msg));sleep(1);}close(fd[1]); //子进程写入完毕,关闭文件exit(0);}//fatherclose(fd[1]); //父进程关闭写端close(fd[0]); //父进程直接关闭读端int status = 0;waitpid(id, &status, 0);printf("child get signal:%d\n", status & 0x7F); //打印子进程收到的信号return 0;
}

我们可以看到,这种情况进程的确会被操作系统杀死,并接受到13号信号。

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

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

相关文章

我对 monorepo 的一些思考

我对 monorepo 的一些思考 我对 monorepo 的一些思考 前言它的由来技术选型 管理工具语言与打包调试工具测试框架代码规范与质量控制本地引用与发包替换发包流程Github 相关配置部署 使用手册 功能特性总结如何使用&#xff1f;清除默认的包(可选)模板包介绍 packagesapps 更新…

GPU池化为实现Robotaxi按下快进键

日前&#xff0c;甲子光年智库推出《2022中国Robotaxi行业研究报告&#xff1a;探寻规模化商业落地之路》。Robotaxi&#xff08;无人驾驶出租车&#xff09;是自动驾驶技术发展应用的终极目标之一&#xff0c;新基建下的智慧共享出行将链接贯穿未来数智化生活全场景。 该报告从…

七. 部署YOLOv8检测器-quantization-analysis

目录 前言0. 简述1. 案例运行2. 补充说明3. 量化分析4. 探讨总结下载链接参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习课程第七章—部署YOLOv8检测器&#xff0c;一起来学习…

C语言:链表

链表是一种常见的基础数据结构&#xff0c;它由一系列节点&#xff08;Node&#xff09;组成。每个节点包含两部分&#xff1a;数据域&#xff08;存储数据&#xff09;和指针域&#xff08;存储下一个节点的地址&#xff09;。链表的特点是元素在内存中不一定连续存储&#xf…

BUUCTF 之Basic 1(BUU LFI COURSE 1)

1、启动靶场&#xff0c;会生成一个URL地址&#xff0c;打开给的URL地址&#xff0c;会看到一个如下界面 可以看到是一个PHP文件&#xff0c;非常的简单&#xff0c;就几行代码&#xff0c;判断一下是否有一个GET的参数&#xff0c;并且是file名字&#xff0c;如果是并且加载&a…

GEE:连续变化检测与分类(Continuous Change Detection and Classification, CCDC)教程

连续变化检测与分类&#xff08;Continuous Change Detection and Classification, CCDC&#xff09;是一种土地变化监测算法&#xff0c;旨在对卫星数据的时间序列进行操作&#xff0c;特别是Landsat数据。CCDC包括两个部分&#xff0c;其一是变化检测算法&#xff08;Change …

python小脚本,实时监测服务器是否宕机状态,并发送到指定群组

一&#xff0c;前言 众所周知&#xff0c;市面上监控软件很多&#xff0c;有Zabbix&#xff0c;Prometheus等&#xff0c;但对于相对简单的功能&#xff0c;需要第一时间发现问题&#xff0c;如服务器宕机&#xff0c;zabbix和Prometheus都需要等几分钟才会报警。 想到最原始…

故障排查:VMware虚拟机网络冲突,导致VPN网络无法正常访问

故障现象 某台windows10系统电脑&#xff0c;远程拨号SSL VPN后&#xff0c;无法正常公司内网。通过排查&#xff0c;发现重启开机&#xff0c;操作系统的默认路由多了一条公司内网的默认路由&#xff0c;但网关不正确。手动删除&#xff0c;重启系统又恢复原样。 排查过程 c…

adb的安装和使用 以及安装Frida 16.0.10+雷电模拟器

.NET兼职社区 .NET兼职社区 .NET兼职社区 1.下载adb Windows版本&#xff1a;https://dl.google.com/android/repository/platform-tools-latest-windows.zip 2.配置adb环境变量 按键windowsr打开运行&#xff0c;输入sysdm.cpl&#xff0c;回车。 高级》环境变量》系统变量》…

OpenCV结构分析与形状描述符(20)计算一个包围给定点集的最小外接圆函数minEnclosingCircle()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 找到一个包围二维点集的最小面积的圆。 该函数使用迭代算法来寻找一个二维点集的最小外接圆。这意味着函数将会通过反复逼近的过程来计算出能够…

多维时序 | Matlab基于BO-LSSVM贝叶斯优化最小二乘支持向量机数据多变量时间序列预测

多维时序 | Matlab基于BO-LSSVM贝叶斯优化最小二乘支持向量机数据多变量时间序列预测 目录 多维时序 | Matlab基于BO-LSSVM贝叶斯优化最小二乘支持向量机数据多变量时间序列预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于BO-LSSVM贝叶斯优化最小二乘支…

莎朗斯通的比基尼视频曝光了她的日常锻炼!自爆曾在重症监护室呆了9天

如果您错过了&#xff0c;莎朗斯通 (Sharon Stone) 的华丽比基尼视频向您展示了她的日常锻炼&#xff01; 9 月 12 日&#xff0c;斯通分享了一段她在泳池里锻炼的视频。她分享了这段视频&#xff0c;并配文&#xff1a;“我刚刚和教练 kristinemarie_18 完成了最后一次锻炼&a…

【Python刷题】Atcoder Beginner Contest 371

目录 A - Jiro题目描述算法思路代码实现 B - Taro题目描述算法思路代码实现 D - 1D Country题目描述算法思路代码实现 E - I Hate Sigma Problem题目描述算法思路代码实现 A - Jiro 题目描述 有三个人&#xff0c;知道他们之中每两个人的年龄关系&#xff0c;输出年龄第二大的…

Unity实现自己的协程系统

为什么自己实现一套协程系统 协程&#xff08;Coroutine&#xff09;是一个强大且灵活的工具&#xff0c;它可以帮助开发者处理异步任务&#xff0c;例如等待某些事件、处理逐帧更新等。在Unity中&#xff0c;协程通常通过IEnumerator来实现&#xff0c;这种机制允许代码在执行…

效率神器来了:AI工具手把手教你快速提升工作效能

随着科技的进步&#xff0c;AI工具已经成为提升工作效率的关键手段。本文将介绍一些实用的AI工具和方法&#xff0c;帮助你自动化繁琐的重复性任务、优化数据管理、促进团队协作与沟通&#xff0c;并提升决策质量。 背景&#xff1a;OOP AI-免费问答学习交流-GPT 自动化重复性任…

IP纯净度对跨境电商有哪些影响

在全球化贸易的浪潮中&#xff0c;跨境电商凭借其打破地理界限的能力&#xff0c;成为推动国际贸易的重要力量。然而&#xff0c;跨境电商的运营并非没有挑战&#xff0c;其中IP纯净度是影响其成功的关键因素之一。本文将探讨IP纯净度对跨境电商运营的多方面影响&#xff0c;并…

从单体到微服务:FastAPI ‘挂载’子应用程序的转变

在现代 Web 应用开发中&#xff0c;模块化架构是一种常见的设计模式&#xff0c;它有助于将大型应用程序分解为更小、更易于管理的部分。FastAPI&#xff0c;作为一个高性能的 Python Web 框架&#xff0c;提供了强大的支持来实现这种模块化设计。通过“挂载”子应用程序&#…

WebGL系列教程八(GLSL着色器基础语法)

目录 1 前言2 基本原则3 基本数据类型4 顶点着色器和片元着色器4.1 声明4.2 初始化项目4.3 赋值 5 结构体5.1 声明5.2 赋值 6 函数6.1 基本结构6.2 自定义函数6.3 常用内置函数 7 精度8 其他9 总结 1 前言 通过前七讲&#xff0c;我们已经见过了WebGL中的部分基础语法&#xff…

webpack5-手撸RemoveConsolePlugin插件

写在前面 其实呢&#xff0c;这个东西也就那样&#xff0c;主要是我们得清楚webpack构建过程中的生命周期钩子&#xff0c; 就拿这个插件来说&#xff0c;我们想要把输出的js文件里面的内容中的console语句去掉&#xff0c;那么我们就需要找到webpack处理完文件时的钩子&#…

海外VS国内:网安上市公司人均创收对比

二级市场分析章节中分析了中国网络网络安全上市公司人均创收63.2万、人均毛利37.6万&#xff0c;人均创利-1.6万。 有网友问了&#xff1a;海外网络安全公司的人均情况如何&#xff1f;那么让我们一起看看吧。 我们统计了在海外上市的28家主要网络安全公司的2023年的人均情况&…