分享

共享内存的使用

 思考的轨迹 2012-04-13


共享内存 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); //关闭文件

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多