返回> 网站首页 

利用内存映射 拷贝大文件

yoours2011-03-03 16:15:08 阅读 1100

简介一边听听音乐,一边写写文章。


一、 失败的解决办法(未测试)
//不是物理内存不足,而是连续的虚拟地址空间不足
//因为Map需要连续的虚拟地址空间,当进程中未使用的最大虚拟地址空间小于MapView的大小时就会失败
//获取当前进程中未使用的最大虚拟地址空间大小
DWORD GetMaxMemoryBlock()
{
    DWORD maxBlock = 0;
    PBYTE p = NULL;
    while(true)
    {
        MEMORY_BASIC_INFORMATION mbi;
        if (VirtualQuery(p, &mbi, sizeof(mbi)) == 0)
break;

        p = (PBYTE)mbi.BaseAddress + mbi.RegionSize;
        if (mbi.AllocationBase != NULL)
continue;

        if (mbi.RegionSize > maxBlock)
maxBlock = (DWORD)mbi.RegionSize;
    }

return maxBlock;
}

//释放当前进程内存
void FreeMem()
{
// 获得Debug权限
// 。。。。。。。

DWORD pid = ::GetCurrentProcessId();
HANDLE hProcess = OpenProcess(PROCESS_SET_QUOTA, FALSE, pid);
if (hProcess != NULL)
{
SetProcessWorkingSetSize(hProcess, (DWORD)-1, (DWORD)-1);
CloseHandle(hProcess);
}
}


二、 大文件拷贝代码
int CMyMapFileDlg::MyCopyFileUseMapFile(char* SourceFile, char* DestFile)
{
// 读文件
HANDLE hFileIn = CreateFile(SourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN/*告诉系统,我会顺序读取文件(系统会为我缓存文件)。系统会读取比我要求读取的字节数要多*/, NULL);
if(hFileIn == INVALID_HANDLE_VALUE)
    {
return -1;
}

//写文件
HANDLE hFileOut = CreateFile(DestFile, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, NULL, /*OPEN_EXISTING*/CREATE_ALWAYS/*追加,还是覆盖*/, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if(hFileOut == INVALID_HANDLE_VALUE)
    {
        CloseHandle(hFileIn);
return -1;
}

// 获得输入文件的大小
LARGE_INTEGER val;
val.LowPart = GetFileSize(hFileIn, (DWORD*)&val.HighPart);
if(val.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR)
{
CloseHandle(hFileIn);
CloseHandle(hFileOut);
return -2;
}

// 创建读文件映射内核对象,句柄保存于hFileInMapping
HANDLE hFileInMapping = CreateFileMapping(hFileIn, NULL, PAGE_READONLY, 0, 0, NULL);
if(hFileInMapping == INVALID_HANDLE_VALUE || hFileInMapping == NULL)
{
CloseHandle(hFileIn);
CloseHandle(hFileOut);
return -3;
}

// 释放输入文件内核对象
CloseHandle(hFileIn);

// 创建写文件映射内核对象,句柄保存于hFileOutMapping
HANDLE hFileOutMapping = CreateFileMapping(hFileOut, NULL, PAGE_READWRITE, val.HighPart, val.LowPart, NULL);
if(hFileOutMapping == INVALID_HANDLE_VALUE || hFileOutMapping == NULL)
{
CloseHandle(hFileInMapping);
CloseHandle(hFileOut);
return -3;
}

// 释放写文件内核对象
CloseHandle(hFileOut);

/* 文件大于4G的处理代码 */
__int64 qwFileSize = val.QuadPart;// 文件总大小
DWORD dwBytesInBlock = 0;//每次读取的大小

// 根据系统信息获得:系统分配粒度
SYSTEM_INFO sinf;
GetSystemInfo(&sinf);
   __int64 qwFileOffset = 0;//读文件视图的偏移量
   __int64 qwmyFileOffset = 0;//写文件视图的偏移量

while(qwFileSize > 0)
{
dwBytesInBlock = sinf.dwAllocationGranularity;//分配粒度。 必须为粒度的整数倍
if(qwFileSize < sinf.dwAllocationGranularity)//文件小于系统分配粒度
dwBytesInBlock = (DWORD)qwFileSize;//偏移量为文件大小

// 将读文件数据映射到进程的地址空间 映射空间大小指定一个粒度
BYTE* pbFileIn = (PBYTE)MapViewOfFile(hFileInMapping, FILE_MAP_READ, (DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF), dwBytesInBlock);
if (pbFileIn == NULL)
{
CloseHandle(hFileInMapping);
CloseHandle(hFileOutMapping);
return -4;
}

// 将写文件数据映射到进程的地址空间
BYTE* pbFileOut = (PBYTE)MapViewOfFile(hFileOutMapping, FILE_MAP_WRITE, (DWORD)(qwmyFileOffset >> 32), (DWORD)(qwmyFileOffset & 0xFFFFFFFF), dwBytesInBlock);
if (pbFileOut == NULL)
{
UnmapViewOfFile(pbFileIn);
CloseHandle(hFileInMapping);
CloseHandle(hFileOutMapping);
return -4;
}

//拷贝文件数据
memcpy(pbFileOut, pbFileIn, dwBytesInBlock);

// 从进程的地址空间撤消文件数据映像
UnmapViewOfFile(pbFileIn);
UnmapViewOfFile(pbFileOut);

qwmyFileOffset += dwBytesInBlock;
qwFileOffset += dwBytesInBlock;
qwFileSize -= dwBytesInBlock;
}

// 关闭文件映射对象
CloseHandle(hFileInMapping);
CloseHandle(hFileOutMapping);

return 0;
}

微信小程序扫码登陆

文章评论

1100人参与,0条评论