进程间通信之共享内存及其shm函数的使用【Linux】

2024-01-07 16:44

本文主要是介绍进程间通信之共享内存及其shm函数的使用【Linux】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

进程间通信之共享内存及其shm函数的使用

  • 什么是共享内存
    • 共享内存的内核数据结构
  • 如何实现共享内存
    • 共享内存函数
      • shmget函数
        • ftok函数
      • shmat函数
      • shmdt函数
      • shmctl函数
  • 代码实现

什么是共享内存

  共享内存区是最快的IPC(Inter-Process Communication,进程间通信)形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据.
在这里插入图片描述
  匿名管道和命名管道都需要通过系统调用接口来实现进程间通信,因为管道属于文件,属于内核中的一种特殊数据结构,由OSweihu,所以只能使用系统调用。而共享内存存在堆栈之间,可以直接读写。双方进程若要进行通信,直接进行内存级的读写即可。

从下图可以更加直观的看到在内核照中的运行方式:

在这里插入图片描述

  • 共享内存的提供者是操作系统。
  • 共享内存缺乏访问控制,用户(进程)双方都不会影响对方,甚至不知道对方的存在,容易造成接受的数据是不完整的。
  • 共享内存 = 共享内存块 + 对应的共享内存的内核数据结构

共享内存的内核数据结构

在这里插入图片描述

如何实现共享内存

共享内存函数

shmget函数

功能:用来创建共享内存
原型: int shmget(key_t key, size_t size, int shmflg);
参数:
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

  参数key要保证在系统中的唯一性,key值相同,就是同一块共享内存,保证key的唯一性可以使用ftok函数实现:

ftok函数

所需要的头文件:
#include <sys/types.h>
#include <sys/ipc.h>
函数原型:
key_t ftok(const char *pathname, int proj_id);
参数:
pathname:指定的文件,此文件必须存在且可存取
proj_id:计划代号(project ID),可以在[0, 255]中随机取一个数字。
函数返回值

  • 成功:返回key_t值(即IPC 键值)
  • 出错:-1,错误原因存于error中
    ftok函数把从pathname导出的信息与id的低序8位组合成一个整数IPC键(也就是shmget函数中需要的key值)。

  注意:如果在使用ftok的这段时间里,pathname指向的文件或者目录被删除而且又重新创建,ftok是能够成功返回的,但是由于inode可能不同了,返回值也就不同于所希望的值,由于key值相同,使用同一块共享内存,这时候就可能会导致key值不同,双方无法共享一块内存,所以这个pathname一定要是一个不会被修改的文件路径。


  对于shmget的第二个参数size,代表这个共享内存的大小,最好是页(PAGE:4096byte)的整数倍,(OS和磁盘进行I/O操作的基本单位也是4096byte),如果设置为4097byte,虽然只是多了一个字节,但是系统底层可能会创建4096 * 2的空间,即使这样,也只能使用自己申请的大小(4097byte),不能使用4096*2的空间。

第三个参数shmflg:

  • 该参数用于确定共享内存属性。
  • 使用上为:标志位 | 内存权限
  • 标志位参数有两种:IPC_CREAT、IPC_EXCL
  • 使用TPC_CREAT | 0666(内存权限):创建共享内存若底层存在,就获取,并返回,若不存在,就新建,并返回。
  • 使用TPC_CREAT | IPC_EXCL | 0666(内存权限):创建共享内存若底层存在,则出错返回。若不存在,则新建,并返回。所以返回的一定是一个全新的shm.

shmat函数

功能:将共享内存段连接到进程地址空间
原型: void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

使用这个函数建立物理内存和虚拟内存的映射。
shmaddr设置为nullptr表示让内核自己确定位置。
shmflg设置为0为默认方式。
使用shmat函数可以简单的这样:
char* shmaddr = (char*)shmat(shmid, nullptr, 0);

  • shmaddrNULL,核心自动选择一个地址
  • shmaddr不为NULLshmflgSHM_RND标记,则以shmaddr为连接地址。
  • shmaddr不为NULLshmflg设置了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

 cmd的三个命令:

命令说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID删除共享内存段

shmctl常用IPC_RMID,用于删除共享内存空间。

代码实现

