【1++的Linux】之进程间通信

👍作者主页:进击的1++
🤩 专栏链接:【1++的Linux】

文章目录

  • 一,进程间通信的目的
  • 二,管道

一,进程间通信的目的

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

进程间通信的必要性:
若没有进程间通信,那么也就无法使用并发能力,无法实现进程间协同。传输数据,消息通知等。

进程是具有独立性的,虚拟地址空间和页表保证了其独立性,因此,进程间通信的成本是比较高的。
想要让两进程间能够通信,那么其必定要能够看到同一份 “内存” 。这份所谓的“内存”不能属于任何一个进程,它应该是共享的。

进程间通信的发展:

  1. 管道
  2. System V进程间通信
  3. POSIX进程间通信

管道:
匿名管道pipe
命名管道

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

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

二,管道

管道是Unix中最古老的进程间通信的形式。是Linux原生就能够提供的。其有一个入口,一个出口,是单向通信的,也可以说是一种特出的半双工通信。

管道的原理:
我们在上面提到,两进程之间能够进行通信,那么两进程之间就得都能看到同一份资源?那么怎么让两进程看到同一份资源呢?
在fork之后,创建出来的子进程会继承父进程的大多数内容,这其中就包括文件描述符表,那么文件对象会被拷贝给子进程吗?显然是不会的,这样做是没有意义的。
我们在创建子进程之前分别以读写方式打开同一个文件,子进程继承之后,其也能够这个文件的文件描述符,有了文件描述符,我们是不是就能够访问这个文件了!!!我们让父进程进行写,那么就关闭其读的那个文件描述符,让子进程读,那么就关闭其写的那个文件描述符。这样,父子进程间就能够就行通信了。这样通信方式我们叫做匿名管道。
管道的本质是一种文件。

下面我们来简单的实现一个匿名管道:

使用pipe系统调用来创建匿名管道。

#include<iostream>
#include<fcntl.h>
#include<unistd.h>
#include<cassert>
#include<cstring>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;int main()
{//创建匿名管道int pipefd[2];//0读--1写int n=pipe(pipefd);assert(n!=-1);cout<<"creat pipe success"<<endl;(void)n;//创建子进程pid_t pid=fork();assert(pid!=-1);if(pid==0)//子进程{//子进程负责读close(pipefd[1]);//关闭写端char buffer[1024];while(true){//sleep(3);ssize_t n=read(pipefd[0],buffer,sizeof(buffer)-1);assert(n!=-1);if(n>0){buffer[n]='\0';cout<<"child get a message["<<getpid()<<"]"<<"father#"<<buffer<<endl;}if(n==0){cout<<"write quite,me quite"<<endl;break;}}close(pipefd[0]);//可有可无exit(0);}//父进程写close(pipefd[0]);//关闭读const char* message="I am sending message";int count=0;while(true){ssize_t n=write(pipefd[1],message,strlen(message));sleep(1);count++;if(count==5) break;}//读写完成,退出close(pipefd[1]);pid_t ret= waitpid(pid,nullptr,0);assert(ret>0);return 0;return 0;
}

运行结果:

写慢读快时
在这里插入图片描述
我们发现写慢读快时,读端不会继续写,而是停下来等待写入。

当我们让写快,读慢时(即读时休眠时间长一些)
在这里插入图片描述
一次会将管道中的所有数据都读出来。管道的大小是有限制的,当管道被写满时,便不会再写,而是等待读。

当把写端关掉,读端进程会直接退出。

当把读端关掉,OS会关掉写进程。

因此管道可以让进程间协同,提供了访问控制。
管道提供的是面向流式的通信服务,其生命周期随进程。

从管道读数据是一次性操作,数据一旦被读,它就从管道中被抛弃,释放空间以便写更多的数据。

在这里插入图片描述
站在内核的角度,管道的本质就是两个进程对同一个文件对象,一个进行写入,一个进行读取。
看待管道和看待文件一样,使用也是一样的,这也符合:Linux下一切皆文件的思想。

一个父进程可以和一个子进程通信,那么一个父进程能否和多个子进程分别通信?—可以的!
代码如下:

#pragma once
#include<iostream>
#include<functional>
#include<vector>
#include<string>
#include<unordered_map>
#include<unistd.h>
#include <utility> 
#include<cassert>
typedef std::function<void()> func;
std::vector<func> callbacks;
std::unordered_map<int,std::string> desc;
void readSQL()
{std::cout << "sub process[" << getpid() << " ] 执行访问数据库的任务\n" << std::endl;
}void execule()
{std::cout << "sub process[" << getpid() << " ] 执行url解析\n" << std::endl;
}void cal()
{std::cout << "sub process[" << getpid() << " ] 执行加密任务\n" << std::endl;
}void save()
{std::cout << "sub process[" << getpid() << " ] 执行数据持久化任务\n" << std::endl;
}void lod()
{ desc.insert({callbacks.size(), "readSQL: 读取数据库"});callbacks.push_back(readSQL);desc.insert({callbacks.size(), "execule: 进行url解析"});callbacks.push_back(execule);desc.insert({callbacks.size(), "cal: 进行加密计算"});callbacks.push_back(cal);desc.insert({callbacks.size(), "save: 进行数据的文件保存"});callbacks.push_back(save);}void showHandler()
{for(auto& e:desc){std::cout<<e.first<<'\t'<<e.second<<std::endl;}
}int Handersize()
{return callbacks.size();
}
#include<unistd.h>
#include<cstring>
#include<sys/types.h>
#include<sys/wait.h>
#include"task.hpp"
#define PROCESS_NUM 5using namespace std;int waitcommand(int waitfd,bool& quite)
{int command=0;ssize_t n=read(waitfd,&command,sizeof(command));if(n==0){quite=true;return -1;}return command;}void SendCommand(int who,int fd,int command)
{ssize_t s=write(fd,&command,sizeof(command));std::cout<<"main process call"<<who<<"excule"<<desc[command]<<std::endl;
}int main()
{lod();//pid  :  fdstd::vector<std::pair<int,int>> slots;//多个子进程for(int i=0;i<PROCESS_NUM;i++){//创建管道int pipefd[2];int n=pipe(pipefd);assert(n!=-1);//创建子进程pid_t pid=fork();assert(pid!=-1);if(pid==0)//子进程{//子进程读close(pipefd[1]);while(true){//等命令bool quite=false;int command=waitcommand(pipefd[0],quite);//不写就等待if(command==-1){//std::cout<<"退出"<<std::endl;break;}else if(command>=0&&command<=Handersize()){callbacks[command]();}else{std::cout<<"非法输入"<<std::endl;}}//退出close(pipefd[0]);std::cout<<"write quite,me quite"<<std::endl;exit(1);}//父进程写close(pipefd[0]);slots.push_back(std::make_pair(pid,pipefd[1]));}//随机派发命令srand((unsigned)time(nullptr));while(true){int command=rand()%Handersize();//选择命令int choice=rand()%slots.size();//选择子进程//指派任务SendCommand(slots[choice].first,slots[choice].second,command);sleep(2);}//关闭所有写for(auto& e:slots){close(e.second);}//回收所有子进程for(auto& e:slots){waitpid(e.first,nullptr,0);}return 0;
}

在这里插入图片描述

命名管道:
命名管道与匿名管道的原理相同,都是通过让两个进程看到同一份资源,从而实现通信,但命名管道不再局限于父子进程之间,而是任意两个进程之间实现通信。
两进程看到相同的资源,是通过管道文件的路径从而实现的。
命名管道的本质也是一种文件,但不是普通的文件,普通的文件我们在读写时,会将内存数据刷新到磁盘中,但是我们的管道是不会的。因此其效率也是很高的。

管道文件的创建:

  1. mkfifo filename
  2. int mkfifo(const char *filename,mode_t mode);

下面是我们实现的命名管道的代码:

// 服务端接收消息

#include"comm.hpp"
#include"Log.hpp"static void getmessage(int fd)
{char buffer[1024];while(true){int n=read(fd,buffer,sizeof(buffer-1));assert(n!=-1);if(n>0){buffer[n]='\0';std::cout<<"["<<getpid()<<"]"<<"client say: "<<buffer<<std::endl;}else if(n==0){std::cout<<"["<<getpid()<<"]"<<"client quit,me quit"<<std::endl;break;}}
}int main()
{//创建管道int n=mkfifo(ipc_path.c_str(),0666);if(n<0){perror("mkfifo");exit(1);}log("管道创建成功",DEBUG)<<"step1"<<std::endl;//打开管道进行读int fd=open(ipc_path.c_str(),O_RDONLY);if(fd<0){perror("open");exit(2);}log("打开管道成功",DEBUG)<<"step2"<<std::endl;for(int i=0;i<Process_Num;i++){int pid=fork();assert(pid>=0);if(pid==0){getmessage(fd);exit(1);}}for(int i=0;i<Process_Num;i++){waitpid(-1,nullptr,0);std::cout<<"等待成功"<<std::endl;}close(fd);log("关闭管道成功",DEBUG)<<"step3"<<std::endl;unlink(ipc_path.c_str());log("删除管道成功",DEBUG)<<"step4"<<std::endl;return 0;
}

