前言

我们知道,在Linux中,进程是相互独立存在的,不存在直接让进程之间互相通信的方式。但是如果我们能让不同进程之间见到同一块内存,也就是都能读写这片区域是不是就能够达到进程间通信呢?

事实证明确实如此。在之前我们可以利用管道来实现出进程之间的通信。(匿名管道和命名管道)但是似乎在传输过程中都是通过操作系统之手(系统调用)来完成的。那么进程之间能否拥有一块属于自己管辖的共享的内存呢?这篇文章就是关于System V共享内存方式实现进程间通信的学习笔记~

『但是呢,我遇到了美丽的天使』

上一篇Linux学习笔记传送门!

【Linux】静动态库的制作和使用_柒海啦的博客-CSDN博客

目录

一、理解一个共享内存

二、共享内存相关接口

1.ftok创建唯一key

2.shmget获取共享内存

3.shmctl操作共享内存

4.shmat挂接共享内存

5.shmdt与共享内存去关联

三、简单demo


一、理解一个共享内存

顾名思义,共享内存就是进程之间可以共享的内存。

此内存就是操作系统开辟的一段内存,通过页表映射-到进程地址空间里的共享区,就可以让不同进程之间共享内存。释放的时候先去掉映射,然后释放掉即可。

其实,当不同进程之间挂接了共享内存后,此时ipc(Inter-Process Communication-进程间通信)是最快的,因为数据传输之间不在涉及内核,即不通过系统调用来传递彼此的信息。

共享内存同样是操作系统申请的,那么操作系统也需要对其进行管理(先描述在组织),所以对于共享内存的组成实际上就是:共享内存 = 共享内存块 + 共享内存的内核数据结构。内核数据结构存放相关信息,比如挂接的进程个数等等。

那么我们如何让操作系统创建共享内存,并且让不同进程挂接到同一个共享内存呢?

二、共享内存相关接口

1.ftok创建唯一key

类似于标识符一样。为了区分不同的共享内存段的名称,我们就需要一个独一无二的共享内存段名字。

(man 3 ftok)

函数原型:

key_t ftok(const char *pathname, int proj_id);

依赖头文件:

#include <sys/types.h>
        #include <sys/ipc.h>

参数:

pathname:给定一个现有的路径-必须有权限。(方便创建文件)

proj_id:大小不超过8bit的数字,即一个字节的内的数据(0 ~ 255)

返回值:

key_t:typedef int 类似于标识符key,用于下面获取共享内存的参数。如果返回值小于零表示创建失败。

注意:

不一定百分百创建成功,可以多试几次。

2.shmget获取共享内存

有了一个唯一的标识符后,我们可以利用此标识符通过系统调用获取一段可以供进程挂接的共享内存。获取可以是找现有的或者重新创建。一般在实际运用中服务端创建,客户端进行获取。

(man 2 shmget)

函数原型:

int shmget(key_t key, size_t size, int shmflg);

依赖头文件:

#include <sys/ipc.h>
        #include <sys/shm.h>

参数:

key:利用ftok算法函数生成的唯一标识符。

size:自定义共享内存的开辟大小,以字节为单位。(最好是页的大小(4KB)的整数倍)

shmflg:操作数。(一共由九个权限标志构成,用法和创建文件时使用的mode模式标志是一样的)

IPC_CREAT        单独如果创建共享,底层存在,会获取并且返回,不存在创建就返回。(Create key if key does not exist.)

IPC_EXCL        单独使用没有价值,和IPC_CREAT一起使用如果底层不存在创建并且返回。底层存在出错并且返回。(Fail if key exists.)

权限码(对文件的权限【Linux】权限管理_柒海啦的博客-CSDN博客)

0:取共享内存标识符,若不存在则函数会报错。

......

(至少包含 IPC_CREAT | 权限码)

返回值

成功就返回合法的标识符,用于表示此段共享内存的编号(类似于文件操作里的fd,这里就是shm+id),失败的话返回-1并且设置错误码。

注意:

开辟的内存空间大小之所以是页的整数倍是为了防止浪费。虽然在监视窗口(ipcs -m 可以查看共享内存资源ipcrm -m shmid可以删除对应的共享内存-命令操作)是你设定的那样,但是实际上操作系统是四舍五入的提供的内存,即比如你要了1个字节的共享内存,实际上操作系统分配的是一整页的内存,然后其余内存就没法用内存不就造成了资源的浪费嘛。

3.shmctl操作共享内存

通常,我们可以利用这个系统调用来释放掉我们所创建的共享内存。

(man 2 shmctl)

函数原型:

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

依赖头文件:

#include <sys/ipc.h>
        #include <sys/shm.h>

参数:

shmid:是shmget接口获取共享内存返回的id,表示操作此段共享内存。

cmd:操作数。

IPC_RMID        释放此共享内存(无论是否存在进程和其挂接)

......

buf:是指向shmid_ds strucc‐的指针,一般情况下我们无需关系,设置为nullptr即可。

返回值

成功操作返回0,失败返回-1。

注意:

通常情况下在服务端最后结束的时候使用此接口对创建了的共享内存进行删除,所以真正用到的参数也就两个,操作数也就只有一个。

4.shmat挂接共享内存

在获取到共享内存的id后,就要和本身进程地址空间进行一个挂接,获取到对应的地址,准备进程间通信。

(man 2 shmat)

函数原型:

void *shmat(int shmid, const void *shmaddr, int shmflg);

依赖头文件:

#include <sys/types.h>
        #include <sys/shm.h>

参数:

shmid:是shmget接口获取共享内存返回的id,表示操作此段共享内存。

shmaddr:一般是挂接的形式相关,一般不进行操作,设置为nullptr即可。

shmflg:操作数。

0:取共享内存标识符,若不存在则函数会报错。

.......

返回值

成功挂接上返回此共享内存的地址。挂接失败返回(void*)-1,并且设置错误码。

注意:

此接口的目的是和当前进程进行挂接,初学阶段不必了解挂接的形式相关,只需要挂接到即可,并且知道挂接成功返回的是共享内存的地址,就和平时我们操作的地址类似。

5.shmdt与共享内存去关联

在客户端(服务端可以不用去关联-最后会进行释放)最后我们需要将此进程和共享内存之间的连接断开就可以使用此系统接口。

(man 2 shmdt)

函数原型:

int shmdt(const void *shmaddr);

依赖头文件:
        #include <sys/types.h>
        #include <sys/shm.h>

参数:

shmaddr:共享内存地址。(可以是上面挂接shmat函数的返回的共享内存地址值)

返回值:

去关联成功返回0,失败返回-1,设置错误码。

注意:

共享内存和进程之间去关联函数。

三、简单demo

下面这段简单代码就演示简单的服务端创建共享内存,服务端、客户端挂接共享内存,服务端读取共享内存,客服端写入共享内存达成进程间通信。最后服务端和客户端去关联共享内存,服务端释放共享内存,结束。(关闭信号可以用quit进行识别)

利用上面介绍的接口就可以完成上述要求,快来试试吧~

//common.h
#pragma once#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <cstdio>
#include <string>
#include <cstring>#define PATH_NAME "/home/QiHai/code/2022/TestProject"
#define PROJ_ID 0x69  // 0 ~ 255 非零
#define SHMSIZE 4096  // 页的整数倍(4KB = 4 * 1024byte) - 否则就会造成空间上的浪费
//client.cpp
#include "common.h"
// 服务端
int main()
{// 首先生成秘钥key_t key = ftok(PATH_NAME, PROJ_ID);// std::cout << key << std::endl;// 创建共享内存umask(0);  // 掩码int shmid = shmget(key, SHMSIZE, IPC_CREAT | IPC_EXCL | 0666);if (shmid < 0){perror("shmget:");exit(1);}std::cout << "创建共享内存成功!" << std::endl;// 挂接到当前进程的共享区char* shmarr = (char*)shmat(shmid, nullptr, 0);  // 共享内存id 挂接形式... 操作if (*((int*)shmarr) < 0){perror("shmat:");exit(2);}std::cout << getpid() << "进程挂接共享内存成功!" << std::endl;// 进程通信 服务端进行循环读取,由于是直接访问内存,所以没有阻塞,需要配合管道食用更佳while (true){char* tmp = shmarr;if (strcmp(shmarr, "quit") == 0) break;printf("%s\n", tmp);sleep(1);}// 去关联 - 实际上服务端可以不用做这一步,因为服务端最后会将此共享内存给删除int s = shmdt(shmarr);if (s < 0){perror("shmdt:");exit(2);}std::cout << getpid() << "进程去关联成功!" << std::endl;// 删除共享内存int m = shmctl(shmid, IPC_RMID, nullptr);  // 共享内存id 操作(当前操作是无论有多少个进程挂接上都会删除掉) nullif (m < 0){perror("shmctl:");exit(3);}std::cout << "删除共享内存成功!" << std::endl;return 0;
}
//server.cpp
#include "common.h"
// 客户端
int main()
{// 首先生成秘钥key_t key = ftok(PATH_NAME, PROJ_ID);int shmid = shmget(key, SHMSIZE, 0);  // 客户端给0即可if (shmid < 0){perror("shmget:");exit(1);}// 挂接到当前进程的共享区char* shmarr = (char*)shmat(shmid, nullptr, 0);  // 共享内存id 挂接形式... 操作if (*((int*)shmarr) < 0){perror("shmat:");exit(2);}std::cout << getpid() << "进程挂接共享内存成功!" << std::endl;// 进程通信 客户端写即可std::string buffer;while (true){std::getline(std::cin, buffer);  // 等待键盘输入snprintf(shmarr, buffer.size() + 1, "%s", buffer.c_str());if (buffer == "quit") break;}// 去关联int s = shmdt(shmarr);if (s < 0){perror("shmdt:");exit(2);}std::cout << getpid() << "进程去关联成功!" << std::endl;return 0;
}

运行结果:

