【hello Linux】进程间通信——共享内存

目录

前言:

1. System V共享内存

1. 共享内存的理解

2. 共享内存的使用步骤

3. 共享内存的使用

        1. 共享内存的创建

        查看共享内存

        2. 共享内存的释放

        3. 共享内存的挂接

        4. 共享内存的去挂接

4. 共享内存的使用示例

1. 两进程挂接与去挂接演示:

2. 两进程通信演示;


Linux🌷

前言:

在之前写的进程间通信的两篇博客中,不管是匿名管道通信还是命名管道通信都是基于文件的一种通信方式;

而今天我们要学习另一种通信方式:System V标准下的 “共享内存”;

在这里简单介绍下System V标准,System V标准是计算机学术界的一些领头人物站在OS层面,专门为进程间通信设计的方案;

System V提供了三个主流方案:

1. 共享内存——也就是今天博客的主要内容;

2. 消息队列;

3. 信号量;

其中共享内存和消息队列是为了传输数据设计的,信号量是为了保证进程间的同步和互斥设计的;

下面开始我们的正文!🔮


1. System V共享内存

1. 共享内存的理解

共享内存是一种允许两个或多个进程访问同一块物理内存的通信机制;

用一张图更好的理解下:

其本质就是先在物理内存中开辟一块空间,然后让相互通信的进程通过页表映射将这块共享空间映射到进程的虚拟地址空间中,因此两个进程便看到了同一份资源,便可以进行进程间通信了,物理内存中开辟的空间就是我们的共享内存;

OS中可能存在多对使用共享内存进行通信的进程,共享内存也自然不止一个,OS也当然要将共享内存进行管理,管理方法——“先描述,在组织”

下面看一下共享内存的数据结构:

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 */
};

我们申请一块共享内存之后 为了保证这块共享内存的唯一性 我们必须要对它做个标识

我们在 linux 系统中我们使用 key 值来唯一标识一块共享内存

在我们的 shmid_ds 结构体的第一行有一个叫做 shm_perm 的结构体 它的类型是 ipc_perm 它的结构体定义如下

struct ipc_perm{
	__kernel_key_t  key;
	__kernel_uid_t  uid;
	__kernel_gid_t  gid;
	__kernel_uid_t  cuid;
	__kernel_gid_t  cgid;
	__kernel_mode_t mode;
	unsigned short  seq;
};

我们看它的第一行有这么一段描述 __kernel_key_t key;

而这个key就是唯一标识共享内存的key值

2. 共享内存的使用步骤

1. 共享内存的创建(开辟共享内存);

2. 共享内存的挂接(在页表中建立共享内存的映射关系);

3. 共享内存的去挂接(在页表中去掉共享内存的映射关系);

4. 共享内存的释放;

3. 共享内存的使用

1. 共享内存的创建

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

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

key:共享内存段的唯一标识,用户可使用如下函数进行key的自动生成

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

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

pathname:自定义的路径名;

proj_id:自定义的项目ID;

返回值:成功返回一个非负整数,失败返回-1;

只要pathname和proj_id是一样的,那么生成的key便是一样的,因此两个进程便可以识别同一共享内存 ;

@size:共享内存的大小,建议是4KB的整数倍。

因为OS在分配内存资源时,是以页面为单位进行分配的,页面的大小是4KB,虽然是这样分配的,但是申请多少还是给你多少,为了避免给用户造成一些错误;

@shmflg:由九个权限标志组成,它们的用法和创建文件时使用的mode模式标志是一样的。

我们一般有如下两种用法:

1. IPC_CREAT共享内存不存在时,创建一个共享内存并返回,如果创建的共享内存已经存在的话,则直接返回当前已经存在的共享内存;

2. IPC_CREAT | IPC_EXCL:如果共享内存不存在则创建并返回,如果共享内存已经存在则返回出错,也就是说如果调用成功,得到的一定是一个最新的,没有被别人使用的共享内存!IPC_EXCL单独使用是没有意义的,

