共享内存

一、共享内存含义:共享内存指 (shared memory)在多处理器的计算机系统中,可以被不同中央处理器(CPU)访问的大容量内存。

由于多个CPU需要快速访问存储器,这样就要对存储器进行缓存(Cache)。任何一个缓存的数据被更新后,由于其他处理器也


可能要存取,共享内存就需要立即更新,否则不同的处理器可能用到不同的数据。共享内存是 Unix下的多进程之间的通信方


法 ,这种方法通常用于一个程序的多进程间通信,实际上多个程序间也可以通过共享内存来传递信息。



二、共享内存的使用: (1)创建共享内存CreateShareMemory


   (2) 打开已创建的共享内存OpenShareMemory


  (3)文件映射File mapping


下面通过具体代码来说明共享内存的使用:


(1)   创建共享内存:

在创建时会调用API函数 CreateFileMapping();

      函数功能:是创建一个新的文件映射内核对象


API函数原型:


HANDLE CreateFileMapping(


HANDLE

hFile, //物理文件句柄


LPSECURITY_ATTRIBUTES

lpAttributes, //安全设置



DWORD flProtect,//保护设置


DWORD dwMaximumSizeHigh, //高位文件大小


DWORD

dwMaximumSizeLow, //低位文件大小


LPCTSTR

lpName //共享内存名称


);


参数解释如下:


1) 物理文件句柄hFile


解释: 1. 任何可以获得的物理文件句柄, 如果你需要创建一个物理文件无关的内存映射也无妨, 将它设置成为 0xFFFFFFFF(INVALID_HANDLE_VALUE)就可以了



2. 如果需要和物理文件关联, 要确保你的物理文件创建的时候的访问模式和"保护设置"匹配,比如: 物理文件只读, 内存映射需要读写就会发生错误。 推荐你的物理文件使用独占方式创建


3. 如果使用 INVALID_HANDLE_VALUE, 也需要设置需要申请的内存空间的大小, 无论物理文件句柄参数是否有效, 这样 CreateFileMapping 就可以创建一个和物理文件大小无关的内存空间给你, 甚至超过实际文件大小, 如果你的物理文件有效, 而大小参数为0, 则返回给你的是一个和物理文件大小一样的内存空间地址范围。 返回给你的文件映射地址空间是可以 通过复制,集成或者命名得到,初始内容为0


设置值:与物理设备相关:物理文件句柄


创建共享内存:

NULL


2)安全设置lpAttributes


解释:

windows保护设置


设置值:NULL表示默认安全属性


3) 保护设置flProtect


解释:访问所创建 文件映射内核对象(共享内存)的方式


设置值:


PAGE_READONLY 以只读方式打开映射


PAGE_READWRITE 以可读、可写方式打开映射


PAGE_WRITECOPY 为写操作留下备份


可组合使用下述一个或多个常数:


SEC_COMMIT 为文件映射一个小节中的所有页分配内存


SEC_IMAGE 文件是个可执行文件


SEC_RESERVE 为没有分配实际内存的一个小节保留虚拟内存空间


4) 高位文件大小dwMaximumSizeHigh


解释:

32位的机器,设置为0;64位机器按照需求分配空间的大小设置值


设置值:整型数(表示高于32位的位数)


5) 低位文件大小dwMaximumSizeLow


解释:低于32位的值的位数    如果参数(4)(5)都为0,则表示了磁盘文件的实际长度


6)

共享内存名称lpName 


具体代码如下:


#include <stdio.h>


#include <Windows.h>


#define SHAREMEMNAME  

"ZSShareMem"          //所创建的共享内存名


int main()


{


DWORD

err;                         //创建的DWORD类型的变量err用来存放调用GetLastError函数的返回值


HANDLE

hShareMem = INVALID_HANDLE_VALUE; //创建一个句柄 将INVALID_HANDLE_VALUE作为无效值


/*创建一个名字为ZSShareMem的共享内存*/



hShareMem = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,1024,SHAREMEMNAME)


/*CreateFileMapping()会返回三种错误码:ERROR_FILE_INVALID、ERROR_INVALID_HANDLE、ERROR_ALREADY_EXISTS*/


err = GetLastError();

//获取CreateFileMapping()API的错误返回码


if (hShareMem == INVALID_HANDLE_VALUE || hShareMem == NULL)

//判断是否出错


{


printf("共享内存创建失败\n");


}


if (err == ERROR_FILE_INVALID )

//判断是否错误为ERROR_FILE_INVALID


{


printf("企图创建一个零长度的文件映射\n");


}


if (err == ERROR_INVALID_HANDLE )

//判断是否错误为ERROR_INVALID_HANDLE


