Linux——进程间通信之SystemV共享内存

前言                

        SystemV通信一般包括三种:共享内存、消息队列和信号量。共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到 内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

共享内存通信

        1)共享内存通信的原理

        共享内存通信的原理是,两个进程的页表通信映射同一份物理内存完成通信,共享内存在系统中可以存在多份,实现多对进程进行通信。

2)创建共享内存

        linux提供了创建共享内存接口shmget,其中key值在操作系统中是唯一的,可以使用ftok系统调用获取,为了两个进程能够顺利读取到同一份共享内存,所以key值必须用户手动传参。

#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
//key为共享内存的唯一标识符,用于区分不同的共享内存,其值需要两个进程约定
//size共享内存的大小,单位是字节,一般是4kB的整数倍
//shmflg为标志位传参,可以同时传多个参数使用|连接
//IPC_CREAT:创建新的共享内存,如果已经存在该key值,直接返回对应的共享内存。如果不使用该参数,则检查是否存在该key对应的共享内存
//IPC_EXCL:与IPC_CREAT搭配使用,如果key对应的共享内存已经存在,直接报错,用于确保共享内存是新创建的。
//perms:指定是内存的权限,三位八进制数字
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
//作用是根据用户传入的参数生成一个唯一的key值
//pathname和proj_id可以随意指定,只有两个参数完全相同时,生成的key值才会相同
//返回值:生成的key,为一个数字

3)管理共享内存

因为操作系统中存在多份共享内存,操作系统必须对共享内存进行描述组织管理。

查看共享内存资源

ipcs -m
ipsc表示查看进程间通信,可以用于查看消息队列,共享内存和信号量
-m用于指定查看共享内存

控制共享内存资源

内核接口shmctl

#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//shmid:共享内存的id
//cmd:指定操作,常用操作有
IPC_STAT:将与shmid对应内核数据结构复制到buf中
IPC_SET:将shmid对应的共享内部分值设置为buf对应值
IPC_RMID:删除指定id的共享内存
//shmid_ds*buf:shmid_ds是操作系统内部对共享内存管理的数据结构
struct shmid_ds {struct ipc_perm shm_perm;    /* Ownership and permissions */size_t          shm_segsz;   /* Size of segment (bytes) */time_t          shm_atime;   /* Last attach time */time_t          shm_dtime;   /* Last detach time */time_t          shm_ctime;   /* Last change time */pid_t           shm_cpid;    /* PID of creator */pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */shmatt_t        shm_nattch;  /* No. of current attaches */...};struct ipc_perm {key_t          __key;    /* Key supplied to shmget(2) */uid_t          uid;      /* Effective UID of owner */gid_t          gid;      /* Effective GID of owner */uid_t          cuid;     /* Effective UID of creator */gid_t          cgid;     /* Effective GID of creator */unsigned short mode;     /* Permissions + SHM_DEST andSHM_LOCKED flags */unsigned short __seq;    /* Sequence number */
};

挂接共享内存

        共享内存创建出来不属于进程,而属于操作系统,挂接的作用是将共享内存首地址挂接到进程的页表当中,使用shmat接口实现挂接。如果挂接成功,就意味着进程的页表中创建了与物理地址的映射,并且可以直接通过虚拟地址访问共享内存地址。

#include <sys/types.h>
#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
//shmid是共享内存的id
//shmaddr是用户指明的挂接位置,默认为nullptr即可
//shmflg是标志位,默认可读写挂接设置为0即可
//返回值:如果挂接成功,返回挂接成功的地址,否则返回(void*)(-1)

去关联共享内存

        和挂接相反,去关联就是解除页表中虚拟地址和共享内存的关联信息,系统调用为shmdt


#include <sys/types.h>
#include <sys/shm.h>int shmdt(const void *shmaddr);
//shmaddr为共享内存的虚拟内存地址
//返回值为0表示解除关联成功,-1表示失败

4)销毁共享内存

        创建共享内存后,进程关闭,共享内存不会主动释放,会一直存在,下一次创建提示共享内存已经存在,除非重启系统,即共享内存声明周期不随进程而随内核。

        删除共享命令

ipcrm -m shmid

        删除指定id的共享内存

shmctl(shmid,IPC_RMID,nullptr);

共享内存的特性

1)共享内存不提供进程协同的任何特性,即使内存中还未写入数据,也可以直接读内存。可能导致两个进程数据不一致,需要用户提供同步机制,可以使用管道或者是信号量来完成两个进程的同步。

2)共享内存通信的速度是最快的,因为进程读写数据是对共享内存直接读写,共享内存同时被两个进程映射,通信过程中不需要系统调用。