3. 共享内存也是有权限的,我们还可以在上述基础上(|或权限),达到共享内存权限的设定;

@返回值:创建成功返回一个非负整数,即该共享内存段的标识码,失败返回-1;
示例:
#include <stdio.h>    
#include <sys/ipc.h>    
#include <sys/shm.h>    
    
#define PATH_NAME "./test.c"    
#define PROJ_ID 0x666    
#define SIZE 4097    
    
int main()    
{    
  //生成key    
  key_t key = ftok(PATH_NAME,PROJ_ID);    
  if(key<0)    
  {    
    perror("ftok fail\n");    
    return 1;    
  }    
    
  //创建共享内存                                                          
  int shmid = shmget(key,SIZE,IPC_CREAT|IPC_EXCL);    
  if(shmid<0)              
  {                             
    perror("shmget fail\n");    
    return 2;              
  }                        
                                             
  printf("key:%u ,shmid:%d\n",key,shmid);    
  return 0;                
}      
结果演示:

查看共享内存

我们可以使用 ipcs 命令查看系统中的进程间通信资源的信息;

如果我们只想知道三种通信方式其中一种的通信信息的话,我们可以在 ipcs 后面携带选项:

-q:列出消息队列相关信息;

-m:列出共享内存相关信息;

-s:列出信号量相关信息;

 其中它的每列信息含义如下:

命名含义
key系统区别各个共享内存的唯一标识
shmid共享内存的用户层id
owner共享内存的拥有者
perms共享内存的权限
bytes共享内存的大小
nattch挂接共享内存的进程数
status共享内存的状态

key和shmid的区别:

我们的key是系统中是被共享内存的唯一标识 而shmid是我们用户层识别共享内存的标识

这就好像是inode和文件名的区别

系统中使用inode来标识一个文件 而我们使用文件名来标识一个文件

2. 共享内存的释放

共享内存的生命周期是随内核的 就算进程结束了共享内存也不会被释放

如果再次运行test.c程序 我们就会发现这样子的错误

想要释放共享内存只有两种方式

1. 关机重启

2. 主动进行释放

使用命令方式释放共享内存:

我们可以使用 ipcrm -m shmid命令释放指定id的共享内存资源

使用函数方式释放共享内存:

我们一般使用shmctl函数来控制共享内存 它的函数原型如下

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

shmid:用户层面标识唯一一个共享内存;

cmd:具体的控制命令;

buf:获取或设置所控制共享内存的数据结构(一般设为NULL);

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

其中关于 cmd 参数 常用的命令有以下几个

选项作用
IPC_STAT获取共享内存的当前关联值 此时参数buf作为输出型参数
IPC_SET在进程有足够权限的前提下 将共享内存的当前关联值设置为buf所指的数据结构中的值
IPC_RMID删除共享内存段

将上述代码修改下,继续运行:

#include <stdio.h>    
#include <sys/ipc.h>    
#include <sys/shm.h>    
    
#define PATH_NAME "./test.c"    
#define PROJ_ID 0x666    
#define SIZE 4097    
    
int main()    
{    
  //生成key    
  key_t key = ftok(PATH_NAME,PROJ_ID);    
  if(key<0)    
  {    
    perror("ftok fail\n");    
    return 1;    
  }    
    
  //创建共享内存    
  int shmid = shmget(key,SIZE,IPC_CREAT|IPC_EXCL);    
  if(shmid<0)    
  {    
    perror("shmget fail\n");    
    return 2;    
  }                                                                                           
    
  printf("key:%x ,shmid:%d\n",key,shmid);    
    
  //释放共享内存    
  shmctl(shmid,IPC_RMID,NULL);    
  printf("delete success!\n");    
  return 0;    
}    

 我们可以看到 共享内存 被释放了

3. 共享内存的挂接

