【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;
}

为了让进程之间能够通信,我们让其能够看到同一份资源,但看到同一份资源也会带来一些时序问题,从而造成数据不一致的问题。
我们上述的代码中是用加入管道的方式从而保证其同步性使得:只有一个进程写完后另一个进程才能够去读。信号量也是解决同步机制的一种方法。

我们将多个进程看到的公共的一份资源称为临界资源。
把进程访问临界资源的代码称为临界区
多个执行流运行时互相干扰,主要是我们不加保护的访问了临界资源(在非临界区是没有影响的),为了更好的进行临界区的保护,我们让多执行流在任何时刻都只有一个进程能够进入临界区----我们将其称为互斥。
原子性:要么不做,要么做完,没有中间状态。

关于信号量我们在后面会有更加详细的解读。

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

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

相关文章

Openssl数据安全传输平台011:秘钥协商客户端

文章目录 0. 代码仓库拷贝jsoncpp库至工程目录下编译protobuf类文件Message.proto VS 2022 设置 0. 代码仓库 https://github.com/Chufeng-Jiang/OpenSSL_Secure_Data_Transmission_Platform/tree/main/Preparation 拷贝jsoncpp库至工程目录下 编译protobuf类文件 VS2022 pr…

宝塔面板安装Python和Flask(新版Python项目)

&#xff08;一&#xff09;宝塔面板的项目菜单&#xff0c;打开Python项目的“项目版本管理” 安装Python版本3.10.0。 会创建一个Python版本的文件夹www/server/pyproject_evn/versions/ 会创建一个Python虚拟环境的文件夹www/server/pyproject_evn/python_venv/ &#xf…

线程安全问题

线程安全 简单来说&#xff0c;在多个线程访问某个方法或者对象的时候&#xff0c;不管通过任何的方式调用以及线程如何去交替执行。在程序中不做任何同步干预操作的情况下&#xff0c;这个方法或者对象的执行/修改都能按照预期的结果来反馈&#xff0c;那么这个类就是线程安全…

Cross-modal Variational Alignment of Latent Spaces

方法 潜空间LS 辅助信息 作者未公布代码

【数据结构】插入排序

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈数据结构 &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 直接插入、希尔排序 1. 什么是排序2…

GPT做SQL查询引擎的自然语言

目录 面向企业查询的生成式人工智能 步骤1&#xff1a;将示例数据转换为单字符字符串 步骤2&#xff1a;为大型语言模型&#xff08;LM&#xff09;创建提示符 步骤3&#xff1a;将数据发送到OpenAI的API 步骤4&#xff1a;执行GPT返回的SQL代码的结果 步骤5(可选)&#…

shell算数运算指令、

1.shell算数运算的指令 (( )) $[ ] let expr expr的字符串运算 例子&#xff1a; 2.shell的if分支结构

第15届蓝桥杯Scratch选拔赛中级(STEMA)真题2023年8月

第15届蓝桥杯Scratch选拔赛中级&#xff08;STEMA&#xff09;真题2023年8月 一、单选题 第 1 题 单选题 点击以下积木块&#xff0c;生成的随机数是一个&#xff08; &#xff09;。 A.整数 B.小数 C.整数或小数 D.以上都不对 第 2 题 单选题 运行以下程序&#xff0…

【Docker】github Actions自动构建

通过github的Actions 实现代码push仓库后自动构建容器并发布到DockerHub. 创建项目 首先我们创建一个项目,这里我就用Vue项目进行演示. npm init vuelatest Actions-demo-swback进去项目&#xff0c;按照提示执行 npm install npm run dev 启动项目. 首先保证项目的正常启动…

数据库的概念和sql语句

数据&#xff1a;数字信息 据&#xff1a;就是属性 对一系列对象的具体属性的描述的集合 数据库&#xff1a;数据库就是用来组织&#xff08;各个数据之间是有关联。是按照规则组织起来的&#xff09;&#xff0c;存储和管理&#xff08;对数据的增删改查&#xff09;的仓库 …

Android 和 iOS APP 测试的那些区别

