ICP - Share Memory

进程通信(IPC,Inter-Porcess Communcation)的方式比较多,目前比较常用的有管道(Pipe)、消息队列(Message Queue)、共享内存(Share Memory)、套接字(Socket)、信号量(Semaphore)、信号(Signal)等,本篇主要关于共享内存进行了一些简要的记录。

一、 windows 进程程通信共享内存

1.同一个可执行文件或DLL使用进程共享“数据段”进行数据共享

描述

方法:

创建自定义的共享“数据段”,如用一下的方式先定义自定义段的数据,并将自定义段属性设置为共享:

#pragma data_seg("MySectionName")
int g_instance_count = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:MySectionName,RWS")

注意“数据段”的属性有以下几种,类似Linux下文件的属性

属性 意义
READ 可以从该段读取数据
WRITE 可以向该段写入数据
EXECUTE 可以执行该段的内容
SHARED 该段的内容为多个实例所共享(本质上是关闭了写时复制机制)

在进行“数据段”属性设置时#pragma comment(linker, "/SECTION:MySectionName,RWS")只需要标明大写首字母即可,如RWS表示READ|WRITE|SHARED。如上面共享数据段内的属性g_instance_count就会在定义了这一数据段的同一个执行文件或DLL多份实例间共享数据。虽然可以很容易的进行简单的数据共享,但是限制较多,可能某些情况下能用的着吧? :joy:

然后你就可以用这个属性干点事情比如,在每个进程开启的时候递增1,在进程退出的时候减少1,用于统计当前开启了多少个同类进程 -.-,这只是一个简单实例,demo就在下面。

2.内存映射(Memory Mapping)

描述

采用windows sdk api :CreateFileMapping来进行文件/内存映射实现数据共享

CreateFileMapping原型如下:

  HANDLE CreateFileMapping(
  HANDLE hFile,                       //物理文件句柄
  LPSECURITY_ATTRIBUTES lpAttributes, //安全设置
  DWORD flProtect,                    //保护设置
  DWORD dwMaximumSizeHigh,            //高位文件大小
  DWORD dwMaximumSizeLow,             //低位文件大小
  LPCTSTR lpName                      //共享内存名称
  );

hFile

指定的需要被映射到内存的物理文件句柄,如果指定INVALID_HANDLE_VALUE则会页面文件上建立一个文件无关的映射。本demo内部就是采用INVALID_HANDLE_VALUE来创建一块文件无关的内存映射进行数据共享。

lpAttributes

安全设置,一般设置为NULL就行。

flProtect

对共享文件的保护设置,包括但不限于以下:

属性 意义
PAGE_READONLY 以只读方式打开映射
PAGE_READWRITE 以可读、可写方式打开映射
PAGE_WRITECOPY 为写操作留下备份

dwMaximumSizeHigh

高位文件大小,指定文件映射长度的高32位,32位进程一般用不到,可以设置0。

dwMaximumSizeLow

低位文件大小,指定文件映射长度的低32位。也就是待映射文件的大小,在不指定有效物理文件句柄的情况下,需要指定待大小。在指定了有效物理文件句柄而设置为0,则会使用物理文件实际长度。

lpName

内存/文件映射的名称也是id,如果已经有一个同名的文件映射函数将会打开它,而不是新建一个文件/内存映射。

return

函数将返回创建的文件映射对象句柄,如果失败返回INVALID_HANDLE_VALUE

使用MapViewOfFile函数将文件映射对象映射到当前应用程序的地址空间

通俗的将就是在你的程序里拿到文件映射段的地址,函数原型如下:

LPVOID WINAPI MapViewOfFile(
  __in HANDLE hFileMappingObject,     //文件映射对象句柄
  __in DWORD dwDesiredAccess,         //文件映射对象的访问方式
  __in DWORD dwFileOffsetHigh,        //文件映射相相对起始地址的高32位地址偏移量
  __in DWORD dwFileOffsetLow,         //文件映射相相对起始地址的低32位地址偏移量
  __in SIZE_T dwNumberOfBytesToMap    //文件映射的字节数
  );

hFileMappingObject

文件映射对象句柄,一般情况下传入CreateFileMapping返回的句柄即可。

dwDesiredAccess

文件映射对象的访问方式,包括但不限于以下:

属性 意义
FILE_MAP_READ 可以读取文件.在调用CreateFileMapping时可以传入PAGE_READONLY或PAGE_READWRITE保护属性
FILE_MAP_WRITE 可以读取文件.在调用CreateFileMapping时可以传入PAGE_READONLY或PAGE_READWRITE保护属性PAGE_READWRITE保护属性
FILE_MAP_ALL_ACCESS FILE_MAP_WRITE | FILE_MAP_READ

dwFileOffsetHigh

文件映射相相对起始地址的高32位地址偏移量,一般设置为0。

dwFileOffsetLow

文件映射相相对起始地址的低32位地址偏移量,根据实际分段偏移量设置。

dwNumberOfBytesToMap

文件映射的字节数,函数将会按照设定的地址偏移处映射指定字节数的数据到地址空间。

return

返回映射文件指定偏移位置的数据地址,后面可以对数据进行操作了。

另外在使用完内存映射文件后,需要使用UnmapViewOfFile断开文件映射对象到地址空间的映射,或在需要关闭内存映射文件时使用CloseHandle关闭指定的内存映射文件对象。

二、 Linux 进程通信共享内存

利用共享内存函数进行进程间数据共享

描述

方法:

使用shmget、shmat、shmdt、shmctl共享内存函数进行数据共享。

至于函数原型就借用在百科上扣的图了 -.-,如下:

shmget

在需要共享数据时,可以首先使用`shmget`来创建或者查找已经存在的共享内存。其中`key`参数理解为为内核中共享内存的编号,也就是一把访问共享内存的钥匙了;`size`则是创建的共享内存的大小;`shmflg` 参数在输入`0`的时候可以用来检测共享内存是否已经创建,正常创建时可以指定IPC_CREAT(没有回自动创建)。

shmat

shmat则是获取共享内存的地址,通常返回一个`void *`的首地址指针。获取之后就能够对共享内存块进行读写操作了。

shmdt

shmctl

具体的使用如果有兴趣可以在demo里面瞅瞅,虽然很low,但是意思还是有的… :joy: