【1++的Linux】之进程间通信(共享内存)
👍作者主页:进击的1++
🤩 专栏链接:【1++的Linux】
我们在前面的文章中提到过,进程间的通信本质都是先看到同一块资源,然后通过这同一块资源进行通信,并且是单向的通信,只能一端发,一端进行读,共享内存也是基于这样的原理而进行的通信,与管道有异曲同工之处,管道是基于文件,拿到同一个文件的文件描述符而进行的通信,需要调用对文件的读写操作函数,因此要经过内核。而共享内存是不会的,其是内存级的通信,因此它的效率非常高。
ps:共享内存因为数据不需要在客户机和服务器端之间复制,数据直接写到内存,不用若干次数据拷贝,所以这是最快的一种IPC。
那么是什么是共享内存呢?
我们其实早已经和它见过面了!!!
我们的共享内存就在共享区中。
我们通过OS申请一块物理内存,作为共享内存,进程间是独立的,有自己的页表,进程地址空间。两进程通过页表将这块物理内存映射到各自的进程地址空间,此时他们就能够看到同一块资源啦!
共享内存的提供者是OS,共享内存不止有一块,此时就需要将他们进行管理,怎么管理呢?六字真言:先描述,后组织!!!
那么共享内存实质是什么呢?----共享内存块+对应的内核数据结构。
共享内存的建立:
shmget()函数
int shmget(key_t key, size_t size, int shmflg);
它的参数都是指什么呢?
key : 通信的双方要保证看到的是同一块共享内存,,它是几不重要,只要它在系统里唯一就行。相同的key就可以看到同一块空间了。
size : 共享内存的大小
shmflg :标志位 IPC_CREAT 创建共享内存:若存在则获取,不存在则创建 IPC_CREAT | IPC_EXCL 创建共享内存:若存在,报错返回,若不存在,创建。
IPC_EXCL单独使用没有意义。
返回值是一个整数 ,其类似于文件描述符一样,是共享内存用户层的标识符。
那么key 值怎么获取,才能使系统中唯一的呢?
key_t ftok(const char *pathname, int proj_id) 我们调用ftok函数去生成一个唯一的key。
pathname:指定的文件,此文件必须存在且可存取
proj_id:计划代号(project ID)
ftok的典型实现是调用stat函数,然后组合以下三个值:
① pathname所在的文件系统的信息(stat结构的st_dev成员)。
② 该文件在本文件系统内的索引节点号(stat结构的st_ino成员)。
③ proj_id的低序8位(不能为0)。
上述三个值的组合产生一个32位键。
创建出共享内存后,我们需要将其挂接到我们需要进行通信的地址空间上,我们用shmat()来实现。
void *shmat(int shm_id, const void *shm_addr, int shmflg);
第一个参数,shm_id是由shmget()函数返回的共享内存标识。
第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
第三个参数,shm_flg是一组标志位,通常为0。
调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回
挂接完成后我们就可以进行通信了。
若向结束通信,我们可以选择让进程和共享内存分离
我们使用shmdt() 函数实现。
int shmdt(const void *shmaddr);
参数shmaddr是shmat()函数返回的地址指针,调用成功时返回0,失败时返回-1.
该函数只是让进程和其分离,并不会删除共享内存。
删除共享内存我们用shmctl()函数。
int shmctl(int shm_id, int command, struct shmid_ds *buf);
第一个参数,shm_id是shmget()函数返回的共享内存标识符。
第二个参数,command是要采取的操作,它可以取下面的三个值 :
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值 IPC_RMID:删除共享内存段
第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。
出来使用函数删除共享内存,我们还可以用命令手动去删除:
ipcs -m 用来查询存在的共享内存
ipcrm -m+shmid 可以删除对应的共享内存
共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问
下面我们利用管道来实现一个同步的共享内存:
#pragma once
#include<iostream>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cassert>
#include<unistd.h>
#include<cstring>
#include<sys/stat.h>
#include<fcntl.h>
#include"LOG.hpp"
#define IPC_PATH "/home/hyp"
#define PROJ_id 0x66 //8位
#define SHM_SIZE 4096 //最好是页的整数倍
#define Fifo_Name "./fifo"class Init
{public:Init(){umask(0);int n=mkfifo(Fifo_Name,0666);//创建管道if(n==-1){perror("mkfifo");exit(1);}}~Init(){unlink(Fifo_Name);}};int OPen_fifo(std::string fifo_name,std::string flag)
{int fd=-1;if(flag=="READ"){fd=open(fifo_name.c_str(),O_RDONLY);}else if(flag=="WRITE"){fd=open(fifo_name.c_str(),O_WRONLY);}else{exit(2);}assert(fd!=-1);return fd;
}
void Sendmessage(int fd)
{int commd=0;int s=write(fd,&commd,sizeof commd);log("发送中.....",DEBUG)<<std::endl;assert(s!=-1);}void Wait(int fd)
{log("等待中.....",DEBUG)<<std::endl;int commd=1;int s=read(fd,&commd,sizeof(commd));assert(s!=-1);
}
//日志打印
#pragma once
#include<iostream>
#include<string>
#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(const std::string& message,int level)
{std::cout<<"|"<<mes[level]<<"|"<<message<<"|";return std::cout;
}#endif
//发送端
#include"comm.hpp"
#include"LOG.hpp"int main()
{key_t k=ftok(IPC_PATH,PROJ_id);if(k==-1){perror("ftok");exit(1);}log("creat key done",DEBUG)<<" client# k:"<<k<<std::endl;int shmid=shmget(k,SHM_SIZE,0);assert(shmid>0);log("creat shm done",DEBUG)<<std::endl;//将创建出的共享内存挂接到自己的地址空间中char* shmaddr=(char*)shmat(shmid,nullptr,0);if(shmaddr==nullptr){perror("shmat");exit(1);}log("attach shm success",DEBUG)<<std::endl;//使用int fd= OPen_fifo(Fifo_Name,"WRITE");while(true){//sleep(1);//char puts[1024];//fgets(shmaddr,10,stdin);//Sendmessage(shmaddr);Sendmessage(fd);int s=read(0,shmaddr,SHM_SIZE-1);if(s>0){shmaddr[s-1]='\0';if(strcmp(shmaddr,"quit")==0){break;}}}close(fd);int n=shmdt(shmaddr);if(n==-1){perror("shmdt");exit(1);}log("detach shm success",DEBUG)<<std::endl;return 0;
}
//收端
#include"comm.hpp"
#include"LOG.hpp"Init init;
int main()
{key_t k=ftok(IPC_PATH,PROJ_id);if(k==-1){perror("ftok");exit(1);}log("creat key done",DEBUG)<<" server# k:"<<k<<std::endl;int shmid=shmget(k,SHM_SIZE,IPC_CREAT|IPC_EXCL|0666);//通信的发起者assert(shmid>0);log("creat shm done",DEBUG)<<std::endl;//将创建出的共享内存挂接到自己的地址空间中char* shmaddr=(char*)shmat(shmid,nullptr,0);if(shmaddr==nullptr){perror("shmat");exit(1);}log("attach shm success",DEBUG)<<std::endl;//使用int fd=OPen_fifo(Fifo_Name,"READ");while(true){Wait(fd);printf("%s\n",shmaddr);if(strcmp(shmaddr,"quit")==0) break;}int x=shmctl(shmid,IPC_RMID,0);assert(x!=-1);log("delete shm success",DEBUG)<<std::endl;return 0;
}
为了让进程之间能够通信,我们让其能够看到同一份资源,但看到同一份资源也会带来一些时序问题,从而造成数据不一致的问题。
我们上述的代码中是用加入管道的方式从而保证其同步性使得:只有一个进程写完后另一个进程才能够去读。信号量也是解决同步机制的一种方法。
我们将多个进程看到的公共的一份资源称为临界资源。
把进程访问临界资源的代码称为临界区
多个执行流运行时互相干扰,主要是我们不加保护的访问了临界资源(在非临界区是没有影响的),为了更好的进行临界区的保护,我们让多执行流在任何时刻都只有一个进程能够进入临界区----我们将其称为互斥。
原子性:要么不做,要么做完,没有中间状态。
关于信号量我们在后面会有更加详细的解读。
相关文章:

【1++的Linux】之进程间通信(共享内存)
👍作者主页:进击的1 🤩 专栏链接:【1的Linux】 我们在前面的文章中提到过,进程间的通信本质都是先看到同一块资源,然后通过这同一块资源进行通信,并且是单向的通信,只能一端发&#…...

Linux高性能服务器编程——ch8笔记
第8章 高性能服务器程序框架 8.1 服务器模型 服务器启动后,首先创建一个(或多个)监听socket,并调用bind函数将其绑定到服务器感兴趣的端口,然后调用listen函数等待客户连接。服务器稳定运行之后,客户端就可…...

Android WMS——ViewRootImpl分析(六)
一、简介 ViewRootImpl是View中的最高层级,属于所有View的根(但ViewRootImpl不是View,只是实现了ViewParent接口),维护了整个视图结构,并作为输入事件的分发器和绘图管道的输入端点,承担着输入事件分发、窗口管理、视图绘制和系统事件响应等关键角色。对于Android应用程…...

Unsatisfied dependency expressed through bean property ‘sqlSessionTemplate‘;
代码没有问题,但是启动运行报错 2023-10-25 16:59:38.165 INFO 228964 --- [ main] c.h.h.HailiaowenanApplication : Starting HailiaowenanApplication on ganluhua with PID 228964 (D:\ganluhua\code\java\hailiao-java\target\classes …...

【C++】智能指针:auto_ptr、unique_ptr、share_ptr、weak_ptr(技术介绍 + 代码实现)(待更新)
文章目录 0. 概述智能指针,智能在哪儿?RAII 的介绍四个智能指针的特点: 1. auto_ptr(C98)🐎核心功能的简单实现 2. unique_ptr(C11)🐎核心功能的简单实现 3. shared_ptr&…...

nodejs+vue全国公考岗位及报考人数分析
传统的搜索引擎尽管解决了信息搜索问题,但无法进行有效的数据分析和优质资源的获取。并且,人们的需求不同,数据的要求也不同。为了解决这一问题,定向抓取数据的爬虫诞生了。它的诞生把人们从重复性的劳动中解放出来,节…...

【0基础学Java第二课】数据类型与变量
2. 数据类型与变量 2.1 字面常量2.2 数据类型2.3 变量2.3.1 变量概念2.3.2 语法格式 2.4 整型变量2.4.1 整型变量2.4.2 长整型变量2.4.3 短整型变量2.4.4 字节型变量 2.5 浮点型变量2.6 字符型2.7 布尔型变量2.8 类型转换2.9 类型提升2.10 字符串类型2.10.1 字符串拼接操作符 2…...

Pytorch整体工作流程代码详解(新手入门)
一、前言 本文详细介绍Pytorch的基本工作流程及代码,以及如何在GPU上训练模型(如下图所示)包括数据准备、模型搭建、模型训练、评估及模型的保存和载入。 适用读者:有一定的Python和机器学习基础的深度学习/Pytorch初学者。 本文…...

读图数据库实战笔记02_图数据建模
1. 概念 1.1. 实体 1.1.1. 通常用名词来表示 1.1.2. 描述一个领域中的事物或者事物类型 1.1.2.1. 汽车 1.1.2.2. 用户 1.1.2.3. 地理位置 1.1.3. 在逻辑模型和技术实现过程中,实体通常会变成“顶点” 1.2. 关系 1.2.1. 用动词(或动词短语&#…...

竞赛 深度学习卫星遥感图像检测与识别 -opencv python 目标检测
文章目录 0 前言1 课题背景2 实现效果3 Yolov5算法4 数据处理和训练5 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 **深度学习卫星遥感图像检测与识别 ** 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐…...

对Happens-Before的理解
Happens-Before Happens-Before 是一种可见性模型,也就是说,在多线程环境下。原本因为指令重排序的存在会导致数据的可见性问题,也就是 A 线程修改某个共享变量对 B 线程不可见。因此,JMM 通过 Happens-Before 关系向开发人员提供…...

分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测
分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测 目录 分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双…...

Java面试八股文之暑假合集
八股文暑假合集 基础篇二分查找 java基础篇7月12号面向对象和面向过程的区别重载和重写String 7月13号自动装箱和拆箱静态方法构造方法成员变量和局部变量对象引用和对象实例返回值 与equals(重要)hashcode()和equals()HashMap 7月16号线程,进程和程序final关键字的…...

竞赛选题 深度学习卫星遥感图像检测与识别 -opencv python 目标检测
文章目录 0 前言1 课题背景2 实现效果3 Yolov5算法4 数据处理和训练5 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 **深度学习卫星遥感图像检测与识别 ** 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐…...

机器学习第一周
一、概述 机器学习大致会被划分为两类:监督学习,无监督学习 1.1 监督学习 监督学习其实就是,给计算机一些输入x和正确的输出y(训练数据集),让他总结x->y的映射关系,从而给他其他的输入x&a…...

大数据采集技术与预处理学习一:大数据概念、数据预处理、网络数据采集
目录 大数据概念: 1.数据采集过程中会采集哪些类型的数据? 2.非结构化数据采集的特点是什么? 3.请阐述传统的数据采集与大数据采集的区别? 4.大数据采集的数据源有哪些?针对不同的数…...

MySQL - 为什么索引结构默认使用B+树,而不是其他?
B-Tree的缺点: 范围查询效率相对较低:虽然B-Tree支持范围查询,但在实际操作中可能需要进行多次树遍历,性能较差。磁盘空间利用不高:B-Tree中的非叶子节点也存储数据,导致磁盘空间利用率相对较低。更复杂的平…...

信息系统项目管理师教程 第四版【第3章-信息系统治理-思维导图】
信息系统项目管理师教程 第四版【第3章-信息系统治理-思维导图】...

2023.NET技术沙龙知识学习笔记
目录 一.Bootstrap Blazor UI组件库企业级应用介绍1.Blazor是什么2.为什么要用Blazor3.Bootstrap Blazor是什么 二.使用WebAssembly运行、扩展.NET应用程序1.WebAssembly简介2.WebAssembly的起源3.为什么选择二进制格式?4.WebAssembly与传统JavaScript的对比5.执行速…...

Golang教程——配置环境,再探GoLand
文章目录 一、Go是什么?二、环境配置验证配置环境变量 三、安装开发者工具GoLand四、HelloGolang 一、Go是什么? Go(也称为Golang)是一种开源的编程语言,由Google开发并于2009年首次发布。Go语言旨在提供一种简单、高…...

C++之lambda匿名、using、typedef总结【全】(二百四十九)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…...

基于SpringBoot的个人博客系统
基于SpringBootVue的个人博客系统的设计与实现~ 开发语言:Java数据库:MySQL技术:SpringBootMyBatisVue工具:IDEA/Ecilpse、Navicat、Maven 系统展示 主页 系统公告 博客详情 后台发文 摘要 基于Spring Boot的个人博客系统是一种…...

javascript中的继承
基本术语 本文中,proto [[Prototype]] 原型链 基本思想: 构造函数生成的对象有一个指针(proto)指向构造函数的原型。如果将构造函数1的原型指向另一个构造函数2的实例,则构造函数1的实例__proto__.proto 指向了构…...

智能问答技术在百度搜索中的应用
作者 | Xiaodong 导读 本文主要介绍了智能问答技术在百度搜索中的应用。包括机器问答的发展历程、生成式问答、百度搜索智能问答应用。欢迎大家加入百度搜索团队,共同探索智能问答技术的发展方向,文末有简历投递方式。 全文6474字,预计阅读时…...

STM32F4X SDIO(一) SD卡介绍
STM32F4X SDIO(一) SD卡介绍 SD卡分类外观分类容量分类传输速度分类 在之前的章节中,讲过有关嵌入式的存储设备,有用I2C驱动的EEPROM、SPI驱动的FLASH和MCU内部的FLASH,这类存储设备的优点是操作简单,但是缺…...

10分钟了解JWT令牌 (JSON Web)
10分钟了解JSON Web令牌(JWT) JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。今天给大家介绍JWT的原理和用法。 1.跨域身份验证 Internet服务无法与用户身份验证分开。一般过程如下。 1.用户向服务器发送用户名和密码。…...

【经验总结】ECU系统休眠后通过诊断报文唤醒ECU且唤醒网络后快发NM报文
目录 前言 正文 1.CanNM状体机分析 2.ComM状态机分析 3.解决方案 4.总结 前言...

基于Android 10系统的ROC-RK3399-PC Pro源码编译
基于Android 10系统的ROC-RK3399-PC Pro源码编译 一、开发环境搭建二、下载Android 10 SDK三、编译Android 10 SDK ROC-RK3399-PC Pro资料下载处:https://www.t-firefly.com/doc/download/145.html一、开发环境搭建 Android 10 SDK的编译对PC机的要求不低ÿ…...

网络滤波器/网络滤波器/脉冲变压器要怎样进行测试,一般要测试哪些参数?
Hqst华强盛导读:网络滤波器/网络滤波器/脉冲变压器要怎样进行测试,一般要测试哪些参数?测试网络滤波器的测试方法和步骤如何,需用到哪些测试工具和仪器设备呢? 一,网络流量的监控和过滤能力测试&am…...

基于vue天气数据可视化平台
目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:…...