当前位置: 首页 > news >正文

进程通信——共享内存

文章目录

  • 1.基本认识
    • 1.1 概念介绍
    • 1.2主要原理
  • 2.使用方法
    • 2.1创建共享内存shmget
      • 2.1.1 shmget
      • 2.1.2 ftok
    • 2.2映射地址空间shmat
    • 2.3 访问共享内存
    • 2.4 同步和互斥
  • 3.接口封装
    • 3.1 创建shm_create
    • 3.2 映射shm_connect
    • 3.3 多进程共享内存

共享内存是一种机制,也是进程间进行通信的一种方式

1.基本认识

1.1 概念介绍

不同进程的资源通常是独立的,他们所占用的内存互相独立,不可互相访问。
共享内存允许多个进程访问同一块内存区域,从而实现数据的共享和交换。是一种高效的进程通信方法。

在共享内存中,多个进程可以将同一块物理内存,映射到它们各自的虚拟地址中,使它们可以直接读写该内存的内容,而无需通过消息传递等其它通信方式,也就是“完全无需额外的拷贝”。这种直接的内存访问,使得数据交换更高效。

1.2主要原理

要理解共享内存,首先要理解“虚拟内存”这一概念。

虚拟内存是进程直接操作的地址,不同进程可以存在相同的虚拟内存地址,因为实际所代表的物理地址并不相同

每个进程内的虚拟地址是连续的,但实际映射在物理地址上,却不是连续的。

通常来说各进程之间不会共用物理内存地址,如下图所示:
在这里插入图片描述
进程A和进程B各自的虚拟内存,分别映射在实际物理内存的不同区域上。

那么,共享内存就是改变这种映射关系,让不同的进程“共用物理内存”。如下图所示:
在这里插入图片描述
这个时候,进程A的第3块虚拟内存,与进程B的第1块虚拟内存,就指向同一块物理内存了。

也就是它们共享内存了。之后其中一个改写了这块内存的内容,另一个就可以直接读到,省去了中间(系统内核)的数据拷贝。这就是所谓“共享内存效率最高”的原因。

但从中我们不难看出,共享内存也是有缺点的,那就是大家都是读写同一块内存,很容易冲突。所以共享内存通常还需要搭配其它同步机制使用。

2.使用方法

共享内存的使用并不难,就是改变内存映射,获取到同一个指向共享内存的地址,然后读写数据。

2.1创建共享内存shmget

第一步是创建一个共享内存区域,并分配一块内存来存储数据。

2.1.1 shmget

#include <sys/ipc.h>
#include <sys/shm.h>/***********************************
* @para key 共享内存的键值,用于标识共享内存段。通常使用 ftok 函数生成键值。
* @para size 共享内存段的大小,以字节为单位
* @para shmflg 共享内存的标志位,用于指定创建共享内存的权限和行为
* @return 成功时,返回共享内存的标识符(即共享内存的ID)
* 		  失败时,返回-1,并设置相应的错误码
* 
*********************************/
int shmget(key_t key, size_t size, int shmflg);//example
key_t key = ftok(".", 123); //详见下一节
int shmid = shmget(key, 1024, IPC_CREAT | 0666);

2.1.2 ftok

上述出现了一个 ftok 函数,这个函数返回了一个 key_t

这个有什么作用呢?其实,这个 key 是不同进程间使用同一片物理内存的关键。如果要进行共享,就大家都得使用同一个 key。否则,系统如何知道哪几个进程之间要共享呢。

而 ftok 则是使用一个“路径(文件或目录)”加一个“立即数”,来产生一个 key。只要大家的这两个参数相同,计算出来的 key 就是一致的。

其原型如下:

#include <sys/types.h>
#include <sys/ipc.h> 
key_t ftok(const char *pathname, int proj_id);参数:
pathname:一个存在的文件路径名,用于生成键值。通常选择一个已经存在的文件。
proj_id:一个整数,用于区分不同的IPC资源。通常选择一个非零的整数。返回值:
如果成功,返回一个键值(key)。
如果失败,返回-1,并设置相应的错误码。

关于ftok,还有几个要点:

  • ftok 的路径可以随便设置,但必须是实际存在的
  • ftok 只是想取文件 inode 这个唯一数值,和文件权限无关
  • proj_id 在一些系统(如 UNIX)上,实际取值是1到255。

为什么对 ftok 说那么多,因为这个 key 对很多进程间通信都是一个关键。不只是共享内存,其它诸如消息队列、信号量之类的方法,也会用到。

2.2映射地址空间shmat

第二步就是,将创建出的共享内存空间,映射到本进程的虚拟地址空间。
映射使用的是 shmat,原型如下:

参数:
shmid:共享内存的标识符(即共享内存的ID),由shmget函数返回
shmaddr:指定共享内存段附加到当前进程地址空间的地址。通常设置为 NULL,表示由系统自动选择一个合适的地址
shmflg:共享内存的标志位,用于指定附加共享内存的权限和行为返回值:
成功时,返回指向共享内存段附加地址的指针。
失败时,返回 -1,并设置相应的错误码。例子:
#include <sys/types.h>
#include <sys/shm.h>
char *shmaddr = shmat(shmid, NULL, 0);

