Linux管道共享内存

news/2024/9/20 1:20:05/ 标签: linux, 管道, 共享内存

前言

进程虽然是独立运行的个体,但它们之间有时候需要协作才能完成一项工作,比如有两个进程需要同步数据,进程 A 把数据准备好后,想把数据发往进程 B,进程 B 必须被提前通知有数据即将到来,或者进程 A 想发送信号给进程 B,以控制进程 B 的运行模式,再或者数据被多个进程共享时,数据变更后应该被所有进程看到,总之诸如此类的需求很多,操作系统必须要实现进程间的相互通信。

进程间通信方式有很多种,有管道、消息队列、共享内存、socket 网络通信等。在认识通信方式之前,先来认识几个概念。

公共资源

可以是公共内存、公共文件、公共硬件等,总之是被所有任务共享的一套资源。

临界区

若多个任务都访问同一公共资源,那么各任务中访问公共资源的指令代码组成的区域就称为临界区。临界区是指程序中那些访问公共资源的指令代码,即临界区是指令,并不是受访的静态公共资源。

互斥

是指某一时刻公共资源只能被 1 个任务独享,即不允许多个任务同时出现在自己的临界区中。公共资源在任意时刻只能被一个任务访问,即只能有一个任务在自己的临界区中执行,其他任务想访问公共资源时,必须等待当前公共资源的访问者完全执行完他自己的临界区代码后(使用完资源后)再开始访问。

管道

管道是进程间通信的方式之一,在 Linux 中一切皆文件,因此管道也被视为文件,只是该文件并不存在于文件系统上,而是只存在于内存中。

既然是文件,管道就要按照文件操作的函数来使用,因此也要使用open 、close 、read 、write 等方法来操作管道管道通常被多个进程共享,而且存在于内存之中,因此共享的原理是所有进程在地址空间中都可以访问到它,管道其实就是内核空间中的内存缓冲区。

管道是用于存储数据的中转站,当某个进程往管道中写入数据后,该数据很快就会被另一个进程读取,之后可以用新的数据覆盖老数据,继续被别的进程读取,因此管道属于临时存储区,其中的数据在读取后可被清除。

管道是个环形缓冲区,环形缓冲区中一个指针用于读数据,另一个用于写数据。当缓冲区己满时,生产者要睡眠,并在睡眠前唤醒消费者,当缓冲区为空时,消费者要睡眠,并在睡眠前唤醒生产者。当缓冲区满或空时,使一方休眠,这是保证数据不丢失的方法。

管道有两端,一端用于从管道中读入数据,另一端用于往管道中写入数据。这两端使用文件描述符的方式来读取,故进程创建管道实际上是内核为其返回了用于读取管道缓冲区的文件描述符,一个描述符用于读,另一个描述符用于写。fd[0]用于读取管道,fd[1]用于写入管道

image-20240420110732072

一般情况下,父子进程中都是一个读数据,一个写数据,并不会存在一方又读又写的情况,因此在父子进程中会分别关掉不使用的管道描述符。比如父进程负责往管道中写数据,它只需要fd[1]描述符,因此只可以通过 close系统调用关闭fd[0]。子进程负责从管道中读数据,它只需要fd[0]描述符,因此只可以通过close系统调用关闭fd[1]。

匿名管道

pipe()

image-20240420111055102

flags介绍

当没有数据可读时
1.O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
2.O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
管道满的时候
1.O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
2.O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

管道读写规则

  1. 读端不读或者读的慢,写端要等读端
  2. 读端关闭,写端收到SIGPIPE信号直接终止
  3. 写端不写或者写的慢,读端等写端
  4. 写端关闭,读端读完pipe内部的数据,读到0说明到文件结尾。
  5. 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
  6. 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>
