Linux:进程的通信

目录

进程间的通信

管道

1.概念

2.匿名管道

3.命名管道

4.匿名管道与命名管道的区别

5.总结管道的特点

共享内存

1.原理

2.共享内存的建立

3.代码

1.相关函数

2.总结


进程间的通信

1.进程间通信目的

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

2.进程间通信发展

  • 管道
  • System V进程间通信
  • POSIX进程间通信

3.进程间通信分类

  • 管道: 匿名管道pipe , 命名管道
  • System V IPC:System V 消息队列,  System V 共享内存 , System V 信号量
  • POSIX IPC:消息队列  共享内存  信号量  互斥量  条件变量  读写锁

4.进程通信的本质理解

  • 进程间通信的前提: 首先让不同进程看到同一块"内存"
  • 看到的同一块"内存" 属于那一个进程? --> 不能隶属于任何一个进程,而应该强调共享

管道

1.概念

我们把从一个进程连接到另一个进程的一个数据流称为一个“管道"

管道都是单向通信, 传输内容的 管道中传输的都是"资源",管道通信的背后是进程通过管道通信,管道就是文件

2.匿名管道

1.概念:匿名管道是内存级文件, 没有名字 , 不会写入磁盘

如何做到让不同进程看到同一份"资源"?子进程继承

-- fork让子进程继承 --- 能够让具有血缘关系的进程进行进程间通信 ---常用于父子进程

2.代码编写

#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]); //fd[2]:输出型参数,期望通过调用它, 得到被打开的文件fd
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码匿名管道底层也是文件,但是没有名字, 默认以读写方式打开pipefd[0]: 读端 pipefd[1]: 写端

a.从键盘读取数据,写入管道,读取管道,写道屏幕

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>int main()
{int fds[2];char buff[100];int len;if(pipe(fds) == -1)perror("创建管道失败"),exit(-1);//从stdin读取数据到buff,并写入管道fd[1]while(fgets(buff,100,stdin)){len = strlen(buff);if(write(fds[1],buff,len) != len){perror("写入失败");break;}memset(buff,0,sizeof(buff));//从管道里读取数据,并写入到stdoutif(len = read(fds[0],buff,100) == -1){perror("读取失败");break;}if(write(1,buff,len) != len){perror("写入stdout失败");break;}}return 0;
}

b.父进程写数据,子进程读数据

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<assert.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;int main()
{//创建管道int pipefd[2] = {0};int n = pipe(pipefd);assert(n != -1);(void)n;//创建子进程pid_t id = fork();assert(id != -1);if(id == 0){//子进程 - 读close(pipefd[1]);//将数据读取到buff里,并打印char buff[1024];while(true){ssize_t s = read(pipefd[0],buff,sizeof(buff)-1);if(s > 0){cout<<"Father:"<<buff<<endl;}}exit(0);}//父进程 - 写close(pipefd[0]);string message = "我是父进程,我正在发送消息";int count = 0;char send_buff[1024];while(true){//往send_buff里面写要发送的信息snprintf(send_buff,sizeof(send_buff),"%s[%d]:%d",message.c_str(),getpid(),count++);//写入管道write(pipefd[1],send_buff,strlen(send_buff));sleep(1);}pid_t ret = waitpid(id,nullptr,0);assert(ret > 0);(void)ret;return 0;
}

c.进程池

1.主进程派发任务,子进程接受任务(匿名管道实现)

2.使用循环创建多个子进程与管道,父进程保存其pipefd与pid (这里将其保存在vector中,构造pair)

3.Task.hpp:封装一个funtional包装器,将任务的执行方法放入vector里, 在创建一张hash表记录

4.子进程通pipefd[0],将要执行的任务编号读取,然后执行

   父进程通过pipefd[1],将要执行的任务编号写入给子进程读取

5.关闭文件描述符,以及回收子进程: 根据vector里记录的pid与pipefd,回收

代码:Linux-test: Linux下,提交代码 - Gitee.com

3.命名管道

1.概念:命名管道是磁盘文件(本质也是内存文件, 不过在磁盘上构建了文件名)

--管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。

--如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。该方式为:打开目录下同一文件

--命名管道是一种特殊类型的文件

2.代码编写

命令行创建命名管道

mkfifo filename  

程序中创建

接口: int mkfifo(const char* pathname , mode_t  mdoe)
头文件: <sys/types.h>   <sys/stat.h> 
返回值: 创建成功: 0  创建失败: -1//示例
int main(int argc, char *argv[])
{mkfifo("p2", 0644);return 0;
}