{


printf("内存空间的命名和现有的内存映射,互斥量,信号量,临界区有同名\n");


}


if (err == ERROR_ALREADY_EXISTS)

//判断是否错误为ERROR_ALREADY_EXISTS


{


printf("%s的共享内存已经存在,句柄值为%d\n",SHAREMEMNAME,hShareMem);


}


else


{


printf("%s的共享内存创建成功,句柄值为%d\n",SHAREMEMNAME,hShareMem);


}


getchar();


return 0;


}


(2)  打开已创建共享内存:

       程序中会调用API函数OpenFileMapping()和 MapViewOfFile();

API函数OpenFileMapping原型:


OpenFileMapping(


__in DWORD

dwDesiredAccess, //访问方式


__in BOOL

bInheritHandle, //如这个函数返回的句柄能由当前进程启动的新进程继承,则这个参数为TRUE


__in LPCSTR

lpName //指定要打开的共享内存名称


);


该函数功能:打开一个现成的文件映射对象即共享内存



         参数dwDesiredAccess访问方式的取值含义:


FILE_MAP_WRITE

映射可读可写。文件映射对象必须通过PAGE_READWRITE访问创建。


FILE_MAP_READ

映射只读。文件映射对象必须通过PAGE_READ 或 PAGE_READWRITE访问创建。


FILE_MAP_ALL_ACCESS 与FILE_MAP_WRITE相同。


FILE_MAP_COPY

映射时保留写操作的副本。文件映射对象必须用PAGE_WRITECOPY访问在win95下创建


MapViewOfFile函数原型:


LPVOID WINAPI MapViewOfFile(   


HANDLE hFileMappingObject,       //已创建共享内存句柄


DWORD dwDesiredAccess,           //访问方式     


DWORD dwFileOffsetHigh,           //高位文件大小


DWORD dwFileOffsetLow,              //低位文件大小


SIZE_T dwNumberOfBytesToMap       //指定映射文件的字节数


);


该函数功能:将文件映射对象(共享内存)映射到当前应用程序(应用进程)的地址空间


参数dwDesiredAccess访问方式取值含义:


FILE_MAP_ALL_ACCESS          

等价于CreateFileMapping的 FILE_MAP_WRITE|FILE_MAP_READ. 文件映射对象被创建时必须指定PAGE_READWRITE 选项.


FILE_MAP_COPY                  

可以读取和写入文件.写入操作会导致系统为该页面创建一份副本.


FILE_MAP_EXECUTE                

可以将文件中的数据作为代码来执行.  


FILE_MAP_READ

可以读取文件.  


FILE_MAP_WRITE

可以读取和写入文件.  


返回值:


如果成功,则返回映射视图文件的开始地址值。


如果失败,则返回 NULL.可调用GetLastError()查看错误。


函数的具体实现:


#include <stdio.h>


#include <Windows.h>


#define SHAREMEMNAME "ZSShareMem"

int main()

{


char

*szShareMem = NULL;


HANDLE

hShareMem   = INVALID_HANDLE_VALUE;


hShareMem = OpenFileMapping(FILE_MAP_ALL_ACCESS,FALSE,SHAREMEMNAME);

//打开一个已经存在的共享内存(ZSShareMem),获得所有权限进行访问


if (hShareMem == NULL || hShareMem == INVALID_HANDLE_VALUE)


{


printf("文件打开失败");


}


szShareMem = (char *)MapViewOfFile(hShareMem,FILE_MAP_WRITE|FILE_MAP_READ,0,0,0);

//将文件映射对象映射到当前进程地址空间


printf("%s\n",szShareMem);

     //打印共享内存中的信息


getchar();


return 0;


}


(3)文件映射: 

具体实现代码如下:


#include <stdio.h>


#include <Windows.h>


#define SHAREMEMNAME

"ZSShareMem"


int main()


{


DWORD

err;


HANDLE

hShareMem = INVALID_HANDLE_VALUE;


char

*address;


int

*p;


hShareMem = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,1024,SHAREMEMNAME);

//创建共享内存


if (hShareMem == INVALID_HANDLE_VALUE || hShareMem == NULL)

//判断是否出错


{


printf("共享内存创建失败\n");


return 0;


}


address = (char *)MapViewOfFile(hShareMem,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);

//将文件映射对象映射到当前进程地址空间并且将映射对象首地址传给address


memset(address,0,1024);                                            

//将address全部置0


sprintf(address,"1");


printf("%s\n",address);


p = (int *)MapViewOfFile(hShareMem,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);

//将MapOfFile函数返回值的类型强制转换成int型


*p = 247;


printf("%d\n",p[0]);


p[1] = 36;


printf("%d\n",p[1]);


getchar();


return 0;


}