服务端会不断的读取客户端的输入数据。当然可以控制如果客户端不输入就不进行读入(利用管道的特性),那么中间就要加上管道操作就可以实现了。

【Linux】进程间通信-共享内存相关推荐

  1. 【Linux】Linux进程间通信——共享内存/消息队列/守护进程

    文章目录 进程间通信--共享内存/守护进程 一, 共享内存 1. 共享内存概念 2. 共享内存使用 1. 共享内存使用步骤 2. 共享内存操作函数 3. 共享内存常用操作命令 4. 共享内存使用示例: ...

  2. c++ fork 进程时 共享内存_c/c++ Linux 进程间通信------共享内存

    1. 什么是共享内存 共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区.进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是 ...

  3. Linux --进程间通信--共享内存

    一.共享内存 共享内存是最高效的通信方式,因为不需要一个进程先拷贝到内核,另一个进程在存内核中读取. 二. ipcs -m 查看共享内存 ipcrm -m 删除共享内存 三.主要函数 shmget 创 ...

  4. linux如何创建共享内存,linux实现共享内存同步的四种方法

    https://blog.csdn.net/sunxiaopengsun/article/details/79869115 本文主要对实现共享内存同步的四种方法进行了介绍. 共享内存是一种最为高效的进 ...

  5. Linux基础入门--进程间通信--共享内存

    Linux基础入门--进程间通信--共享内存 1.共享内存IPC原理 2.共享内存管理 1.共享内存IPC原理 共享内存进程间通信机制主要用于实现进程间大量的数据传输,共享内存是在内存单独开辟的一段内 ...

  6. Linux下进程间通信--共享内存:最快的进程间通信方式

    内存共享最新整理: Linux下进程间通信-共享内存 - 码到城攻共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式https://www.codecomeon.com/posts/109/ ...

  7. 操作系统实验报告6:进程间通信—共享内存

    操作系统实验报告6 实验内容 实验内容:进程间通信-共享内存. (1).验证:编译运行课件 Lecture 08 例程代码: Linux 系统调用示例 reader-writer 问题:Algorit ...

  8. linux查看共享内存max,浅析Linux的共享内存与tmpfs文件系统

    浅析Linux的共享内存与tmpfs文件系统 前言 共享内存主要用于进程间通信,Linux有两种共享内存(Shared Memory)机制: (1)** System V shared memory( ...

  9. 操作系统实验报告7:进程间通信—共享内存。实现一个带有n个单元的线性表的并发维护。

    操作系统实验报告7 实验内容 实验内容:进程间通信-共享内存.实现一个带有n个单元的线性表的并发维护. 建立一个足够大的共享内存空间(lock, M),逻辑值lock用来保证同一时间只有一个进程进入M ...

  10. C# 进程间通信(共享内存)

    原文:C# 进程间通信(共享内存) 进程间通信的方式有很多,常用的方式有: 1.共享内存(内存映射文件,共享内存DLL). 2.命名管道和匿名管道. 3.发送消息 本文是记录共享内存的方式进行进程间通 ...

最新文章

  1. 选购高清监控摄像机的十个技巧
  2. 《jQuery、jQuery UI及jQuery Mobile技巧与示例》——3.3 技巧:生成类名
  3. 构造函数必须是public吗_c++ 构造函数,析构函数必须要给成公有的吗?
  4. mds聚类matlab,MDS图示聚类结果
  5. JDK动态代理与CGLIB动态代理区别
  6. oracle字段重复新增错误,Oracle 判断表或字段是否存在新增/修改表结构可重复执行sql...
  7. 广告出价--如何使用PID控制广告投放成本
  8. gulp自动添加版本号
  9. Swift - 26 - 函数的基础写法
  10. 【渝粤教育】国家开放大学2018年秋季 0505-22T护理学基础 参考试题
  11. app调html页面,app界面管理(风格色调).html
  12. 【高等数学】一元函数积分表
  13. c++ 时间戳 转换成时间_区块链世界的公证人:时间戳
  14. UnityShader2:Shader与材质
  15. python 时间模块 -- time
  16. 最强升级系统的锚点连接
  17. Word 论文排版操作顺序
  18. 程序员工资到底有多高?国内哪个城市挣得最多?有没有你在的城市
  19. 墨盒和墨仓打印机区别?
  20. 塔望食业洞察|人参饮料行业环境 市场现状及发展思考

热门文章

  1. 苹果更新一半能取消吗_苹果股价最新行情-iPhone12一半用户选蓝色 苹果股价还会继续涨吗?...
  2. 串行、并行、并发,别再傻傻分不清了!
  3. 深圳关内主要旅游景点地址和公交路线
  4. java点到直线距离_求取点到直线的距离
  5. 转:石康 不奋斗的姑娘我不爱
  6. registration status: 204
  7. 陈旭数据科学与计算机学院,数据科学与计算机学院硕士研究生招生专业及各专业导师...
  8. 修改新网域名DNS服务器到DNSPOD解析服务
  9. 官方公布中国自行车排名十强辐轮王土拨鼠全世界碳纤维自行车品牌
  10. 2021.03.15 云题库