文章目录

  • 共享内存介绍
    • 最快的IPC形式
    • 共享内存示意图
    • 共享内存数据结构
    • 共享内存函数
      • shmget函数
        • shmfig
      • shmat函数
        • 说明:
      • shmdt函数
      • shmctl函数
    • 共享内存的原理
      • 小结
    • 共享内存的特点
      • 生命周期
      • 共享内存的大小
      • 共享内存为什么快
      • 共享内存没有任何的保护机制即同步互斥
      • 扩展内容
  • 代码编写(重点)
    • ftok介绍
      • 原型及参数介绍
      • 为什么要用
      • 与shmid的区别
    • makefile书写
    • comm.cpp(重点)
      • 条件编译以及头文件
      • toHex函数
      • getkey函数
      • creatShm以及getShm
      • attachShm,detachShm和delShm
      • 定义类进行封装
    • server.cc
    • client.cc
  • 代码总结

共享内存介绍

最快的IPC形式

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到
内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据

共享内存示意图

共享内存数据结构

shmid_ds中的内容可以通过系统函数获得(后面会讲):

struct shmid_ds {struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};

共享内存函数

shmget函数

功能:用来创建共享内存
原型:int shmget(key_t key, size_t size, int shmflg);
参数:

key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的

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

shmfig

IPC_CREAT and IPC_EXCL
单独使用IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回
IPC_EXCL不能单独使用,一般都要配合IPC_CREAT
IPC_CREAT | IPC_EXCL: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 – 如果创建成功,对应的shm,一定是最新的!

shmat函数

功能:将共享内存段连接到进程地址空间
原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:

shmid: 共享内存标识
shmaddr:指定连接的地址->设为nullptr即为随即指定
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函数

功能:用于控制共享内存
原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:

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

返回值:成功返回0;失败返回-1

共享内存的原理

先创建出一块共享的物理内存,然后通过页表各自映射到两个进程。->让不同的进程看到同一份资源! 内存块->共享内存
(之前我们是通过看到同一个文件来进行通信的,这是通过看到同一个内存块进行通信的)
如何取消呢?:
进程AB分别修改页表,去掉映射关系,释放共享内存块即可

小结

  1. 创建
  2. 关联进程和取消关联
  3. 释放共享内存块

共享内存的特点

生命周期

共享内存的生命周期不随进程,而是随整个操作系统
我们只能通过指令删除或者使用系统调用接口进行删除

共享内存的大小

共享内存的大小是以PAGE页(4kb)为单位的
![[Pasted image 20230418185622.png]]
OS分给你8kb,但是我只能用4097->可以防止访问越界

共享内存为什么快

我们在通信的时候,没有使用任何的接口,一旦共享内存映射到进程的地址空间,该共享内存就直接被所有的进程直接看到了
因为共享内存的这种特性,可以让进程通信的时候,减少拷贝的次数,因此是最快的

共享内存没有任何的保护机制即同步互斥

因为管道是通过系统接口通信的(里面可能会有保护机制),而共享内存直接通信

扩展内容

试着改一下代码:(将在之后的博客中书写)

  1. 让我client写完后,才通知server读取.刚开始的时候一定先让client先运行 一个管道
  2. 将命名管道带进来
  3. client写完后,才通知server读取,读取完了,才让client进行写入 两个管道

代码编写(重点)

ftok介绍

原型及参数介绍

函数原型:key_t ftok(const char* pathname,int proj_id);
其中,pathname是路径字符串,proj_id是项目id
这两个参数都可以随便书写,没有任何意义,只不过要保证不同的shm空间,这两个参数一定是不完全相同的.

为什么要用

我们要保证每一个共享内存都有唯一的一个编号,且server端和client端都可以接收得到同一个key值来访问同一块共享内存(即实现我们所谓的"不同的进程看到同一份资源"),这个编号便可由ftok函数产生.

与shmid的区别

在我们使用shmget成功后,会返回一个标识码,我们把它称之为"shmid",那么shmid不也是唯一的吗?和我们的key有什么区别呢?

其实他们俩的区别和inode编号与文件fd的区别差不多
对于shm的未来的所有操作,在用户层阶段,都是使用的 shmid
只有在系统内核,才是key值–>key本质是内核中使用的
所以我们一般用户只需要关注shmid即可

makefile书写