我们一般使用 shmat函数来进行共享内存的挂接

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

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

shmid: 待关联共享内存的用户级标识符;

shmaddr:指定共享内存映射到进程地址空间的某一地址(通常设置为NULL,让OS来指定 );
shmflg:挂接共享内存时设置的某些属性(一般设置为0);

返回值:成功则返回共享内存映射到进程地址空间中的起始地址,失败则返回-1;

4. 共享内存的去挂接

我们一般使用shmat函数来进行共享内存和虚拟地址空间的去挂接

int shmdt(const void *shmaddr);

shmaddr:shmat函数时得到的起始地址

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

4. 共享内存的使用示例

1. 两进程挂接与去挂接演示:

 我们可以看到nattch是由0->1->2->1->0的;

注意:我们在shmget的时候必须设置权限,否则会挂不上去;

  • comm.h:
#include <stdio.h>    
#include <sys/ipc.h>    
#include <sys/shm.h>    
#include <sys/types.h>    
#include <unistd.h>    
    
#define PATH_NAME "./"        
#define PROJ_ID 0x333        
#define SIZE 4097 
  • makefile:
.PHONY:all    
all:server client    
    
server:server.c    
  gcc -o $@ $^    
    
client:client.c    
  gcc -o $@ $^    
    
.PHONY:clean    
clean:    
  rm -f server client     
  • server.c:
#include "comm.h"

int main()
{
  //生成共享内存key
  key_t key = ftok(PATH_NAME,PROJ_ID);
  if(key<0)
  {
    perror("ftok fail");
    return 1;
  }

  //创建共享内存
  int shmid = shmget(key,SIZE,IPC_CREAT|IPC_EXCL|0666);
  if(shmid<0)
  {
    perror("shmget fail");
    return 2;
  }
  printf("shmget success! key:0x%x shmid:%d\n",key,shmid);
  sleep(3);

  //挂接共享内存
  char* mem = (char*)shmat(shmid,NULL,0);                                                                  
  printf("shmat success!\n");
  sleep(3);

  //去挂接共享内存
  shmdt(mem);
  printf("shmdt success!\n");
  sleep(3);

  //释放共享内存
  shmctl(shmid,IPC_RMID,NULL);
  printf("shmrm success!\n");
  sleep(3);
  return 0;
}
  •  client.c:
#include "comm.h"    
    
int main()    
{    
  //生成key    
  key_t key = ftok(PATH_NAME,PROJ_ID);    
  if(key<0)    
  {    
    perror("ftok fail");    
    return 1;    
  }    
    
  //获取shmid    
  int shmid = shmget(key,SIZE,IPC_CREAT|0666);                                                             
  if(shmid<0)    
  {    
    perror("shmget fail");    
    return 2;    
  }    
    
  //挂接共享内存    
  char* mem = (char*)shmat(shmid,NULL,0);    
  sleep(1);    
  printf("client shmat success!\n");    
    
  //去挂接    
  shmdt(mem);    
  sleep(1);    
  printf("client shmdt success!\n");    
    
  return 0;    
}    

2. 两进程通信演示;

 我们在上述代码中添加了通信的内容,结果如上所示;

  • server.c: 
  //通信    
  while(1)    
  {    
    printf("%s\n",mem);    
    sleep(1);                                                           
  }    
  • client.c:
  //通信    
  char c='A';    
  while(c<'Z')    
  {    
    mem[c-'A']=c;    
    c++;    
    mem[c-'A']='\0';    
    sleep(2);                                                           
  }    

客户端每隔2秒往共享内存中写入数据,服务端每隔1秒读一次;

我们发现在客户端还没有写入数据时,服务端就开始读了;

并且,服务端再读数据之后,并没有把数据拿走;

这两点是区别与管道通信的,管道是具有同步机制(读写两端会相互等待的),读了之后数据就带走了;

坚持打卡!😀

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

高性能:负载均衡