综合以上两个特性,可以使用管道和共享内存结合的方式,完成进程间通信,管道为共享内存提供协同机制,而共享内存用于实际数据通信。

共享内存与命名管道结合通信实例

//common_fifo
#ifndef __COMMON_FIFO_HPP__
#define __COMMON_FIFO_HPP__#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <fcntl.h>
#include <cassert>
using namespace std;// 管道路径
string path = "./fifo";
// 创建mode
#define Mode 0666class fifo
{
public:fifo(const string &path) : _path(path){umask(0);int n = mkfifo(_path.c_str(), Mode);// 管道创建失败,打印失败信息if (n < 0){cerr << "mkfifo fail" << " ,errorno " << errno << " , errstring: " << strerror(errno) << endl;}else{cout<<"mkfifo sucess"<<endl;}}~fifo(){// 通信结束,删除管道int n = unlink(_path.c_str());if (n == 0){cout << "remove fifo file" << _path << " sucess " << endl;}else{cout << "remove fifo file" << _path << " fail " << endl;}}private:string _path;
};class Sync
{
public:Sync(): _wfd(-1), _rfd(-1){cout<<"Sync"<<endl;}~Sync(){if(_wfd != -1){close(_wfd);}if(_rfd != -1){close(_rfd);}}//以只读方式打开void openRead(){int rfd = open(path.c_str(),O_RDONLY);if(rfd < 0){cerr<<"openRead fail,errno is"<<errno<<"error string is"<<strerror(errno)<<endl;exit(1);}else {cout<<"openRead sucess"<<endl;}_rfd = rfd;}//以只写方式打开void openWrite(){   int wfd = open(path.c_str(),O_WRONLY);if(wfd < 0){cout<<"openWrite fail,errno is"<<errno<<" ,error string is"<<strerror(errno)<<endl;exit(1);}else {cout<<"openWrite sucess"<<endl;}_wfd = wfd;}bool wait(){char c = 0;ssize_t n = read(_rfd,&c,sizeof(c));if(n <= 0){cout<<"wait error"<<endl;return false;}return true;}void wakeUp(){char wake = 'A';ssize_t n = write(_wfd,&wake,sizeof(wake));if(n < 0){cout<<"wake up error"<<endl;}}private:int _wfd;int _rfd;
};#endif
//Common_shm.hpp
#pragma once
#include <iostream>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <cstring>
#include <stdlib.h>
#include <unistd.h>using namespace std;
#define SIZE 4096const char* pathName = "/home/yw";
const int proj_id = 0x6666;key_t getShmKey()
{key_t key = ftok(pathName,proj_id);if(key < 0){cerr<<"ftok error,errno: "<<errno<<", error string: "<<strerror(errno)<<endl;exit(-1);}return key;
}class shmIPC
{
public:shmIPC(key_t key):_key(key),_shmid(0),_size(SIZE),_address(nullptr){}~shmIPC(){//解除挂接int n = shmdt(_address);if(n < 0){exit(-1);cout<<"shm Deattach fail, errno: "<<errno<<" ,error: "<<strerror(errno)<<endl;}_address = nullptr;//删除共享内存n = shmctl(_shmid,IPC_RMID,nullptr);if(n < 0){exit(-1);cerr<<"shm del fail,errno: "<<errno<<" ,strerror: "<<strerror(errno)<<endl;}else{cout<<"shm del sucess"<<endl;}}void createShm(size_t size = SIZE){_size = size;int shmid = shmget(_key,_size,IPC_CREAT | IPC_EXCL);if(shmid < 0){cerr<<"shm create fail,errno: "<<errno<<" ,strerror: "<<strerror(errno)<<endl;exit(-1);}_shmid = shmid;}void getShm(){int shmid = shmget(_key,_size,IPC_CREAT);if(shmid < 0){cerr<<"shm get fail,errno: "<<errno<<" ,strerror: "<<strerror(errno)<<endl;exit(-1);   }_shmid = shmid;}void print(){struct shmid_ds shmds;int n = shmctl(_shmid,IPC_STAT,&shmds);if(n < 0){cerr<<"shm print fail,errno: "<<errno<<" ,strerror: "<<strerror(errno)<<endl;exit(-1);}cout<<"shmds.shm_segsz: "<<shmds.shm_segsz<<endl;cout<<"shmds.shm_nattch: "<<shmds.shm_nattch<<endl;cout<<"shmds.shm_ctime: "<<shmds.shm_ctime<<endl;cout<<"shmds.shm_perm.__key: "<<shmds.shm_perm.__key<<endl;cout<<"shmid: "<<_shmid<<endl;}void *shmAttach(){void *address = shmat(_shmid,nullptr,0);if(address == (void*) (-1)){cout<<"shm attach fail, errno: "<<errno<<" ,error: "<<strerror(errno)<<endl;exit(-1);}_address = address;return address;}private:key_t _key;int _shmid;size_t _size;void* _address;
};
//shmClient.cpp
#include "Common_shm.hpp"
#include "Common_fifo.hpp"int main()
{//0.创建监视对象Sync sync;sync.openRead();//1.创建对象shmIPC shmipc(getShmKey());//2.创建共享内存shmipc.createShm(SIZE);//3.挂接共享内存char *address = (char*) shmipc.shmAttach() ;cout<< address <<endl;//TO DOwhile(true){bool isWakeUp = sync.wait();if(isWakeUp)cout<<"shm content: "<<address<<endl;elsebreak;}return 0;
}
//Common_shmServer.cpp
#include "Common_shm.hpp"
#include "Common_fifo.hpp"int main()
{//0.创建监管对象fifo f1(path);Sync sync;sync.openWrite();   //1.创建对象shmIPC shmipc(getShmKey());//2.获取共享内存shmipc.getShm();//3.挂接共享内存char *address = (char*) shmipc.shmAttach() ;cout<< address <<endl;//TO DOint i=10;memset(address,0,SIZE);for(int i=0;i<10;i++){address[i] = i + 'A';sleep(2);sync.wakeUp();}return 0;
}

        在上面的代码中,shmServer每隔1s向共享内存中写入一个字符并且唤醒shmClient进程,shmClient进程每次唤醒打印共享内存中的数据。

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

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

相关文章

Linux网络 | 网络层IP报文解析、认识网段划分与IP地址

前言&#xff1a;本节内容为网络层。 主要讲解IP协议报文字段以及分离有效载荷。 另外&#xff0c; 本节也会带领友友认识一下IP地址的划分。 那么现在废话不多说&#xff0c; 开始我们的学习吧&#xff01;&#xff01; ps&#xff1a;本节正式进入网络层喽&#xff0c; 友友们…

SQLGlot:用SQLGlot解析SQL

几十年来&#xff0c;结构化查询语言&#xff08;SQL&#xff09;一直是与数据库交互的实际语言。在一段时间内&#xff0c;不同的数据库在支持通用SQL语法的同时演变出了不同的SQL风格&#xff0c;也就是方言。这可能是SQL被广泛采用和流行的原因之一。 SQL解析是解构SQL查询…

Windows程序设计10:文件指针及目录的创建与删除

文章目录 前言一、文件指针是什么&#xff1f;二、设置文件指针的位置&#xff1a;随机读写&#xff0c;SetFilePointer函数1.函数说明2.函数实例 三、 目录的创建CreateDirectory四、目录的删除RemoveDirectory总结 前言 Windows程序设计10&#xff1a;文件指针及目录的创建与…

线程互斥同步

前言&#xff1a; 简单回顾一下上文所学&#xff0c;上文我们最重要核心的工作就是介绍了我们线程自己的LWP和tid究竟是个什么&#xff0c;总结一句话&#xff0c;就是tid是用户视角下所认为的概念&#xff0c;因为在Linux系统中&#xff0c;从来没有线程这一说法&#xff0c;…

DRM系列七:Drm之CREATE_DUMB

本系列文章基于linux 5.15 DRM驱动的显存由GEM&#xff08;Graphics execution management&#xff09;管理。 一、创建流程 创建buf时&#xff0c;user层提供需要buf的width,height以及bpp(bite per pixel)&#xff0c;然后调用drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &…

我们信仰AI?从神明到人工智能——信任的进化

信任的进化&#xff1a; 信任是我们最宝贵的资产。而现在&#xff0c;它正像黑色星期五促销的廉价平板电视一样&#xff0c;被一点点拆解。在过去&#xff0c;世界很简单&#xff1a;人们相信晚间新闻、那些满是灰尘书籍的教授&#xff0c;或者手持病历、眉头紧锁的医生。而如…

数据分析系列--[11] RapidMiner,K-Means聚类分析(含数据集)

一、数据集 二、导入数据 三、K-Means聚类 数据说明:提供一组数据,含体重、胆固醇、性别。 分析目标:找到这组数据中需要治疗的群体供后续使用。 一、数据集 点击下载数据集 二、导入数据 三、K-Means聚类 Ending, congratulations, youre done.

1-刷力扣问题记录

25.1.19 1.size()和.length()有什么区别 2.result.push_back({nums[i], nums[left], nums[right]});为什么用大括号&#xff1f; 使用大括号 {} 是 C11 引入的 初始化列表 语法&#xff0c;它允许我们在构造或初始化对象时直接传入一组值。大括号的使用在许多情况下都能让代码…

神经网络参数量和运算量的计算- 基于deepspeed库和thop库函数

引言 最近需要对神经网络的参数量和运算量进行统计。找到一个基于deepspeed库函数计算参数量和运算量的例子。而我之前一直用thop库函数来计算。 看到有一篇勘误博文写道使用thops库得到的运算量是MACs (Multiply ACcumulate operations&#xff0c;乘加累积操作次数&#xf…

读书笔记--分布式架构的异步化和缓存技术原理及应用场景

本篇是在上一篇的基础上&#xff0c;主要对分布式应用架构下的异步化机制和缓存技术进行学习&#xff0c;主要记录和思考如下&#xff0c;供大家学习参考。大家知道原来传统的单一WAR应用中&#xff0c;由于所有数据都在同一个数据库中&#xff0c;因此事务问题一般借助数据库事…

无用知识研究:std::initializer_list的秘密

先说结论&#xff0c;用std::initializer_list初始化vector&#xff0c;内部逻辑是先生成了一个临时数组&#xff0c;进行了拷贝构造&#xff0c;然后用这个数组的起终指针初始化initializer_list。然后再用initializer_list对vector进行初始化&#xff0c;这个动作又触发了拷贝…

Jupyterlab和notebook修改文件的默认存放路径的方法

文章目录 1.缘由2.操作流程2.1找到默认的路径2.2创建配置文件2.3修改配置文件内容2.4注意事项 1.缘由 我自己使用jupyterlab的时候&#xff0c;打开是在这个浏览器上面打开的&#xff0c;但是这个打开的文件路径显示的是C盘上面路径&#xff0c;所以这个就很麻烦&#xff0c;因…

HarmonyOS:ArkWeb进程

ArkWeb是多进程模型,分为应用进程、Web渲染进程、Web GPU进程、Web孵化进程和Foundation进程。 说明 Web内核没有明确的内存大小申请约束,理论上可以无限大,直到被资源管理释放。 ArkWeb进程模型图 应用进程中Web相关线程(应用唯一) 应用进程为主进程。包含网络线程、Vi…

基于Spring Security 6的OAuth2 系列之九 - 授权服务器--token的获取

之所以想写这一系列&#xff0c;是因为之前工作过程中使用Spring Security OAuth2搭建了网关和授权服务器&#xff0c;但当时基于spring-boot 2.3.x&#xff0c;其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0&#xff0c;结果一看Spring Security也升级…

音标-- 02-- 重音 音节 变音

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 国际音标1.重音2.音节3.变音 国际音标 1.重音 2.音节 3.变音

Adaptive LLM Transformer²

看到了一个不错的论文https://arxiv.org/pdf/2501.06252 TRANSFORMER-SQUARED: SELF-ADAPTIVE LLMS 挺有意思的&#xff0c;是一家日本AI公司SakanaAI的论文&#xff08;我以前写过他们的不训练提升模型的能力的文章&#xff0c;感兴趣可以去翻&#xff09;它家有Lion Jones坐镇…

优化代码性能:利用CPU缓存原理

在计算机的世界里&#xff0c;有一场如同龟兔赛跑般的速度较量&#xff0c;主角便是 CPU 和内存 。龟兔赛跑的故事大家都耳熟能详&#xff0c;兔子速度飞快&#xff0c;乌龟则慢吞吞的。在计算机中&#xff0c;CPU 就如同那敏捷的兔子&#xff0c;拥有超高的运算速度&#xff0…

linux 函数 sem_init () 信号量、sem_destroy()

&#xff08;1&#xff09; &#xff08;2&#xff09; 代码举例&#xff1a; #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h>sem_t semaphore;void* thread_function(void* arg) …

分库分表技术方案选型

一、MyCat 官方网站&#xff0c;技术文档 MyCat是一款由阿里Cobar演变而来的用于支持数据库读写分离、分片的数据库中间件。它基于MySQL协议&#xff0c;实现了MySQL的协议和能力&#xff0c;并作为代理层位于应用和数据库之间&#xff0c;可以隐藏底层数据库的复杂性。 原理…

【智力测试——二分、前缀和、乘法逆元、组合计数】

题目 代码 #include <bits/stdc.h> using namespace std; using ll long long; const int mod 1e9 7; const int N 1e5 10; int r[N], c[N], f[2 * N]; int nr[N], nc[N], nn, nm; int cntr[N], cntc[N]; int n, m, t;void init(int n) {f[0] f[1] 1;for (int i …