//server.cpp
// 由server再共享内存里面读取信息#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include <cstdlib>
const int SIZE = 4094;
int main()
{key_t key = ftok(".", 113);if (key < 0){perror("ftok");exit(1);}int shmid = shmget(key, SIZE, IPC_CREAT | 0666);if (shmid < 0){perror("shmget");exit(2);}char *addr = (char *)shmat(shmid, NULL, 0);int i = 0;while (i < 20){i++;printf("client# %s\n", addr);fflush(stdout);memset(addr, 0, sizeof(addr)); // 每次写入都清空,保证共享内存中没有上一次的数据残留。sleep(1);}shmdt(addr);shmctl(shmid, IPC_RMID, NULL);return 0;
}
// client.cpp
// 由client再共享内存里面发送信息#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>
#include <string.h>
#include <unistd.h>
#include <cstdlib>
const int SIZE = 4094;
int main()
{key_t key = ftok(".", 113);if (key < 0){perror("ftok");exit(1);}int shmid = shmget(key, SIZE, IPC_CREAT | 0666);if (shmid < 0){perror("shmget");exit(2);}char *addr = (char *)shmat(shmid, NULL, 0);int i = 0;while (i < 15){fgets(addr, 100, stdin);fflush(stdin);sleep(1);i++;}shmdt(addr);return 0;
}
// makeflie
.PHONY:all
all:server clientserver:server.cppg++ -o $@ $^client:client.cppg++ -o $@ $^.PHONY:clean
clean:rm -f server client

  上面的代码是实现一个由客户端进行在终端上进行写入信息到共享内存,服务端从共享内存读出到终端打印到屏幕上的一个过程。


    😄 创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看😄

  • List item

这篇关于进程间通信之共享内存及其shm函数的使用【Linux】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/580589

相关文章

Mybatis对MySQL if 函数的不支持问题解读

《Mybatis对MySQLif函数的不支持问题解读》接手项目后,为了实现多租户功能,引入了Mybatis-plus,发现之前运行正常的SQL语句报错,原因是Mybatis不支持MySQL的if函... 目录MyBATis对mysql if 函数的不支持问题描述经过查询网上搜索资料找到原因解决方案总结Myb

Mysql中RelayLog中继日志的使用

《Mysql中RelayLog中继日志的使用》MySQLRelayLog中继日志是主从复制架构中的核心组件,负责将从主库获取的Binlog事件暂存并应用到从库,本文就来详细的介绍一下RelayLog中... 目录一、什么是 Relay Log(中继日志)二、Relay Log 的工作流程三、Relay Lo

使用Redis实现会话管理的示例代码

《使用Redis实现会话管理的示例代码》文章介绍了如何使用Redis实现会话管理,包括会话的创建、读取、更新和删除操作,通过设置会话超时时间并重置,可以确保会话在用户持续活动期间不会过期,此外,展示了... 目录1. 会话管理的基本概念2. 使用Redis实现会话管理2.1 引入依赖2.2 会话管理基本操作

Springboot请求和响应相关注解及使用场景分析

《Springboot请求和响应相关注解及使用场景分析》本文介绍了SpringBoot中用于处理HTTP请求和构建HTTP响应的常用注解,包括@RequestMapping、@RequestParam... 目录1. 请求处理注解@RequestMapping@GetMapping, @PostMappin

springboot3.x使用@NacosValue无法获取配置信息的解决过程

《springboot3.x使用@NacosValue无法获取配置信息的解决过程》在SpringBoot3.x中升级Nacos依赖后,使用@NacosValue无法动态获取配置,通过引入SpringC... 目录一、python问题描述二、解决方案总结一、问题描述springboot从2android.x

SpringBoot整合AOP及使用案例实战

《SpringBoot整合AOP及使用案例实战》本文详细介绍了SpringAOP中的切入点表达式,重点讲解了execution表达式的语法和用法,通过案例实战,展示了AOP的基本使用、结合自定义注解以... 目录一、 引入依赖二、切入点表达式详解三、案例实战1. AOP基本使用2. AOP结合自定义注解3.

Python中Request的安装以及简单的使用方法图文教程

《Python中Request的安装以及简单的使用方法图文教程》python里的request库经常被用于进行网络爬虫,想要学习网络爬虫的同学必须得安装request这个第三方库,:本文主要介绍P... 目录1.Requests 安装cmd 窗口安装为pycharm安装在pycharm设置中为项目安装req

Python容器转换与共有函数举例详解

《Python容器转换与共有函数举例详解》Python容器是Python编程语言中非常基础且重要的概念,它们提供了数据的存储和组织方式,下面:本文主要介绍Python容器转换与共有函数的相关资料,... 目录python容器转换与共有函数详解一、容器类型概览二、容器类型转换1. 基本容器转换2. 高级转换示

使用Python将PDF表格自动提取并写入Word文档表格

《使用Python将PDF表格自动提取并写入Word文档表格》在实际办公与数据处理场景中,PDF文件里的表格往往无法直接复制到Word中,本文将介绍如何使用Python从PDF文件中提取表格数据,并将... 目录引言1. 加载 PDF 文件并准备 Word 文档2. 提取 PDF 表格并创建 Word 表格

使用Python实现局域网远程监控电脑屏幕的方法

《使用Python实现局域网远程监控电脑屏幕的方法》文章介绍了两种使用Python在局域网内实现远程监控电脑屏幕的方法,方法一使用mss和socket,方法二使用PyAutoGUI和Flask,每种方... 目录方法一:使用mss和socket实现屏幕共享服务端(被监控端)客户端(监控端)方法二:使用PyA