内存映射文件

vc 内存映射文件---《WINDOWS核心编程》17章学习笔记

windows下如果要对一个磁盘文件指定部分进行修改有如下【三种方法】:
【方法一】打开文件,把file指针移动到指定位置,写入数据;
【方法二】打开文件,读文件指定部分内容到内存buf,在buf中查找,修改,最后回写入文件;
【方法三】内存映射文件,此方法同时考虑到了以上2种方式,即直接操作文件+在内存中操作文件
        其原理和虚拟内存相同,不同的是以磁盘文件代替系统的页文件。
        也就是把磁盘文件看作一块内存,并得到一个buf的指针指向文件头,对此buf的操作等同操作文件。

内存映射文件的【三个好处】:
【好处一】加载EXE和DLL时,节省内存,减少加载时间;
          不学不知道,原来windows加载EXE和DLL都是通过内存映射文件方式来进行的
【好处二】操作文件时,可不必进行IO操作,不必对文件内容进行缓冲操作.
【好处三】可达到进程间通信的目的;实际上所有进程间通信全依赖内存映射文件,不过内存映射文件无法单独完成
          因为进程通信要有互相通知的过程,即发消息,所以表现出来形式各异。


【好处一】的实例说明
由于windows加载EXE和DLL都是通过内存映射文件方式来进行的,
得出结论同一EXE文件的不同实例之间是可以通信的
vc6.0建对话框程序<AppInst>,在文件AppInst.cpp上面加入下列代码
/**********************************************
#pragma data_seg("Shared") //都是编译选项
volatile LONG g_lApplicationInstances = 0;//大概意思是更改本exe文件结构,添加1个节(术语)
#pragma data_seg()    //此节的内容是共享的,本例共享的变量为g_lApplicationInstances
#pragma comment(linker, "/Section:Shared,RWS")
***********************************************/
这样设置后,同1个EXE不同实例间g_lApplicationInstances是共享的,可以自己实践
缺点:不同EXE就没有办法了,所以禁止多次运行还是用互斥稳当,这个实例只是验证windows加载EXE的方式

 

【好处二】的实例说明,象操作内存buf一样操作文件,操作大文件时效果明显
/**********************************************
#include <windows.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
// 步骤1 打开文件FILE_FLAG_WRITE_THROUGH
HANDLE hFile = CreateFile(
   "demo.txt",
   GENERIC_WRITE | GENERIC_READ,// 如果要映射文件:此处必设置为只读(GENERIC_READ)或读写
   0,    // 此设为打开文件的任何尝试均将失败
   NULL,
   OPEN_EXISTING,
   FILE_ATTRIBUTE_NORMAL, //|FILE_FLAG_WRITE_THROUGH,【解1】
   NULL);
if (hFile != INVALID_HANDLE_VALUE)// 文件打开失败返回句柄为-1
      // 这步必须测试,详细见步骤2
{
   printf("文件打开成功~!/n");
}

// 步骤2 建立内存映射文件
DWORD dwFileSize = GetFileSize(hFile, NULL);
printf("文件大小为:%d/n", dwFileSize);
HANDLE hFileMap = CreateFileMapping(
   hFile, // 如果这值为INVALID_HANDLE_VALUE,是合法的,上步一定测试啊
   NULL,   // 默认安全性
   PAGE_READWRITE,   // 可读写
   0, // 2个32位数示1个64位数,最大文件字节数,
    // 高字节,文件大小小于4G时,高字节永远为0
   0,//dwFileSize, // 此为低字节,也就是最主要的参数,如果为0,取文件真实大小
   NULL);
if (hFileMap != NULL)
{
   printf("内存映射文件创建成功~!/n");
}

// 步骤3:将文件数据映射到进程的地址空间
PVOID pvFile = MapViewOfFile( //pvFile就是得到的指针,用它来直接操作文件
   hFileMap,
   FILE_MAP_WRITE,    // 可写
   0,     // 文件指针头位置 高字节
   0, // 文件指针头位置 低字节 必为分配粒度的整倍数,windows的粒度为64K
   0);   // 要映射的文件尾,如果为0,则从指针头到真实文件尾
if (pvFile != NULL)
{
   printf("文件数据映射到进程的地址成功~!/n");
}

// 步骤4: 像操作内存一样操作文件,演示功能把整个文件倒序
char *p = (char*)pvFile;
printf("%s/n", p);
for (unsigned int i = 0; i <= dwFileSize / 2; i++)
{
   int nTmp = p[i];
   p[i] = p[dwFileSize - 1 - i];
   p[dwFileSize - 1 - i] = nTmp;
}
printf("%s/n", p);

// 步骤5: 相关的释放工作
UnmapViewOfFile(pvFile); // 释放内存映射文件的头指针
CloseHandle(hFileMap);   // 内存映射文件句柄
CloseHandle(hFile);    // 关闭文件
getchar();
return 0;
}
***********************************************/
【解1】如此操作文件文件会立即改变吗?可能不会,
为了提高速度,系统将文件的数据页面进行高速缓存,并且在对文件的映射视图进行操作时不立即更新文件的磁盘映像。如果需要确保你的更新被写入磁盘,可以强制系统将修改过的数据的一部分或全部重新写入磁盘映像中,方法是调用F lushViewOfFile函数或在CreateFile加上此选项FILE_FLAG_WRITE_THROUGH


