共享内存 Shared Memory在Win32中,每个进程有自己的地址空间,一个进程不能轻易地访问另一个进程地址空间中的数据。Win32系统允许多个进程(运行在同一计算机上)使用内存映射文件来共享数据。
这种数据共享是让两个或多个进程映射同一文件映射对象的视图,即它们在共享同一物理存储页。这样,当一个进程向内存映射文件的一个视图写入数据时,其他的进程立即在自己的视图中看到变化。但要注意,对文件映射对象要使用同一名字。
1. 创建共享内存
HANDLE m_hMapFile = CreateFileMapping(
(HANDLE)0xFFFFFFFF, //0xFFFFFFFF表示创建一个进程间共享的对象
NULL,
PAGE_READWRITE, //读写共享
0,
4096, //共享区间大小4096
"SharedMem"); //共享内存名
2. 映射到本进程的地址空间
void* m_pBaseMapFile = MapViewOfFile(
m_hMapFile,
FILE_MAP_READ|FILE_MAP_WRITE,
0,
0,
0);
3. 使用共享内存
unsigned char *p = (unsigned char*)m_pBaseMapFile;
p[0]=00; //修改共享内存
p[1]=11;
p[2]=22;
printf("%d, %d\n", p[0], p[1]); //读共享内存
4. 取消本进程地址空间的映射
UnmapViewOfFile(m_pBaseMapFile);
5. 关闭共享内存句柄
CloseHanle(m_hMapFile);
---------------------------------------------------
在另外一个进程中使用刚才创建的共享内存
1. 获得共享内存句柄
HANDLE m_hMapFile = OpenFileMapping(
FILE_MAP_READ | FILE_MAP_WRITE,
FALSE,
"SharedMem") //必须与刚才创建时的名称相同
Note: 也可以用CreateFileMapping, 返回刚才创建的共享内存句柄
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
*phr = E_FAIL;
return NULL;
}
if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE)) {
*phr = E_FAIL;
return NULL;
}
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = &sd;
HANDLE m_hMapFile = CreateFileMapping(
(HANDLE)0xFFFFFFFF,
&sa,
PAGE_READWRITE,
0,
4096, //共享区间大小4096
"SharedMem"); //共享内存名
2. 映射到本进程的地址空间
void* m_pBaseMapFile = MapViewOfFile(
m_hMapFile,
FILE_MAP_READ|FILE_MAP_WRITE,
0,
0,
0);
3. 使用共享内存
unsigned char *p = (unsigned char*)m_pBaseMapFile;
p[0]=55; //修改共享内存
p[1]=66;
printf("%d, %d\n", p[0], p[1]); //读共享内存
4. 取消本进程地址空间的映射
UnmapViewOfFile(m_pBaseMapFile);
5. 关闭共享内存句柄
CloseHanle(m_hMapFile);
---------------------------------------------------
由于某种需要,很多时候可能需要对文件进行随机偏移读取和修改。一般情况下,可以先fseek到文件中制定的位置,再将文件块读入内存-修改-写回。 对于大文件(GB量级),或者频繁的随机文件读写,这样的方式会非常耗费时间。
这类操作一般是以内存映射文件(即将文件映射到进程的某一块空间)的方式来加以处理的。使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行
I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内
存到文件的回写以及释放内存块等步骤,所以效率大大提高。
//获得文件句柄
HANDLE hFile = CreateFile(
"data.dat", //文件名
GENERIC_READ|GENERIC_WRITE, //对文件进行读写操作
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING, //打开已存在文件
FILE_ATTRIBUTE_NORMAL,
0);
//返回值size_high,size_low分别表示文件大小的高32位/低32位
DWORD size_low,size_high;
size_low = GetFileSize(hFile,&size_high);
//创建文件的内存映射文件。
HANDLE hMapFile = CreateFileMapping(
hFile, NULL,
PAGE_READWRITE, //对映射文件进行读写
size_high,
size_low, //这两个参数共64位,所以支持的最大文件长度为16EB
NULL);
if(hMapFile==INVALID_HANDLE_VALUE)
{
AfxMessageBox("Can't create file mapping.Error%d:\n", GetLastError());
CloseHandle(hFile);
return;
}
//把文件数据映射到进程的地址空间
void* pvFile = MapViewOfFile(
hMapFile,
FILE_MAP_READ|FILE_MAP_WRITE,
0,
0,
0);
unsigned char *p=(unsigned char*)pvFile;
//至此,就获得了外部文件data.dat在内存地址空间的映射,
//下面就可以用指针p"折磨"这个文件了
CString s;
p[size_low-1]=0x1f;
p[size_low-2]=0x2f; //修改该文件的最后两个字节(文件大小<4GB高32位为0)
s.Format("%#x,%#x,%#x",p[size_low-3],p[size_low-2],p[size_low-1]);//读文件的最后3个字节
AfxMessageBox(s);
//结束
UnmapViewOfFile(pvFile); //撤销映射
CloseHandle(hMapFile);
CloseHandle(hFile); //关闭文件