我们这里需要写两个文件:client.ccserver.cc
所以需要用all来依赖这两个文件,到时候只需要make all便可以同时完成两个文件的编译

.PHONY:all
all:shmclient shmservershmclient:client.ccg++ -o $@ $^ -std=c++11
shmserver:server.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f shmserver shmclient
//make all/make 都完成编译(make默认的是代码的开头)
//make clean会删除这两个文件

comm.cpp(重点)

条件编译以及头文件

#ifndef __COMM_HPP__ //如果没有定义这个宏,则才执行以下的编译,防止重复编译
#define __COMM_HPP__
#include <iostream>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>#define PATHNAME "."//当前路径
#define PROJID 0x6666//随便写的一个数using namespace std;const int gsize = 4096; //暂时//函数调用接口,接下来一个个地写#endif

toHex函数

为了方便调试,我们写一个函数将十进制数字才换成十六进制:

string toHex(int x)
{char buffer[64];snprintf(buffer,sizeof(buffer),"0x%x",x);//snprintf函数可以将字符串格式化输出到buffer中//格式为0x
}

getkey函数

上文我们提到过,需要使用ftok函数来得到一个唯一的key值,作为shmget的参数

key_t getKey()
{key_t k = ftok(PATHNAME,PROJID);if(k == -1)//差错判断{cerr << "error: " << errno << ":" << strerror(errno) << endl;//errno会存储一个错误编号,可以痛过strerror解码出来exit(1);}return k;
}

creatShm以及getShm

//使用这个辅助函数的话,我们就不需要为client和server分别书写一份相同的代码了
//只需要让他们俩传入不同的flag即可
static int createShmHelper(key_t k,int size,int flag)
{int shmid = shmget(k,size,flag);if(shmid == -1){cerr << "error: " << errno << ":" << strerror(errno) << endl;exit(2);}return shmid;
}int creatShm(key_t k,int size)
{umask(0)//将该进程的掩码设置为0return createShmHelper(k,size,IPC_CREAT | IPC_EXCL | 0666);
}
int getShm(key_t k,int size)
{return createShmHelper(k,size,IPC_CREAT);
}

attachShm,detachShm和delShm

我们已经创建出了共享内存这块空间了,需要我们将它写入我们的链表中,即attach

char* attachShm(int shmid)
{char* start = (char*)shmat(shmid,nullptr,0);return start;
}
void detachShm(char* start)
{int n = shmdt(start);assert(n!=-1);(void)n;
}
void delShm(int shmid)//使用shmctl接口
{int n = shmctl(shmid, IPC_RMID, nullptr);assert(n != -1);(void)n;
}

定义类进行封装

#define SERVER 1
#define CLIENT 0class Init
{public:Init(int t):type(t){key_k k = get_key();if(type == SERVER){shmid = createShm(k,gsize);}else{shmid = getShm(k,gsize);}start = attach(shmid);}char *getStart(){ return start; }~Init(){detachShm(start);if(type == SERVER) delShm(shmid);}private:char* start;int type;//server or clientint shmid;
};

server.cc

#include "comm.hpp"
#include <unistd.h>int main()
{Init init(SERVER);char *start = init.getStart();int n = 0;while(n <= 30){cout <<"client -> server# "<< start << endl;sleep(1);n++;}return 0;
}

client.cc

#include "comm.hpp"
#include <unistd.h>int main()
{Init init(CLIENT);char *start = init.getStart();char c = 'A';while(c <= 'Z'){start[c-'A'] = c;c++;start[c] = '\0';sleep(1);}return 0;
}

代码总结

makefile

.PHONY:all
all:shmclient shmservershmclient:client.ccg++ -o $@ $^ -std=c++11
shmserver:server.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f shmserver shmclient

comm.cpp