【好处三】的实例说明 不同进程间通信,省去了对磁盘文件依赖,直接建立内存映射文件
步骤1: vc6.0建对话框程序,添加3个Button; 1个Edit:m_edit; 1个listbox: m_list

在对话框单元声明公共变量
HANDLE m_hFileMap;
CString strMsg;
步骤2: OnInitDialog() 添加   
SetDlgItemText(IDC_EDIT1, "操作数据成功~!");
m_hFileMap = NULL;
步骤3:按钮1-建立内存映射文件,并写入内容
/***********************************************
HANDLE hFileMap= CreateFileMapping(INVALID_HANDLE_VALUE, NULL,//必须是INVALID_HANDLE_VALUE
   PAGE_READWRITE, 0, 4 * 1024, TEXT("MMFSharedData"));
if (hFileMap == NULL)
{
   m_list.AddString("创建虚拟内存映射文件失败~!");
   return;
}
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
   strMsg.Format("%d: 指定的文件已存在,创建失败~!", hFileMap);
   m_list.AddString(strMsg);
   CloseHandle(hFileMap);
   return;
}

strMsg.Format("%d: 创建成功", hFileMap);
m_list.AddString(strMsg);
m_hFileMap = hFileMap;

PVOID pView = MapViewOfFile(m_hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0,0,0);
if (pView == NULL)
{
   m_list.AddString("创建视图失败~!");
   return;
}

m_edit.GetWindowText((LPTSTR)pView, 4 * 1024);
m_list.AddString((LPTSTR)pView);
UnmapViewOfFile(pView);
***********************************************/


步骤4:按钮2-打开存在的内存映射文件,并读出内容 【此功能与按钮1可在不同进程】
/***********************************************
HANDLE hFileMapT = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE,
   false, TEXT("MMFSharedData"));
if (hFileMapT == NULL)
{
   m_list.AddString("打开文件失败~!");
   return;
}

strMsg.Format("%d: 打开成功", hFileMapT);
m_list.AddString(strMsg);

PVOID pView = MapViewOfFile(hFileMapT,
   FILE_MAP_READ | FILE_MAP_WRITE, 0,0,0);

if (pView != NULL)
{
   m_list.AddString((char*)pView);
   UnmapViewOfFile(pView);
   if (!CloseHandle(hFileMapT))
   {
    m_list.AddString("文件关闭失败~!");
   }  
}
***********************************************/

步骤5 最后的释放
if (m_hFileMap == 0) // 映射文件已经关闭
{
   return;
}

CloseHandle(m_hFileMap); // 关闭成功  
m_hFileMap = NULL;

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值