目录 什么是负载均衡 负载均衡分类 服务端负载均衡 服务端负载均衡——软硬件分类 服务端负载均衡——OSI模型分类 客户端负载均衡 负载均衡常见算法 七层负载均衡做法 DNS解析 反向代理 什么是负载均衡 将用户请求分摊&#xff08;分流&#xff09; 到不同的服务器上…

中移链控制台对接4A平台功能验证介绍

中移链控制台具备单独的注册登录页面&#xff0c;用户可通过页面注册或者用户管理功能模块进行添加用户&#xff0c;通过个人中心功能模块进行用户信息的修改和密码修改等操作&#xff0c;因业务要求&#xff0c;需要对中移链控制台的用户账号进行集中管理&#xff0c;统一由 4…

什么是分布式任务调度?怎样实现任务调度

通常任务调度的程序是集成在应用中的&#xff0c;比如&#xff1a;优惠卷服务中包括了定时发放优惠卷的的调度程序&#xff0c;结算服务中包括了定期生成报表的任务调度程序&#xff0c;由于采用分布式架构&#xff0c;一个服务往往会部署多个冗余实例来运行我们的业务&#xf…

C S S

目录 1.样式定义方式 1.1行内样式表 1.2内部样式表 1.3外部样式表 2.注解 3.选择器 3.1标签选择器 3.2 id选择器 3.3 类选择器 3.4 派生选择器 3.5 伪类选择器 链接伪类选择器&#xff1a; 位置伪类选择器&#xff1a; ​编辑 目标伪类选择器&#xff1a; 复合选…

Winform从入门到精通(37)——FolderBrowserDialog(史上最全)

文章目录 前言1、Name2、Description3、RootFolder4、SelectedPath5、ShowNewFolderButton前言 当需要获取一个可以通过用户自由选择路径的时候,这时候就需要FolderBrowserDialog控件 1、Name 获取FolderBrowserDialog对象 2、Description 用于指示对话框的描述,如下: …

Windows forfiles命令详解,Windows按时间搜索特定类型的文件。

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 forfiles 一、结果输出格式二、按时间搜索三、搜索指定类型文件四、批量删除文件 forfile…

ATTCK v12版本战术介绍——防御规避(四)

一、引言 在前几期文章中我们介绍了ATT&CK中侦察、资源开发、初始访问、执行、持久化、提权战术理论知识及实战研究、部分防御规避战术&#xff0c;本期我们为大家介绍ATT&CK 14项战术中防御规避战术第19-24种子技术&#xff0c;后续会介绍防御规避其他子技术&#xf…

【Unity URP】Rendering Debugger和可视化MipMap方案

写在前面 最近开始学习Unity性能优化&#xff0c;是结合了《Unity游戏优化》这本书和教程《Unity性能优化》第叁节——静态资源优化(3)——纹理的基础概念一起学习。在学习纹理优化部分时候遇到了问题&#xff0c;固定管线下Unity的Scene窗口有一个可视化Mipmap的渲染模式&…

PMP/高项 05-项目进度管理

项目进度管理 概念 项目进度管理&#xff08;Schedule Management) 项目进度管理又叫项目工期管理&#xff08;Duration Management)或项目的时间管理(Time Management) 是一种为管理项目按时完成项目所需的各个过程 进度管理过程 规划进度管理 定义活动 排列活动顺序 估算活…

【LeetCode】 309.最佳买卖股票时机含冷冻期

309.最佳买卖股票时机含冷冻期&#xff08;中等&#xff09; 思路 状态定义 一、很容易想到四种状态&#xff1a; a.今天买入&#xff1b;b.今天卖出&#xff1b;c.昨天卖出&#xff0c;今天处于冷冻期&#xff0c;无法进行操作&#xff1b;d.今天不操作&#xff0c;处于持有…

太酷了,库昊