2.3 访问共享内存

这一步比较简单,就是向共享内存(也可以理解为自己的地址空间)写数据和读数据。

memcpy(shmaddr, source_buff, source_length); //写 
memcpy(target_buff, shmaddr, target_length); //读

2.4 同步和互斥

之前提到过共享内存的不足。由于多个进程可以同时访问共享内存,需要使用同步和互斥机制来确保数据的一致性和正确性。常见的同步机制包括信号量、互斥锁和条件变量等。详情请看进程互斥访问-信号量。

3.接口封装

3.1 创建shm_create

#include <sys/types.h>
#include <sys/ipc.h> 
key_t ftok(const char *pathname, int proj_id);
#define KEY_NUM 126
#define FILE_PATH "./share.txt"int shm_create_with_key(const char *filepath, int len, int key_num)
{key_t key = ftok(filepath, key_num);if(key < 0){printf("create key error!\n");return key;}int shmid = shmget(key, len, IPC_CREATE|0666);//666代表可读可写if(shmid < 0){return shmid;printf("create share mem error!\n")}return shmid
}int shm_create(int len)
{return shm_create_with_key(FILE_PATH, len, KEY_NUM);
}

3.2 映射shm_connect


char* shm_connect(int shm_id)
{char *mem = shmat(shm_id, NULL, 0);if(mem < 0){printf("shmat error!\n");return mem;}return mem;
}

3.3 多进程共享内存

#include <sys/types.h>
#include <sys/ipc.h> 
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>#define KEY_NUM 126
#define FILE_PATH "./share.txt"//使用文件路径作为共享内存
int shm_create_with_key(const char *filepath, int len, int key_num)
{key_t key = ftok(filepath, key_num);if(key < 0){printf("create key error!\n");return key;}int shmid = shmget(key, len, IPC_CREAT|0666);//666代表可读可写if(shmid < 0){return shmid;printf("create share mem error!\n");}return shmid;
}int shm_create(int len)
{return shm_create_with_key(FILE_PATH, 100, KEY_NUM);
}char* shm_connect(int shm_id)
{char *mem = (char*)shmat(shm_id, NULL, 0);if(mem < 0){printf("shmat error!\n");return NULL;}return mem;
}int main()
{int shm_id = shm_create(10);if(shm_id < 0){printf("shm_create error!\n");return -1;}   char *mem = shm_connect(shm_id);if(!mem){printf("shm_connect error!\n");return -1;}int i = 10;while (--i){printf("%s\n", mem);sleep(1);}shmdt(mem);return 0;
}
#include <sys/types.h>
#include <sys/ipc.h> 
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "string.h"#define KEY_NUM 126
#define FILE_PATH "./share.txt"int shm_create_with_key(const char *filepath, int len, int key_num)
{key_t key = ftok(filepath, key_num);if(key < 0){printf("create key error!\n");return key;}int shmid = shmget(key, len, IPC_CREAT|0666);//666代表可读可写if(shmid < 0){return shmid;printf("create share mem error!\n");}return shmid;
}int shm_create(int len)
{return shm_create_with_key(FILE_PATH, 100, KEY_NUM);
}char* shm_connect(int shm_id)
{char *mem = (char*)shmat(shm_id, NULL, 0);if(mem < 0){printf("shmat error!\n");return NULL;}return mem;
}int main()
{int shm_id = shm_create(10);if(shm_id < 0){printf("shm_create error!\n");return 0;}   char *mem = shm_connect(shm_id);if(!mem){printf("shm_connect error!\n");return 0;}//初始化memcpy(mem, "hello", 6);int i = 0;while (i<10){mem[0] = '1'+i;++i;sleep(1);}shmdt(mem);return 0;
}

http://www.mrgr.cn/news/16727.html

相关文章:

  • 数据源10min自动断开连接导致查询抛异常(未获取可用连接)
  • pc端项目登陆方式
  • 男人圣经 18
  • 使用MCP2518FD在STM32G4上实现SPI转CAN通信
  • HIVE 数据仓库工具之第一部分(讲解部署)
  • 【王树森】Vision Transformer (ViT) 用于图片分类(个人向笔记)
  • 如何利用chatgpt的提升代码能力
  • audiocraft - 免费文本转音乐、AI音乐生成、AI音乐创作工具,Facebook开源,本地一键整合包下载
  • 华为数据之道-读书笔记
  • Nginx快速入门:编译及常用配置
  • kingbase读取blob,下载文件注意事项
  • 没有永远免费的加速器,但是永远有免费的加速器【20240831更新】
  • 基于STM32校车安全监控系统的设计(论文+源码+实物)
  • redis面试官经常问的问题
  • 认知杂谈32
  • 秋风送爽,夏意未央|VELO Prevail Revo坐垫,一骑绿动起来吧~
  • python学习之路 - PySpark快速入门
  • 【软考】路由器
  • volatile 关键字
  • 我从obsidian 转入 语雀 了