a.创建命名管道,进程A写数据,进程B读取数据

mkfifo name_pipe
//A
echo "hello world" > name_pipe
//B
cat < name_pipe删除管道文件: unlink 文件名(name_pipe)   也可以 rm  文件名

b.用命名管道实现server&client通信

server:创建管道文件, 打开管道文件, 从管道文件中读取信息到buff,并打印, 关闭文件,

client: 打开管道文件, 将buff的内容写入管道文件, 关闭文件

其中server端也可以创建多个子进程去读取管道文件里的信息

代码:Linux-test: Linux下,提交代码 - Gitee.com

4.匿名管道与命名管道的区别

1.它们让不同进程看到同一份资源的手段不一样,但它们的本质是一样的(都是文件)

命名管道可以让不同进程进行通信 ~~> 打开同一目录下相同的文件

匿名管道只能让父子进程进行通信 ~~> 子进程继承的方式

2.为什么匿名管道叫匿名?

--匿名管道 ~~> 内存级文件 ~~>在磁盘上没有对应的映像

--命名管道 ~~> 内存级文件 ~~>但在磁盘上给其构建了文件名

5.总结管道的特点

a.管道是用来进行具有血缘关系的进程进行进程间的通信 --- 常用于父子通信

b.管道具有通过让进程协同 , 提供了访问控制!

c.管道提供的是面向流式的通信服务 --- 面向字节流 --- 协议

d.管道是基于文件的,文件的生命周期是随进程的-->管道的生命周期是随进程的(如果通信双方退出,管道会释放)

e.管道是单向通信的 , 就是半双工通信的一种特殊情况

访问控制:

//写入的一方,fd没有关闭,如果有数据,就读,没有数据就等

//写入的一方,fd关闭,读取的一-方,read会返回0,表示读到了文件的结尾!

--写快,读慢,写满不能在写了

--写慢,读快,管道没有数据的时候,读必须等待

--写关,读0,标识读到了文件结尾

--读关,写继续写,OS终止写进程

共享内存

1.原理

2.共享内存的建立

共享内存的提供者: 操作系统

操作系统管理共享内存~~>先描述在组织 ~~> 共享内存 = 共享内存块 + 对应的内核数据结构

--创建共享内存 ~~>将该内存与地址空间建立映射 ~~> 进行通信 ~~> 去掉关联 ~~> 删除共享内存

3.代码

1.相关函数

ftok函数

功能:用于创建共享内存、消息队列和信号量等 IPC 对象的键值
原型key_t ftok(const char *pathname, int proj_id);
参数pathname:路径名 proj_id:项目标识符
返回值:成功返回一个键值,失败返回-1//将 pathname 所指向文件的索引节点号(inode number)与 proj_id 结合起来,
//生成一个唯一的键值,这个键值可以用作创建或获取 IPC 对象的标识符。

shmget函数

功能:用来创建共享内存
原型int shmget(key_t key, size_t size, int shmflg);
参数key:这个共享内存段名字size:共享内存大小shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

shmat函数

功能:将共享内存段连接到进程地址空间
原型void *shmat(int shmid, const void *shmaddr, int shmflg);
参数shmid: 共享内存标识shmaddr:指定连接的地址shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。
公式:shmaddr -(shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

shmdt函数

功能:将共享内存段与当前进程脱离
原型int shmdt(const void *shmaddr);
参数shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

shmctl函数

功能:用于控制共享内存
原型int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数shmid:由shmget返回的共享内存标识码cmd:将要采取的动作(有三个可取值)buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

当进程运行结束, 我们的共享内存还存在, IPC资源的生命周期随内核

命令行删除:

查看共享内存: ipcs -m
删除共享内存: ipcrm -m shmid (手动)

a.示例1

// 1. 创建公共的Key值
key_t k = ftok(PATH_NAME, PROJ_ID); // const char* path_name, int id
assert(k != -1);                    // key创建失败// 2. 创建共享内存 -- 建议要创建一个全新的共享内存 -- 通信的发起者
int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666); // 最后可以给0
if (shmid == -1)
{perror("shmget"); exit(1);
}
//IPC_CREAT: 如果创建共享内存,底层已经存在(直接获取并返回),如果不存在(创建后再返回)
//IPC_CREAT | IPC_EXCL :底层已经存在(出错返回),如果不存在(创建后再返回)// 3.将指定的共享内存,挂接到自己的地址空间
char *shmaddr = (char *)shmat(shmid, nullptr, 0); // 类似于malloc// 进行通信
//...// 4. 将指定的共享内存,从自己的地址空间中去关联
int n = shmdt(shmaddr);
assert(n != -1);
// 5.删除共享内存,IPC_RMID即便是有进程和当下的shm挂接,依旧删除共享内存
n = shmctl(shmid, IPC_RMID, nullptr);
assert(n != -1);