#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>using namespace std;//  IPC_CREAT and IPC_EXCL
// 单独使用IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回
// IPC_EXCL不能单独使用,一般都要配合IPC_CREAT
// IPC_CREAT | IPC_EXCL: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 -- 如果创建成功,对应的shm,一定是最新的!#define PATHNAME "."
#define PROJID 0x6666// 共享内存的大小是以PAGE页(4KB)为单位的
const int gsize = 4096; //暂时key_t getKey()
{key_t k = ftok(PATHNAME, PROJID);if(k == -1){cerr << "error: " << errno << " : " << strerror(errno) << endl;exit(1);}return k;
}string toHex(int x)
{char buffer[64];snprintf(buffer, sizeof buffer, "0x%x", x);return buffer;
}static int createShmHelper(key_t k, int size, int flag)
{int shmid = shmget(k, gsize, flag);if(shmid == -1){cerr << "error: " << errno << " : " << strerror(errno) << endl;exit(2);}return shmid;
}int createShm(key_t k, int size)
{umask(0);return createShmHelper(k, size, IPC_CREAT | IPC_EXCL | 0666);
}int getShm(key_t k, int size)
{return createShmHelper(k, size, IPC_CREAT);
}char* attachShm(int shmid)
{char *start = (char*)shmat(shmid, nullptr, 0);return start;
}void detachShm(char *start)
{int n = shmdt(start);assert(n != -1);(void)n;
}void delShm(int shmid)
{int n = shmctl(shmid, IPC_RMID, nullptr);assert(n != -1);(void)n;
}#define SERVER 1
#define CLIENT 0class Init
{public:Init(int t):type(t){key_t k = getKey();if(type == SERVER) shmid = createShm(k, gsize);else shmid = getShm(k, gsize);start = attachShm(shmid);}char *getStart(){ return start; }~Init(){detachShm(start);if(type == SERVER) delShm(shmid);}
private:char *start;int type; //server or clientint shmid;
};#endif

client.cc

#include "comm.hpp"
#include <unistd.h>int main()
{Init init(CLIENT);// start 就已经执行了共享内存的起始空间char *start = init.getStart();char c = 'A';while(c <= 'Z'){start[c - 'A'] = c;c++;start[c - 'A'] = '\0';sleep(1);}// key_t k = getKey();// cout << "client key: " << toHex(k) << endl;// int shmid = getShm(k, gsize);// cout << "client shmid: " << shmid << endl;// //3. 将自己和共享内存关联起来// char* start = attachShm(shmid);// sleep(15);// // 4.  将自己和共享内存去关联// detachShm(start);return 0;
}

server.cc

#include "comm.hpp"
#include <unistd.h>int main()
{Init init(SERVER);// start 就已经执行了共享内存的起始空间char *start = init.getStart();int n = 0;// 我们在通信的时候,没有使用任何接口?一旦共享内存映射到进程的地址空间,该共享内存就直接被所有的进程 直接看到了!// 因为共享内存的这种特性,可以让进程通信的时候,减少拷贝次数,所以共享内存是所有进程间通信,速度最快的// 共享内存没有任何的保护机制(同步互斥) -- 为什么?管道通过系统接口通信,共享内存直接通信while(n <= 30){cout <<"client -> server# "<< start << endl;sleep(1);n++;}// 扩展内容:// 1. client写完了,才通知让server读取。刚开始,一定先让client运行 一个管道// 2. 命名管道带进来 // 3. client写完了,才通知让server读取.读取完了,才让client进行写入 两个管道// //1. 创建key// key_t k = getKey();// cout << "server key: " << toHex(k) << endl;// //2. 创建共享内存// int shmid = createShm(k, gsize);// cout << "server shmid: " << shmid << endl;// sleep(3);// //3. 将自己和共享内存关联起来// char* start = attachShm(shmid);// sleep(20);// // 通信代码在这里!// // 4.  将自己和共享内存去关联// detachShm(start);// sleep(3);// struct shmid_ds ds;// int n = shmctl(shmid, IPC_STAT, &ds);// if(n != -1)// {//     cout << "perm: " << toHex(ds.shm_perm.__key) << endl;//     cout << "creater pid: " << ds.shm_cpid  << " : " << getpid() << endl;// }// ?. 删除共享内存//delShm(shmid);return 0;
}