//客户端发送消息


#include<iostream>
#include"comm.hpp"
#include"Log.hpp"
#include<cstring>
int main()
{int fd=open(ipc_path.c_str(),O_WRONLY);if(fd<0){perror("open");exit(3);}log("client 打开管道成功",DEBUG)<<"step5"<<std::endl;std::string buffer;while(true){std::cout<<"client say:"<<std::endl;std::getline(std::cin,buffer);int n=write(fd,buffer.c_str(),buffer.size());}close(fd);return 0;}
#pragma once
#include<iostream>
#include"comm.hpp"
#ifndef _LOG_H_
#define _LOG_H_#define DEBUG 0
#define NOTICE 1
#define WARNING 2
#define ERROR 3std::string mes[4]={"DEBUG","NOTICE","WARNING","ERROR"
};std::ostream &log(std::string message,int level)
{std::cout<<"|"<<unsigned(time(nullptr))<<"|"<<mes[level]<<"|"<<message;return std::cout;
}#endif
#pragma once
#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<string>
#include<cassert>
#include<fcntl.h>
#include<unistd.h>
#include<sys/wait.h>
#define Process_Num 4std::string ipc_path="./fifo.ipc";

一个普通的全局的静态函数与普通函数的区别是:用static修饰的函数,限定在本源码文件中,不能被本源码文件以外的代码文件调用。

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

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

相关文章

深度学习:张量 介绍

张量[1]是向量和矩阵到 n 维的推广。了解它们如何相互作用是机器学习的基础。 简介 虽然张量看起来是复杂的对象&#xff0c;但它们可以理解为向量和矩阵的集合。理解向量和矩阵对于理解张量至关重要。 向量是元素的一维列表&#xff1a; 矩阵是向量的二维列表&#xff1a; 下标…

unity button移动位置some values driven by canvas

1 可以在button父节点把限制取消勾选 2 在不动整个布局的情况下&#xff0c;只修改局部变量&#xff1a;忽略布局即可

【C++】list的介绍及使用 | 模拟实现list(万字详解)

目录 一、list的介绍及使用 什么是list&#xff1f; list的基本操作 增删查改 获取list元素 不常见操作的使用说明 ​编辑 接合splice ​编辑 移除remove 去重unique 二、模拟实现list 大框架 构造函数 尾插push_back 迭代器__list_iterator list的迭代器要如何…

2023年MathorCup高校数学建模挑战赛大数据挑战赛赛题浅析

比赛时长为期7天的妈杯大数据挑战赛如期开赛&#xff0c;为了帮助大家更好的选题&#xff0c;首先给大家带来赛题浅析&#xff0c;为了方便大家更好的选题。 赛道 A&#xff1a;基于计算机视觉的坑洼道路检测和识别 A题&#xff0c;图像处理类题目。这种题目的难度数模独一档…

TextureView和SurfaceView

1、Surface Surface对应了一块屏幕的缓冲区&#xff0c;每一个window对应一个Surface&#xff0c;任何View都是画在Surface上的&#xff0c;传统的View共享一块屏幕缓冲区&#xff0c;所有的绘制都必须在UI线程上进行。 2、SurfaceView 顾名思义就是Surface的View&#xff0c;…

Python爬虫网易云音乐,Tkinter制作音乐播放器

目录 一、效果展示 二、环境 三、实现过程 四、源码 一、效果展示 页面的美化以及功能还有待升级~ 先来说一下已有功能吧&#xff1a; 可以在搜索框中通过歌曲或歌手名称进行搜索&#xff0c;效果和在网易云官网搜索一样。 点击开始下载&#xff0c;就会将搜索结果的第一…

FoLR:Focus on Local Regions for Query-based Object Detection论文学习笔记

论文地址&#xff1a;https://arxiv.org/abs/2310.06470 自从DETR问询式检测器首次亮相以来&#xff0c;基于查询的方法在目标检测中引起了广泛关注。然而&#xff0c;这些方法面临着收敛速度慢和性能亚优等挑战。值得注意的是&#xff0c;在目标检测中&#xff0c;自注意力机制…