b.用共享内存实现server&client通信

server:获取key,创建共享内存,挂上链接,进行通信(读取数据), 去除链接,删除共享内存

client:获取key,获取共享内存,挂上链接,进行通信(写数据),去除链接

由于共享内存没有访问控制, 可以利用管道具有访问控制的特性(实现其访问控制)

代码:Linux-test: Linux下,提交代码 - Gitee.com

结果:

2.总结

1.共享内存是所有进程通信(IPC)速度最快的,不需要过多的拷贝(不需要将数据给OS):

只要通信双方使用shm,一方直接向共享内存中写入数据,另一方,立马就可以看到对方写入的数据

(共享内存一旦映射进各进程的地址空间,双方进程如果想要通信,直接进行内存级的读写即可)

2.共享内存缺乏访问控制 ! ~~> 会带来并发问题 (但我们可以用管道来增加访问控制)

3.为什么之前的pipe,fifo都要通过read,write来进行通信?

使用管道让双方看到的公共资源属于文件,需要操作系统自己去维护(文件是属于内核中的一种数据结构)

管道

共享内存

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

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

相关文章

软件测试|Beautiful Soup库详细使用指南

简介 Beautiful Soup是一款强大的Python库&#xff0c;广泛用于解析HTML和XML文档&#xff0c;从中提取数据并进行处理。它的灵活性和易用性使得数据抽取变得简单&#xff0c;本文将详细介绍Beautiful Soup库的基本用法和示例。 安装Beautiful Soup 首先&#xff0c;需要确保…

JavaEE学习笔记 2024-1-12 --Tomcat服务器、Servlet

JavaEE JavaEE是企业级开发 是综合性非常强的阶段  包含的知识点:JavaSE,MySQL,JDBC,WEB(HTML,CSS,JS,前端框架),Servlet,JSP,XML,AJAX等技术 1.服务器 JavaEE项目需要运行在服务器之上 WEB服务器就是WEB项目的容器,WEB项目的运行环境,将项目部署到服务器下,可以通过服务器…

Java面试之并发篇(二)

1、前言 本篇主要基于Java面试题之并发篇&#xff08;一&#xff09;继续梳理java中关于并发相关的高频面试题。本篇的面试题基于网络整理&#xff0c;和自己编辑。在不断的完善补充哦。 2、synchronized 的原理是什么? synchronized是 Java 内置的关键字&#xff0c;它提供…

软件测试|selenium 元素无法选择异常的原因及解决

简介 在进行 Web 自动化测试时&#xff0c;使用 Selenium 可能会遇到各种异常情况。其中之一就是 ElementNotSelectableException 异常&#xff0c;该异常通常意味着在尝试选择一个不可选元素时出现了问题。本文将详细介绍这个异常的原因、可能的解决方法&#xff0c;并提供示…

韩国LG集团在 CES2024 消费电子展上发布的的无线透明OLED屏幕

国际消费类电子产品展览会&#xff08;International Consumer Electronics Show&#xff0c;简称CES&#xff09;&#xff0c;由美国电子消费品制造商协会&#xff08;简称CTA&#xff09;主办&#xff0c;旨在促进尖端电子技术和现代生活的紧密结合。该展始于1967年&#xff…

Oracle 19c OCP 1z0 082考场真题解析第32题

考试科目&#xff1a;1Z0-083 考试题量&#xff1a;85 通过分数&#xff1a;57% 考试时间&#xff1a;150min本文为云贝教育郭一军&#xff08;微信&#xff1a;guoyJoe&#xff09;原创&#xff0c;请尊重知识产权&#xff0c;转发请注明出处&#xff0c;不接受任何抄袭、演绎…

linux centos 账户管理命令

在CentOS或其他基于Linux的系统上&#xff0c;账户管理涉及到用户的创建、修改、删除以及密码的管理等任务。 linux Centos账户管理命令 1 创建用户&#xff1a; useradd username 这将创建一个新用户&#xff0c;但默认不会创建家目录。如果想要创建家目录&#xff0c;可以…

大数据开发工程师需要具备哪些技能?

在回答这个问题之前&#xff0c;需要充分了解一下当前大数据的几个就业方向&#xff0c;可以参考下主流互联网行业的部门架构、职责和JD&#xff0c;大数据开发工程师&#xff0c;总体来说有这么几类&#xff0c;不同的公司叫法不一样&#xff1a; 1、数仓开发工程师 2、算法挖…