【Linux】共享内存(shm)代码实现相关推荐

  1. 进程间通信之共享内存SHM

    文章目录 1. system-v IPC简介 2. 函数ftok()函数介绍 3. 共享内存SHM介绍 4. 共享内存SHM相关接口函数 5. 共享内存SHM代码示例 1. system-v IPC简 ...

  2. c++ 共享内存_关于Linux共享内存的实验 [二] - 原因

    关于Linux共享内存的实验 [一] 上文采用的"删文件"和"杀进程"的方法主要是为了快速演示实验现象,但这种做法不利于通过调试手段进一步探究其内在的逻辑.为此 ...

  3. linux 共享内存操作(shm_open、mmap、编译链接库:-lz -lrt -lm -lc都是什么库)

    文章目录 linux 共享内存操作(shm_open) 一.背景 二.函数使用说明 shm_open ftruncate(改变文件大小) mmap共享内存 三.示例代码 创建内存共享文件并写入数据 打 ...

  4. linux的共享内存,linux共享内存实际在哪里?

    我只想知道共享内存驻留在Linux系统中的位置?它在物理内存还是虚拟内存中?linux共享内存实际在哪里? 我知道有关进程的虚拟内存发送信箱,他们从不同的工艺处理和流程没有看到对方的记忆,但我们可以利 ...

  5. Linux共享内存(二)

    Linux共享内存编程实例 原文链接:http://blog.csdn.net/pcliuguangtao/article/details/6526119 /*共享内存允许两个或多个进程进程共享同一块 ...

  6. 【Linux共享内存】

    Linux共享内存 一.基本概念 二.常用函数 1. shm_open 2. mmap 3. munmap 4. shm_unlink 5. ftruncate 三.使用示例 四.share内存不足解 ...

  7. 进程间通信(IPC)之内存映射mmap和共享内存shm

    一.共享内存shm 1 概念:多个进程的地址空间都映射到同一块物理内存,这样多个进程都能看到这块物理内存,实现进程间通信,而且不需要数据的拷贝,所以速度最快. 二.内存映射mmap 1 前言:先介绍一 ...

  8. Linux——进程间通信(共享内存shm)笔记

    文章目录 前言 一.共享内存的通信原理 二.共享内存函数 1.共享内存实现步骤 2.函数的说明 1.shmget( )函数 2.shmat( )函数 3.shmdt( )函数 三.代码示例: 执行结果 ...

  9. Linux进程间通信:共享内存(shm)

    目录 ★ key值说明 ★ shmget函数 ★ shmat函数 ★ shmdt函数 ★ shmctl函数 ★ 操作说明 ★ IPC相关指令 简介:共享内存指 (shared memory)在多处理器 ...

  10. Linux 18 IPC之共享内存shm

    一.原理 共享内存是三个IPC(Inter-Process Communication)机制中的一个. 它允许两个不相关的进程访问同一个逻辑内存. 共享内存是在两个正在进行的进程之间传递数据的一种非常 ...

最新文章

  1. mysql 行列转换 动态_mysql 行列动态转换的实现(列联表,交叉表)
  2. 信息学奥赛一本通 1154:亲和数
  3. 小白学深度之LSTM长短期记忆神经网络——深度AI科普团队
  4. 宝藏世界显示连接不到服务器,宝藏世界新手常见问题解答 新手攻略
  5. 很感人的爱情故事——世界上最遥远的距离
  6. ln: 创建符号链接 “include/asm”: 不支持的操作
  7. C++冒泡排序(初级版)
  8. 服务器安装找不到lsi驱动,IBM 服务器 SAS Raid LSI Windows2008 硬盘 驱动
  9. Mysql-DQL基础查询
  10. 修复损坏图片的c语言,免费修复损坏的JPEG照片和图像
  11. SecureCRT背景颜色
  12. 机器人自动驾驶中的时间同步
  13. 基于JAVA校园快递联盟系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署
  14. 什么是SDK?MFC?
  15. 使用小马哥win10 激活工具激活后, 桌面上经常出现一个广告快捷方式的 解决方法...
  16. 【杂谈】ChatGPT是否可以取代人类的工作
  17. vue 分页添加序号
  18. Stata:内生变量的交乘项如何处理?
  19. Wide Deep
  20. 什么语言能替代HTML,替代HTML的下一代WEB标记语言XHTML

热门文章

  1. 山东专升本计算机网络(二)
  2. “互联网+”时代 母婴行业像叫外卖一样“叫母婴用品”
  3. 鐘明系列十:『32阶3次幻方』
  4. 什么是骨传导耳机,骨传导耳机原理
  5. java freemark导出word (模板、单张图片、多张图片源码)
  6. 1.2 极限的性质【极限】
  7. Box of CSS3
  8. zabbix mysql 表分区_5-Zabbix系统MySQL数据库分区表的设定--精简说明
  9. YANG语言标准中文
  10. Sentinel-5P数据介绍与预处理