H5游戏源码分享-手机捉鬼游戏

H5游戏源码分享-手机捉鬼游戏 一款考验手速的游戏 <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><title>手机捉鬼 微信HTML5在线朋友圈游戏</title><meta name&…

SSM度假村管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 度假村管理系统是一套完善的信息系统&#xff0c;结合SSM框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要 采用B/S模式开发…

计算机毕设 opencv 图像识别 指纹识别 - python

文章目录 0 前言1 课题背景2 效果展示3 具体实现3.1 图像对比过滤3.2 图像二值化3.3 图像侵蚀细化3.4 图像增强3.5 特征点检测 4 OpenCV5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往…

VulnHub DC-1

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

自动驾驶,从“宠儿”走进“淘汰赛”

从“一步到位”到场景、技术降维。从拼落地路径&#xff0c;到拼雷达、算力&#xff0c;再到如今的性价比之争&#xff0c;自动驾驶似乎变得愈发“接地气”。 作者|斗斗 编辑|皮爷 出品|产业家 比起去年&#xff0c;黄文欢和张放今年显得更加忙碌。 “自动驾驶赛道&…

“爱知道”,你知道吗?

拥抱时代浪潮&#xff0c;加速科技变革。数字经济时代&#xff0c;杭州重点贯彻市委市政府数字经济创新提质“一号发展工程”&#xff0c;加快发展数字经济&#xff0c;推动全市数字经济往高攀升、向新进军、以融提效。基于政府对数字经济新活力的赋能、优化数字社会环节、构建…

若依微服务上传图片文件代理配置

在使用若依微服务文件上传时候,文件上传成功会上传到D:/ruoyi/uploadPath目录下。默认使用9300端口进行访问图片文件,现在我想把它代理到80端口应该怎么做呢? 配置前:http://localhost:9300/statics/2023/09/24/test.jpg 配置后:http://localhost/statics/2023/09/24/test…

msigdbr hallmarks gsea broad研究所

使用msigdbr r包 #BiocManager::install("msigdb") #https://www.gsea-msigdb.org/gsea/msigdb #https://cran.r-project.org/web/packages/msigdbr/vignettes/msigdbr-intro.html #https://bioconductor.org/packages/release/data/experiment/vignettes/msigdb/ins…

LVS-DR模式+keepalived+nginx+tomcat实现动静分离、负载均衡、高可用实验

实验条件&#xff1a; test2——20.0.0.20——主服务器——ipvsadm、keepalived服务 test3——20.0.0.30——备服务器——ipvsadm、keepalived服务 nginx5——20.0.0.51——后端真实服务器1&#xff08;tomcat的代理服务器&#xff09;——nginx服务 nginx6——20.0.0.61—…

【笔录】TVP技术沙龙:寻宝AI时代

目录 引言大模型的应用案例大模型三问模型落地可行性考量维度AIGC的几个可行应用方向AIGC的存储LLM工程应用范式演进LLM编程协作范式变化 引言 大模型是10倍的机会&#xff0c;但并不是平均主义的机会&#xff0c;没有低垂的果实。 企业想在大模型的赛道上跑出成绩&#xff0c;…

Python第三方库 - Flask(python web框架)

1 Flask 1.1 认识Flask Web Application Framework&#xff08; Web 应用程序框架&#xff09;或简单的 Web Framework&#xff08; Web 框架&#xff09;表示一个库和模块的集合&#xff0c;使 Web 应用程序开发人员能够编写应用程序&#xff0c;而不必担心协议&#xff0c;线…

Go语言标准输入

文章目录 Go语言标准输入函数使用 Go语言标准输入 函数 Scan // 使用stdin读取内容&#xff0c;读取的内容以空白&#xff08;换行也属于空白&#xff09;分隔&#xff0c;赋值给函数参数。返回读取的个数和错误 func Scan(a ...interface{}) (n int, err error)Scanf // 和…

基于Qt实现的轻量级CAD画图软件

1.界面展示 2.功能展示 3.关于Graphics View绘图架构 由于QPainter画图形&#xff0c;不能实现对图形的选择、编辑、移动等操作&#xff0c;所以我的整个项目是基于Craphics View绘图架构来实现的 3.1 Craphics View &#xff08;1&#xff09;Craphics View绘图架构介绍 …