目前市面上主流的移动操作系统就是 Android 和 iOS 两种&#xff0c;移动端测试本身就跟 Web 应用测试有自己的专项测试&#xff0c;比如安装、卸载、升级、消息推送、网络类型测试、弱网测试、中断测试、兼容性测试等都是区别于 Web 应用需要关注的测试领域。 那么&#xff0…

汽车行驶性能的主观评价方法(1)-底盘校准方法

底盘校准的目的是&#xff0c;从行驶性能和行驶舒适性两个方面进行协调&#xff0c;从而优化行驶动力学特性。为了达到这一目标&#xff0c;工程人员早在设计阶段&#xff0c;就对大多数对行驶动力性有重要意义的部件提出了要求。这些要求不仅与底盘的组件有关&#xff0c;还必…

零资源的大语言模型幻觉预防

零资源的大语言模型幻觉预防 摘要1 引言2 相关工作2.1 幻觉检测和纠正方法2.2 幻觉检测数据集 3 方法论3.1 概念提取3.2 概念猜测3.2.1 概念解释3.2.2 概念推理 3.3 聚合3.3.1 概念频率分数3.3.2 加权聚合 4 实验5 总结 摘要 大语言模型&#xff08;LLMs&#xff09;在各个领域…

796. 子矩阵的和(二维前缀和)

题目&#xff1a; 796. 子矩阵的和 - AcWing题库 思路&#xff1a; 1.暴力搜索&#xff08;搜索时间复杂度为O(n2)&#xff0c;很多时候会超时&#xff09; 2. 前缀和&#xff08;左上角&#xff08;二维&#xff09;前缀和&#xff09;&#xff1a;本题特殊在不是直接求前…

730. 机器人跳跃问题--二分

题目&#xff1a; 730. 机器人跳跃问题 - AcWing题库 思路&#xff1a; 二分 1.当起始能量E大于最大建筑高度1e5 时&#xff0c;E的能量在整个条约过程中全程递增&#xff0c;则大于E的初始能量也必然成立&#xff08;满足二段性&#xff09;。故最小初始能量范围为[0,1e5]&a…

研发效能(DevOps)职业技术认证-第六期开班啦丨IDCF

本证书是由国家工业和信息化部教育与考试中心颁发的职业技术证书&#xff0c;也是国内首个《研发效能&#xff08;DevOps&#xff09;工程师职业技术认证》。该《认证》对研发效能&#xff08;DevOps&#xff09;工程师的职业技术分为初级、中级、高级三个专业等级。 IDCF社区…

[SQL开发笔记]UPDATE 语句:更新表中的记录

一、功能描述&#xff1a; UPDATE 语句&#xff1a;用于更新表中的记录 二、UPDATE 语句语法详解&#xff1a; UPDATE 语法 UPDATE table_nameSET column1value1,column2value2,...WHERE some_columnsome_value; 参数说明&#xff1a; 1.table_name&#xff1a;要修改的表…

淘宝API接口获取商品信息,订单管理,库存管理,数据分析

在淘宝开放平台中&#xff0c;每个API接口都有相应的文档说明和授权机制&#xff0c;以确保数据的安全性和可靠性。开发者可以根据自己的需求选择相应的API接口&#xff0c;并根据文档说明进行调用和使用。 淘宝开放平台API接口是一套REST方式的开放应用程序编程接口&…

CMake aux_source_directory 学习

如下&#xff0c;prj是空文件夹&#xff1b; add.h; #include <iostream>using namespace std;int add1(int a, int b); num.h; int num1100; int num2301; add.cpp&#xff1b; #include "add.h"int add1(int i, int j) {return i j; } main.cpp&#x…

【VUE】ElementPlus之动态主题色调切换(Vue3 + Element Plus+Scss + Pinia)

前言 关于ElementPlus的基础主题色自定义可以参阅《【VUE】ElementPlus之自定义主题样式和命名空间》 有了上面基础的了解&#xff0c;我们知道ElementPlus的主题色调是基于CSS3变量特性进行全局控制的&#xff0c; 那么接下来我们也基于CSS3变量来实现主题色调的动态切换效果&…