昨天晚上凌晨3点30&#xff0c;勇士和国王的第7场比赛开打。 在上一局在勇士主场干翻勇士后&#xff0c;国王队的信心倍增&#xff0c;他们用自己的节奏一次次击溃勇士&#xff0c;特别是今天的前两节&#xff0c;国王能能够回应勇士的进球&#xff0c;防守也更有侵略性。今天不…

图扑数字孪生助力智慧冷链园区实现大数据实时监控

前言 近年来&#xff0c;业界学者及企业就智慧冷链物流展开深入研究&#xff0c;2010 年 IBM 发布的《智慧的未来供应链》研究报告中提出智慧供应链概念&#xff0c;并由此延伸出智慧物流概念&#xff0c;即智慧物流是以信息化为依托并广泛应用物联网、人工智能、大数据、云计…

【2023 年第十三届 MathorCup 高校数学建模挑战赛】D 题 航空安全风险分析和飞行技术评估问题 27页论文及代码

【2023 年第十三届 MathorCup 高校数学建模挑战赛】D 题 航空安全风险分析和飞行技术评估问题 27页论文及代码 1 题目 D 题 航空安全风险分析和飞行技术评估问题 飞行安全是民航运输业赖以生存和发展的基础。随着我国民航业的快速发展&#xff0c;针对飞行安全问题的研究显得…

巧用千寻位置GNSS软件| 桥台锥坡放样操作技巧

桥台锥坡放样是针对道路施工中&#xff0c;路桥结合部桥台圆锥形斜坡面进行放样设计的专用程序。本期将给大家介绍如何使用千寻位置GNSS软件实现快速完成桥台锥坡放样。 点击【测量】->【桥台锥坡放样】&#xff0c;从线路库中选择桥台经过的线路或是单独增加桥台 锥坡放样&…

QML动画分组(Grouped Animations)

通常使用的动画比一个属性的动画更加复杂。例如你想同时运行几个动画并把他们连接起来&#xff0c;或者在一个一个的运行&#xff0c;或者在两个动画之间执行一个脚本。动画分组提供了很好的帮助&#xff0c;作为命名建议可以叫做一组动画。有两种方法来分组&#xff1a;平行与…

【无功功率控制】连接到无限电网的小型风电场的无功功率控制(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

jvm之字节码

写在前面 java字节码由单字节的指令(也叫做操作码)组成&#xff0c;但一个 byte 最多能够存储 256 个指令&#xff0c;够用吗&#xff1f;截止到目前是够的&#xff0c;因为指令的个数是200多一点&#xff0c;指令分为如下四类&#xff1a; 1&#xff1a;栈操作指令&#xff…

SmartEngine流程引擎之Custom模式

目录 一、为什么选用SmartEngine 二、各类流程引擎框架简单对比 1、流程设计器推荐 2、什么是BPMN 流程定义解释说明 三、SmartEngine之Custom实操 1、引入依赖 2、典型的初始化代码如下 3、节点如何流转以及流程实例存储问题 4、定义Delegation 关键类 一、为什么选用…

Java 基础入门篇(四)—— 方法的重载与参数传递机制

文章目录 一、方法的定义二、方法的参数传递机制 ★2.1 基本类型的参数传递2.2 引用类型的参数传递 三、方法重载 一、方法的定义 方法的作用&#xff1a;封装一段代码的语法结构&#xff0c;可以被重复调用&#xff0c;以此提高代码的复用性&#xff0c;提高开发效率&#xf…

ChatGPT Plus价格太贵,可以约上三五知己一起上车体验一下,这个项目就能帮到你

对于想体验ChatGPT PLus的小伙伴&#xff0c;可能觉得自己一个人一个月花费20美元&#xff0c;相对于人民币每月137多&#xff0c;确实是一个不少的开支&#xff0c;如果&#xff0c;几个人合作一个账号&#xff0c;这样负担就减少了。刚好&#xff0c;最近逛github发现刚好有一…