Linux-进程间通信(进程间通信介绍、匿名管道原理及代码使用、命名管道原理及代码使用)

一、进程通信介绍

1.1进程间通信的目的
  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事( 如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变。
1.2 进程间如何通信

进程=内核数据结构+代码和数据

每个进程都拥有自己独立的PCB与数据,由于进程与进程间是相互独立的,互相看不到也不想看到对方的数据,所以进程间通信的前提是让不同进程看到同一份资源。进程间通信一定是某个进程需要通信,让操作系统创建一个共享资源,而用户不能直接操控操作系统,所以操作系统需要提供许多的系统调用,系统调用不同就会创建出不同的共享资源,那么进程间的通信方式也会不同

1.3进程间通信的方式

管道

  • 匿名管道pipe
  • 命名管道

System V IPC

  • System V 消息队列
  • System V 共享内存
  • System V 信号量

POSIX IPC

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

二、匿名管道

2.1匿名管道原理

当一个进程以读和写的方式分别打开一个文件,由于打开方式的不同,就会创建两个struct file,每个文件都有自己的属性、数据、更重要的是存在内核级的文件缓冲区,两个struct file除了打开方式字段不同,其余信息都是相同的,所以操作系统不会将文件的信息在拷贝一份,两个struct file指向的文件信息是一份。当该进程创建子进程,子进程会继承父进程的文件描述符表,此时父子进程可以对同一个文件进行读取操作,该文件中的缓冲区就相当于一个共享资源,这个缓冲区也成为道,两个进程间就可以进行通信了

当我们想让父进程写入数据,子进程读取数据时,我们只需要关闭父进程的读文件,关闭子进程的写文件即可,这样就可以做到子进程向管道中写数据,父进程向文件中读数据了

【注意】:

  1. 管道只能进行通单向信,及只能一个进程进行读一个进程写,不能让两个进程既可以读也可以写,所以我们需要关闭对应不需要功能的文件描述符(不关闭也是可以的,但是建议关掉,防止误写误读)
  2. 写端写入的数据会保存在内核级文件缓冲区中,直到被读走

2.2 pipe接口(创建管道)
#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码

int fd[ ]为输出型参数,会带回以读和写方式打开的文件的fd,由于我们知道文件的fd不知道文件的名字,所以称为匿名管道

2.3 代码理解(父进程读,子进程写)
#include<iostream>
#include<unistd.h>
#include<cerrno>
#include<cstring>
#include <sys/types.h>
#include<string>
#include<sys/wait.h>
#include <sys/types.h>
#define SIZE 1024void chilewrite(pid_t wfd)
{while(1){std::string message="father I am your child process!";write(wfd,message.c_str(),message.size());sleep(2);}
}void fathread(pid_t rfd)
{char buffer[SIZE];while(1){int n=read(rfd,buffer,sizeof(buffer)-1);if(n>0){buffer[n]='\0';std::cout<<buffer<<std::endl;}else if(n==0){// 如果read的返回值是0,表示写端直接关闭了,我们读到了文件的结尾std::cout<<"写端关闭了"<<std::endl;break;}else{std::cerr<<"read err"<<std::endl;break;}}
}int main()
{//创建管道int pipefd[2];int n=pipe(pipefd);if(n<0){std::cerr<<"errno:"<<errno<<" errstring:"<<strerror(errno)<<std::endl;return 1;}//创建子进程pid_t id=fork();if(id==0){//子进程std::cout<<"子进程准备就绪,准备写入数据了"<<std::endl;close(pipefd[0]);chilewrite(pipefd[1]);close(pipefd[1]);exit(0);}//父进程std::cout<<"父进程准备就绪,准备读取数据了"<<std::endl;close(pipefd[1]);fathread(pipefd[0]);close(pipefd[0]);//进程等待int status=0;pid_t rid=waitpid(id,&status,0);if(rid>0){std::cout << "wait child process done, exit sig: " << (status&0x7f) << std::endl;std::cout << "wait child process done, exit code(ign): " << ((status>>8)&0xFF) << std::endl;}return 0;
}
2.4 管道的四种情况
  1. 如果管道内部是空的并且写端没有关闭,此时读取条件不具备,读端会被阻塞,等待写入数据
  2. 如果管道内部被写满了并且读端没有关闭,此时写入条件不具备,写端会被阻塞,等待读端读取数据
  3. 如果管道一直在读,但是已经关闭了写端,此时读端read返回值会一直读到0,表示读到了文件末尾
  4. 如果管道一直在写,但是已经关闭了读端,此时OS会直接使用13号信号杀死进程,代表进程异常
2.5 管道的五种特征
  1. 匿名管道只能进行有血缘关系的进程之间进行通信,因为匿名管道是依靠子进程继承父进程的文件描述符表实现的(通常用于实现父子进程之间的通信)
  2. 管道内部自带进程的同步机制,多执行流执行代码时具有明显的顺序性,写入管道的数据直到被读取之前会保持在管道缓冲区中(如果缓冲区未满),而读取操作则会等待直到有数据可读,这种机制避免了同时读写导致的数据损坏问题
  3. 管道文件的生命周期是随进程的,当所有打开该文件的进程都退出后,该文件资源也会被释放
  4. 管道文件在进行通信时是面向字节流的,读与写的次数不是一 一匹配的,数据没有明确的分割,一次拿多少数据都行
  5. 管道通信是一种特殊的半双工模式。半双工通信允许数据在两个方向上传输,但不能同时进行。这意味着在任何时候,数据只能在一个方向流动。一旦一方开始发送数据,另一方必须等待接收完毕后才能开始发送。全双工通信允许数据同时在两个方向上进行传输,无需等待。由于半双工模式是可以双向传输数据的,但是管道只能单向通信,所以是特殊的半双工模式
2.6 管道的读写规则
  • 当没有数据可读时

        O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止

        O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。

  • 当管道满的时候

        O_NONBLOCK disable: write调用阻塞,直到有进程读走数据

        O_NONBLOCK enable:调用返回-1,errno值为EAGAIN 如果所有管道写端对应的文件描述符被关闭,则read返回0

  • 如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程 退出
  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

PIPE_BUF一般至少为512个字节,在Linux下,PIPE_BUF为4096个字节,当写入的的数据量不大于PIPE_BUF时,写入操作是安全的,不会发生写一半数据就被读走的情况,如果写入的数据量大于PIPE_BUF,则可能会发写入的数据提前被读走一部分

三、命名管道

匿名管道只能实现具有血缘关系进程之间的通信,而命名管道可以实现两个毫无相关的进程之间的通信,下面先介绍一下命名管道的原理

3.1命名管道原理

        当一个进程以读的方式打开一个文件,该进程会有自己的进程PCB和文件描述符表,同时会创建一个struct file

        当另一个进程以写的方式也打开这个文件,该进程也会有自己的进程PCB、文件描述符表,并且也会创建一个struct file,但是由于这两个进程打开的文件是一样的,而struct file也就是打开文件的方式不同,所以两个struct file都是指向的同一份文件信息,也指向了同一个内核级文件缓冲区,那么这两个毫无关系的进程就指向了同一段空间,就可以进行通信了。

        怎么确保两个进程打开的是同一个文件呢?---------文件的路径

3.2 mkfifo接口
  • 命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
mkfifo filename

  • 命名管道也可以从程序里创建,相关函数有:
int mkfifo(const char *filename,mode_t mode);#参数:filename为文件名 mode为创建文件的权限
#头文件:#include <sys/types.h>#include <sys/stat.h>#返回值:成功返回0,失败返回-1,并且设置错误码
3.2 代码使用举例

namedpipe.hpp:

#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <cerrno>
#include <unistd.h>
#include <iostream>
#include <fcntl.h>#define Defaultfd -1
#define Creater 1
#define User 2
#define SIZE 128
#define Read O_RDONLY
#define Write O_WRONLYconst std::string path = "./myfifo";
class NamedPipe
{
private:bool OpenNamePipe(int mode){_fd = open(_fifo_path.c_str(), mode);if (_fd < 0)return false;return true;}public:NamedPipe(const std::string path, int who): _fifo_path(path), _id(who), _fd(Defaultfd){if (_id == Creater){int ret = mkfifo(_fifo_path.c_str(), 0666);if (ret != 0){std::perror("mkfifo");}std::cout << "Creater creat namedpipe!" << std::endl;}}bool OpenforRead(){return OpenNamePipe(Read);}bool OpenforWrite(){return OpenNamePipe(Write);}int ReadNamedPipe(std::string *out){char buffer[SIZE];int n = read(_fd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = '\0';*out = buffer;}return n;}int WriteNamedPipe(std::string& in){return write(_fd,in.c_str(),in.size());}~NamedPipe(){if (_id == Creater){int ret = unlink(_fifo_path.c_str());if (ret != 0){std::perror("unlink");}std::cout << "Creater free namedpipe!" << std::endl;}}
private:std::string _fifo_path;int _id;int _fd;
};

client.cpp:

#include "namedpipe.hpp"
int main()
{// 以使用者的身份打开NamedPipe fifo(path, User);if (fifo.OpenforWrite()){std::cout << "Client open fifopipe for write!" << std::endl;while (true){std::cout << "Please enter message" << std::endl;std::string message;std::getline(std::cin, message);fifo.WriteNamedPipe(message);}}return 0;
}

server.cpp:

#include "namedpipe.hpp"
int main()
{// 以创建者的身份打开NamedPipe fifo(path, Creater);if (fifo.OpenforRead()){std::cout << "Server open fifopipe for read!" << std::endl;while (true){std::string message;int n = fifo.ReadNamedPipe(&message);if (n > 0){std::cout << "Client:" << message << std::endl;}else if (n == 0){std::cout << "写端关闭了,读端也要关闭!" << std::endl;break;}else if (n < 0){std::perror("read"); break;}}}return 0;
}

结果展示:

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

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

相关文章

day15 学一下Tailwindcss(java转ts全栈/3r教室)

目前距离全栈差得最多的是前端&#xff0c;而对于前端主要是CSS一直不熟悉&#xff0c;觉得很复杂写起来总是不上道&#xff0c;所以特别关注下Tailwindcss吧&#xff0c;其他前端框架可以先放放&#xff0c;多说无益直接用tailwindcss做个页面试试 看下文档&#xff1a;Tailwi…

final、finally、finalize有什么区别?

引言 在Java编程语言中&#xff0c;final、finally和finalize是三个具有不同用途和语义的关键字或方法。它们在编程和面试中经常被提及&#xff0c;因此理解它们之间的区别是非常重要的。 题目 final、finally、 finalize有什么区别&#xff1f; 典型回答 final&#xff1…

第三方软件测试机构的优势

软件测试机构在软件开发和验收过程中扮演着至关重要的角色&#xff0c;其优势主要体现在以下几个方面&#xff1a; 专业性&#xff1a;软件测试机构通常拥有专业的测试团队&#xff0c;这些团队成员具备丰富的测试经验和深厚的专业知识&#xff0c;能够准确识别软件中的潜在问…

LLM大语言模型原理、发展历程、训练方法、应用场景和未来趋势

LLM&#xff0c;全称Large Language Model&#xff0c;即大型语言模型。LLM是一种强大的人工智能算法&#xff0c;它通过训练大量文本数据&#xff0c;学习语言的语法、语义和上下文信息&#xff0c;从而能够对自然语言文本进行建模。这种模型在自然语言处理&#xff08;NLP&am…

Django-admin单例模式和懒加载

Django-admin单例模式和懒加载 单例模式 class Foo:def __init__(self):self.name "张三"def __new__(cls, *args, **kwargs):empty_object super().__new__(cls)return empty_objectobj1 Foo() obj2 Foo()当我们实例化对象时&#xff0c;就会在内存开一个空间…

基于ZYNQ7020的ARM+FPGA模块化仪器

模块化仪器平台基于 FPGA控制器&#xff0c; 搭配丰富灵活的仪器模块&#xff0c;如万⽤表、⽰波器、信 号发⽣器、数据记录仪、⾳频分析仪等&#xff0c;涵盖了⾼精度信号、⾼速与射频信号测试测量与处理&#xff0c;提供了从验证到试产到量产的全过程测试测量技术与解决⽅案&…

Python来计算 1,2,3,4 能组成多少个不相同且不重复的三位数?

我们今天的例子是 有 1&#xff0c;2&#xff0c;3&#xff0c;4 四个数字&#xff0c;它们能组成多省个互不相同且无重复的三位数&#xff1f;都分别是多少&#xff1f; 话不多说&#xff0c;我们先上代码 num 0 # 我们写了三个for循环&#xff0c;表示生成的三位数 for i…

深度学习中的变形金刚——transformer

很荣幸能和这些大牛共处一个时代。网络结构名字可以是一个卡通形象——变形金刚&#xff0c;论文名字可以来源于一首歌——披头士乐队的歌曲《All You Need Is Love》。 transformer在NeurIPS2017诞生&#xff0c;用于英语-德语&#xff0c;英语-法语的翻译&#xff0c;在BLEU…

可以在手机端运行的大模型标杆:微软发布第三代Phi-3系列模型,评测结果超过同等参数规模水平,包含三个版本,最小38亿,最高140亿参数

本文原文来自DataLearnerAI官方网站&#xff1a; 可以在手机端运行的大模型标杆&#xff1a;微软发布第三代Phi-3系列模型&#xff0c;评测结果超过同等参数规模水平&#xff0c;包含三个版本&#xff0c;最小38亿&#xff0c;最高140亿参数 | 数据学习者官方网站(Datalearner…

消除模型“焦虑”,浪潮信息切中AI生态建设的“症结”

大模型的崛起&#xff0c;真正开启人工智能重塑千行百业的序幕。 此绝非虚言。今年初&#xff0c;《政府工作报告》明确提出深化大数据、人工智能等研发应用&#xff0c;开展“人工智能”行动。这标志着以大模型为代表的新一代人工智能技术将加速进入到垂直行业。 但“人工智…

[C++][算法基础]整数划分(统计动态规划)

一个正整数 &#x1d45b; 可以表示成若干个正整数之和&#xff0c;形如&#xff1a;&#x1d45b;&#x1d45b;1&#x1d45b;2…&#x1d45b;&#x1d458;&#xff0c;其中 &#x1d45b;1≥&#x1d45b;2≥…≥&#x1d45b;&#x1d458;,&#x1d458;≥1。 我们将这…

Python_GUI工具包 Pyside6的简介与基础操作

Python_GUI工具包 Pyside6的简介与基础操作 本文默认读者具备以下技能&#xff1a; 熟悉python基础知识&#xff0c;vscode或其它编辑工具 具备自主扩展学习能力 一、Pyside6简介 首先需要在这里先说明一下,我之前写的文章大多是ai相关的内容&#xff0c;此时在这里引入Pyt…

【练习1】

1.字符串最后一个单词的长度 #include <iostream> #include<string> using namespace std;int main() {string a;int res,i,flag;flag1;i0;getline(cin,a);res0;while(flag1){if(a[i]! ){resres1;}else{res0;}if(ia.length()-1){flag-1;}i;}cout<<res<<…

RakSmart站群服务器租用注意事项科普

随着互联网的飞速发展&#xff0c;站群运营成为越来越多企业和个人的选择。而RakSmart作为知名的服务器提供商&#xff0c;其站群服务器租用服务备受关注。在租用RakSmart站群服务器时&#xff0c;源库建议有一些关键的注意事项需要特别留意&#xff0c;以确保服务器的稳定运行…

Blender面操作

1.细分Subdivide -选择一个面 -右键&#xff0c;细分 -微调&#xff0c;设置切割次数 2.删除 -选择一个或多个面&#xff0c;按X键 -选择要删除的是面&#xff0c;线还是点 3.挤出面Extrude -选择一个面 -Extrude工具 -拖拽手柄&#xff0c;向外挤出 -微调&#xff…

【酱浦菌-爬虫项目】爬取百度文库文档

1. 首先&#xff0c;定义了一个变量url&#xff0c;指向百度文库的搜索接口 ‘https://wenku.baidu.com/gsearch/rec/pcviewdocrec’。 2. 然后&#xff0c;设置了请求参数data&#xff0c;包括文档ID&#xff08;docId&#xff09;和查询关键词&#xff08;query&#xff09;。…

docker各目录含义

目录含义builder构建docker镜像的工具或过程buildkit用于构建和打包容器镜像&#xff0c;官方构建引擎&#xff0c;支持多阶段构建、缓存管理、并行化构建和多平台构建等功能containerd负责容器生命周期管理&#xff0c;能起、停、重启&#xff0c;确保容器运行。负责镜管理&am…

Postgresql的安装教程dbever的连接pgAdmin4的连接

最近在学习Postgresql. 首先&#xff0c;我去官网上下载了Community DL Page12.18这个版本&#xff0c;低版本比较稳定而且文档比较多 https://www.cnblogs.com/xy-ouyang/p/12009503.html 接下来&#xff0c;我去上面的链接参考了连接。打开了postgresql的服务器之后&#x…

《HCIP-openEuler实验指导手册》1.6 Apache静态资源配置(目录访问)

知识点 常用用途&#xff1a; 软件仓库镜像及提供下载服务&#xff1a; 配置步骤 删除网站主目录中的文件&#xff08;本实验机目录为/home/source ip为192.168.12.137 端口为81&#xff09; cd /home/source rm -rf *在主目录中新建6个文件夹如下图 mkdir test{1..6}新建…

Midjourney之绘画背景的选择

hello 小伙伴们&#xff0c;我是你们的老朋友——树下&#xff0c;今天分享Midjourney提示词中绘画背景的选择&#xff0c;话不多说&#xff0c;直接开始~ 对于背景的选择&#xff0c;Midjourney中主要体现在年代和所处的环境对绘画产生不同的影响 科技的发展&#xff0c;我们…