//管道是单向,面向字节流,管道是有大小的,自带同步机制
int main()
{int pipefd[2] = {0};if(pipe(pipefd) != 0){perror("pipe false!");return 1;}printf("pipefd[0]:%d\n", pipefd[0]);printf("pipefd[1]:%d\n", pipefd[1]);// pipefd[0] refers to theread end of the pipe// pipefd[1] refers to the  write  end  of  the  pipe.//子进程写if(fork() == 0){close(pipefd[0]);const char* msg = "hello world\n";while(1){//只要有缓冲区就一直写sleep(1);write(pipefd[1], msg, strlen(msg));//验证管道的大小//write(pipefd[1], "c", 1);// count++;// printf("%d\n", count);}}close(pipefd[1]);//父进程读while(1){char buffer[1024] = {0};//只要有数据就一直读ssize_t s = read(pipefd[0], buffer, sizeof(buffer));if(s == 0){printf("写端退出\n");break;} else if(s > 0) {buffer[s] = '\0';printf("父进程读到子进程发来的:%s", buffer);}else break;}int status = 0;waitpid(-1, &status, 0);printf("exit code: %d\n", (status >> 8)&0xFF);printf("exit signal: %d\n", (status)&0x7F);}

总结

  1. 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道
  2. 管道提供流式服务
  3. 一般而言,进程退出,管道释放,所以管道的生命周期随进程
  4. 一般而言,内核会对管道操作进行同步与互斥
  5. 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

命名管道

命名管道相比匿名管道,可以在不同的进程之前进行通信。相比直接继承父进程资源,命名管道要想让不同进程看到同一份资源,是通过文件名来实现的。

mkfifo()

image-20240420112229698
实现一个client和server通信的例子。

client

#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#define MY_FIFO "./fifo"
int main()
{int fd = open(MY_FIFO, O_WRONLY);if(fd < 0){perror("open error\n");return 1;}while(1){printf("请输入字符");fflush(stdout);char buffer[32] = {0};ssize_t s = read(0, buffer, sizeof(buffer) - 1);if(s > 0){buffer[s - 1] = 0;//printf("%s\n", buffer);write(fd, buffer, strlen(buffer));}}close(fd);
}

server

#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
#define MY_FIFO "./fifo"int main()
{umask(0); //避免影响mkfifo的权限设置if(mkfifo(MY_FIFO, 0666) < 0){perror("mkfifo error");return 1;}int fd = open(MY_FIFO, O_RDONLY);if(fd < 0){perror("open error");return 2;}while(1){char buffer[32] = {0};ssize_t s = read(fd, buffer, sizeof(buffer) - 1);if(s > 0){buffer[s] = 0;if(strcmp(buffer, "ls") == 0){if(fork() == 0){execl("/usr/bin/ls", "ls", "-a","-l", NULL);exit(1);}waitpid(-1, NULL, 0);}printf("client#:%s\n", buffer);}else if(s == 0){printf("client quit...\n");}else{perror("read error");}}close(fd);return 0;
}

image-20240420094254168

共享内存

共享内存是最快的通信方式,systemV的IPC资源生命周期是随内核的,要通过显示释放[ipcrm -m shmid 删除memory ],或者os重启。不提供同步互斥机制。

创建共享内存shmget()

image-20240420163631476

参数

key是共享内存的名字,key能保证这个共享内存的名字是唯一的。

size:共享内存大小

shmflg:IPC_CREAT、IPC_EXCL 等IPC_CREAT和IPC_EXCL一起用能保证创建出来的共享内存是最新的

返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

共享内存链接到进程地址空间shmat()

image-20240420164101869

参数

shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

shmaddr为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()

image-20240420164835293

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

CMD

命令说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够的权限下,把共享内存的当前关联值设置为shmid_ds给出的值
IPC_RMID删除共享内存

查看共享内存

image-20240420103030140

例子

server

#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>#define PATH_NAME "./"
#define PROJ_ID 0x6666#define SIZE 4096 //最好是4096的整数倍int main()
{//ftok创建key (自定义路径,自定义ID)key_t key=ftok(PATH_NAME,PROJ_ID);if(key<0){perror("ftok"); return 1;}//创建共享内存,返回的是共享内存的标识码int shmid=shmget(key,SIZE,IPC_CREAT);if(shmid<0){perror("shmget"); return 2;}printf("key:%u,shmid:%d\n",key,shmid);//attaches the shared memory//返回指向共享内存的一个地址的指针char* mesg=(char*)shmat(shmid,NULL,0);printf("attaches the shared memory success!\n");//通信开始while(1){sleep(1); printf("%s\n",mesg);}//detaches the shared memoryshmdt(mesg);printf("detaches shm success!\n");//删除共享内存shmctl(shmid,IPC_RMID,NULL);printf("key:0x%x,shmid:%d->shm delete success\n",key,shmid);return 0;}

client

#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>#define PATH_NAME "./"
#define PROJ_ID 0x6666#define SIZE 4096 //最好是4096的整数倍int main()
{key_t key=ftok(PATH_NAME,PROJ_ID);//相同的方法创建出相同的keyif(key<0){perror("ftok"); return 1;}//创建共享内存,返回的是共享内存的标识码int shmid=shmget(key,SIZE,IPC_CREAT);if(shmid<0){perror("shmget"); return 1;}//attaches the shared memory//返回指向共享内存的一个地址的指针char* mesg=(char*)shmat(shmid,NULL,0);printf("client attaches the shared memory success!\n");//开始通信//detachesshmdt(mesg);printf("client detaches success!\n");return 0;
}

http://www.ppmy.cn/news/1428679.html

相关文章

安全开发实战(3)--存活探测与端口扫描

目录 安全开发专栏 前言 存活探测 端口扫描 方式一: 1.3.1 One 1.3.2 Two 1.3.3 批量监测 方式二: 1.3.1 One 1.3.2 Two 1.3.3 Three 1.3.4 扫描ip地址,提取出开放端口和协议 ​编辑 1.3.5 批量扫描(最终完成版) 总结 安全开发专栏 安全开发实战​http://t.csd…

在线拍卖系统,基于SpringBoot+Vue+MySql开发的在线拍卖系统设计和实现

目录 一. 系统介绍 二. 功能模块 2.1. 管理员功能模块 2.2. 用户功能模块 2.3. 前台首页功能模块 2.4. 部分代码实现 一. 系统介绍 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系…

数电期末复习(二)逻辑代数基础

这里写目录标题 2.1 二值逻辑变量与基本逻辑运算2.1.1 与运算2.1.2 或运算2.1.3 非运算2.1.4 常用复合逻辑运算 2.2 逻辑函数的建立及其表示方法2.2.1 真值表表示2.2.2 逻辑函数表达式表示2.2.3 逻辑图表示方法2.2.4 波形图表示方法 2.3 逻辑代数2.3.1 逻辑代数的基本定律和恒等…

2.Vue简介

Vue简介 Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&#xff0c;V…

【QT教程】QT6物联网应用

QT6物联网应用 使用AI技术辅助生成 QT界面美化视频课程 QT性能优化视频课程 QT原理与源码分析视频课程 QT QML C扩展开发视频课程 免费QT视频课程 您可以看免费1000个QT技术视频 免费QT视频课程 QT统计图和QT数据可视化视频免费看 免费QT视频课程 QT性能优化视频免费看 免费Q…

【蓝桥杯2025备赛】素数判断:从O(n^2)到O(n)学习之路

素数判断:从O( n 2 n^2 n2)到O(n)学习之路 背景:每一个初学计算机的人肯定避免不了碰到素数&#xff0c;素数是什么&#xff0c;怎么判断&#xff1f; 素数的概念不难理解:素数即质数&#xff0c;指的是在大于1的自然数中&#xff0c;除了1和它本身不再有其他因数的自然数。 …

Linux文件chattr/lsattr/Linux权限(搭建权限测试环境实战)引申到内部原理及Linux删除系统文件原理-7539字详谈

企业高薪思维: 每一个阶段什么时候是最重要的&#xff1f;&#xff08;快速定位&#xff09; 1.学习最重要的事情 &#xff08;学生阶段&#xff0c;找工作前阶段&#xff09; 2.家庭&#xff0c;女朋友 &#xff08;工作阶段/学生阶段&#xff0c;学习不受到影响&#xff09; …

electron项目打包慢、打包报错

项目使用了electron框架&#xff0c;在第一次打包或者网络条件不好的环境下进行打包时熟速度慢的出奇&#xff0c;甚至经常出现打包失败的情况&#xff08;如上面图片的报错&#xff09;。 这是因为&#xff0c;在electron打包的过程中&#xff0c;需要去官方源https://github.…

UML绘制

processon官网 https://www.processon.com/template_create 官方学习地址 https://plantuml.com/zh/class-diagram 在Android studio 中自动生成类图 https://blog.csdn.net/zyfzhangyafei/article/details/126636358 plantUML 在线编辑 https://www.plantuml.com/plantuml/um…

排序 “叁” 之交换排序

目录 1. 基本思想 2.冒泡排序 2.1 基本思想 2.2 代码示例 2.3 冒泡排序的特性总结 3.快速排序 3.1 基本思想 &#x1f335;hoare版本 &#x1f335;挖坑法 ​编辑 &#x1f335;前后指针版本 ​编辑 3.2 快速排序优化 &#x1f33b;三数取中法选key 3.4 快速排序…

抓取电商产品数据的方法|电商平台商品详情数据|批量上架|商品搬家|电商封装API数据采集接口更高效安全的数据采集

大量级电商数据采集时使用电商API接口有以下优势&#xff1a; 1. 数据准确性&#xff1a;通过电商API接口获取数据&#xff0c;可以保证数据的准确性和实时性&#xff0c;避免了手动采集可能出现的错误和延迟。 2. 自动化采集&#xff1a;API接口可以实现自动化的数据获取和更…

Elasticsearch:(二)2.安装kibana

1.环境安装介绍: 安装java环境安装Elasticsearch安装kibana安装Elasticsearch-head插件 本节文章主要讲解kibana的安装。 2.下载 下载Elasticsearch对应的版本,参考官方自身产品兼容版本:支持一览表 | Elastic 下载地址:Kibana 7.17.20 | Elastic Kibana 7.17.20 | Ela…

UE5、CesiumForUnreal实现建筑白模生成及白模美化功能

1.实现目标 在专栏上篇文章基于GeoJson文件生成城市级白模(本文建筑白模数量12w+)的基础上修改,计算法线和纹理坐标,并基于特定材质进行美化,美化后的白模GIF动图如下所示: 文章目录 1.实现目标2.实现过程2.1 基于Cesium材质美化2.1.1实现原理2.1.2 C++代码2.1.3 蓝图应…

webview引入的外链跳转到微信小程序的原生页面

1、首先在H5设置的入口文件中引入jweixin的sdk <head> <meta charset"utf-8"> <meta http-equiv"X-UA-Compatible" content"IEedge"> <script type"text/javascript" src"https:/…

【LeetCode热题100】【动态规划】零钱兑换

题目链接&#xff1a;322. 零钱兑换 - 力扣&#xff08;LeetCode&#xff09; 要拿硬币凑钱&#xff0c;硬币无限多&#xff0c;就是完全背包问题&#xff0c;定义dp[i]是要凑的钱i的硬币数&#xff0c;对于当前硬币来说&#xff0c;如果选择了这个硬币&#xff0c;要么要凑的…

Java发送邮件 启用SSL

使用的maven依赖: <dependency><groupId>com.sun.mail</groupId><artifactId>javax.mail</artifactId><version>1.4.7</version> </dependency> 配置文件mail.properties如下: # 邮箱配置 email.username=your-email@exa…

【神经网络与深度学习】文本情感分类

数据准备 AclImdb – v1 Dataset 是用于二进制情绪分类的大型电影评论数据集&#xff0c;其涵盖比基准数据集更多的数据&#xff0c;其中有 25,000 条电影评论用于训练&#xff0c;25,000 条用于测试&#xff0c;还有其他未经标记的数据可供使用。 数据预处理和数据装载 imp…

金融数字化能力成熟度指引

1 范围 本文件提出了金融数字化能力成熟度模型、成熟度计算方法&#xff0c;明确了不同维度金融数字化转型能力 相应的分档要求。 本文件适用于金融机构衡量金融科技应用和数字化转型发展水平&#xff0c;检视自身数字化发展优势与短板&#xff0c; 加快数字化转型&#xff0c…

亚远景科技-如何应对汽车软件开发中质量与速度的冲突带来的挑战?

在汽车软件开发中&#xff0c;质量与速度之间的冲突是常见的挑战。传统上&#xff0c;提高质量可能需要更多的时间和资源&#xff0c;而追求速度可能会牺牲一定的质量标准。然而&#xff0c;结合ASPICE&#xff08;Automotive SPICE&#xff09;和敏捷&#xff08;Agile&#x…

富文本编辑器(wangEdit)+(jquery.wordexport)实现web版在线编辑导出

小插曲&#xff1a;最开始的方向是Html5的contenteditable"true"的文档可编辑属性。只能修改文档文字内容&#xff0c;不能修改样式&#xff0c;如修改字体&#xff0c;字号&#xff0c;颜色等。于是用了第一款&#xff08;quil&#xff09;富文本插件。只能说一般&a…