ubantu系统运维命令,端口相关操作

1、使用sudo ufw status命令查看所有开放的端口&#xff0c;如下图&#xff1a; 2、使用命令sudo ufw allow 8443&#xff0c;打开端口8443.如下图&#xff1a; 3、使用 sudo ufw reload刷新端口配置&#xff0c;如下图&#xff1a;

软件测试|Selenium 元素不可交互异常ElementNotInteractableException问题分析与解决

简介 在使用 Selenium 进行 Web 自动化测试时&#xff0c;我们可能会遇到各种异常情况。其中之一就是 ElementNotInteractableException 异常&#xff0c;这通常意味着在尝试与页面元素交互时出现了问题。本文将详细介绍这个异常的原因、可能的解决方法&#xff0c;并提供示例…

Python 解决安装三方包失败的问题

pip 安装三方包失败&#xff0c;常见的情况有三种&#xff1a;不能访问源所在服务器&#xff1b;Python 版本不支持&#xff1b;和本地版本冲突。 不能访问源服务器 对于这张问题&#xff0c;有两种解决方法 # 方法一 pip config set global.index-url <源服务器> pip…

MobaXterm游戏讲解

前言 没想到吧&#xff0c;这里还有游戏&#xff0c;以下是玩法 玩法 注 点击Type可以自由更改地图大小 1.Netwalk 这个游戏是用鼠标点击每一个格子&#xff0c;进行旋转方向&#xff0c;使得所有方块连接接来&#xff0c;全部变成亮蓝色 2.Mines 这个就是扫雷了&#xff…

C++I/O流——(2)预定义格式的输入/输出(第一节)

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 含泪播种的人一定能含笑收获&#xff…

【设计模式】01-前言

23 Design Patterns implemented by C. 从本文开始&#xff0c;一系列的文章将揭开设计模式的神秘面纱。本篇博文是参考了《设计模式-可复用面向对象软件的基础》这本书&#xff0c;由于该书的引言 写的太好了&#xff0c;所以本文基本是对原书的摘抄。 0.前言 评估一个面向对…

如何利用大语言模型(LLM)打造定制化的Embedding模型

一、前言 在探索大语言模型&#xff08;LLM&#xff09;应用的新架构时&#xff0c;知名投资公司 Andreessen Horowitz 提出了一个观点&#xff1a;向量数据库是预处理流程中系统层面上最关键的部分。它能够高效地存储、比较和检索高达数十亿个嵌入&#xff08;也就是向量&…

unittest自动化测试框架

一、unittest简介 Unittest是python内置的一个单元测试框架&#xff0c;主要用于自动化测试用例的开发与执行 简单的使用如下 import unittestclass TestStringMethods(unittest.TestCase):def setUp(self):print("test start")def test_upper(self):self.assertE…

可以打印试卷的软件有哪些?推荐这几款

可以打印试卷的软件有哪些&#xff1f;随着科技的飞速发展&#xff0c;越来越多的学习工具如雨后春笋般涌现&#xff0c;其中&#xff0c;能够打印试卷的软件尤其受到广大学生和家长的青睐。这些软件不仅方便快捷&#xff0c;而且内容丰富&#xff0c;可以满足不同学科、不同年…

python学习笔记9(程序的描述方式、程序的组织结构、顺序结构、选择结构1)

&#xff08;一&#xff09;程序的描述方式 自然语言、流程图、伪代码 &#xff08;二&#xff09;程序的组织结构 顺序、选择、循环 &#xff08;三&#xff09;顺序结构 &#xff08;四&#xff09;选择结构1 if 1、条件写法1 2、如果只有一个判断的写法 3、注意冒号和缩进…

NOIP2013提高组day2 - T3:华容道

题目链接 [NOIP2013 提高组] 华容道 题目描述 小 B 最近迷上了华容道&#xff0c;可是他总是要花很长的时间才能完成一次。于是&#xff0c;他想到用编程来完成华容道&#xff1a;给定一种局面&#xff0c; 华容道是否根本就无法完成&#xff0c;如果能完成&#xff0c; 最少…

Element-ui图片懒加载

核心代码 <el-image src"https://img-blog.csdnimg.cn/direct/2236deb5c315474884599d90a85d761d.png" alt"我是图片" lazy><img slot"error" src"https://img-blog.csdnimg.cn/direct/81bf096a0dff4e5fa58e5f43fd44dcc6.png&quo…