读书人

处理大文件偏移的有关问题

发布时间: 2013-04-20 19:43:01 作者: rapoo

处理大文件,偏移的问题
以下是一篇关于处理大文件的文章的

文章里有详细的代码,有几个地方没看懂。


 在本例中,首先通过GetFileSize()得到被处理文件长度(64位)的高32位和低32位值。然后在映射过程中设定每次映射的块大小为1000倍的分配粒度(系统的数据分块大小),如果文件长度小于1000倍的分配粒度时则将块大小设置为文件的实际长度。在处理过程中由映射、访问、撤消映射构成了一个循环处理。其中,每处理完一个文件块后都通过关闭文件映射对象来对每个文件块进行整理。CreateFileMapping()、MapViewOfFile()等函数是专门用来进行内存文件映射处理用的。

// 创建文件对象
HANDLE hFile = ::CreateFile(strFile, GENERIC_READ,FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
TRACE("创建文件对象失败,错误代码:%d\r\n", GetLastError());
return;
}
// 创建文件映射对象
HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (hFileMap == NULL)
{
TRACE("创建文件映射对象失败,错误代码:%d\r\n", GetLastError());
return;
}
// 得到系统分配粒度
SYSTEM_INFO SysInfo;
GetSystemInfo(&SysInfo);
DWORD dwGran = SysInfo.dwAllocationGranularity;
// 得到文件尺寸
DWORD dwFileSizeHigh;
__int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
qwFileSize |= (((__int64)dwFileSizeHigh) << 32);///MSDN

// 偏移地址
__int64 qwFileOffset = 0;
__int64 T_newmap = 900 * dwGran;
// 块大小
DWORD dwBlockBytes = 1000 * dwGran;//文件数据分段大小
if (qwFileSize - qwFileOffset < dwBlockBytes)
dwBlockBytes = (DWORD)qwFileSize;

// 映射视图
char *lpbMapAddress = (char *)MapViewOfFile(hFileMap,FILE_MAP_READ,
(DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF),dwBlockBytes);
if (lpbMapAddress == NULL)
{
TRACE("映射文件映射失败,错误代码:%d ", GetLastError());
return;
}
// 关闭文件对象
CloseHandle(hFile);
///////////读文件数据
while(qwFileOffset < qwFileSize)
{
/******************** 读文件 ***************************/
//read_eh(&lpbMapAddress)读取已映射到内存的数据,并将文件指针作相应后移(lpbMapAddress++),返回指针偏移量
qwFileOffset = qwFileOffset + read_eh(&lpbMapAddress); //修改偏移量
if (qwFileOffset > T_newmap)
{//当数据读到90%时,为防数据溢出,需要映射在其后的数据 T_newmap
UnmapViewOfFile(lpbMapAddress);//释放当前映射
if ((DWORD)(qwFileSize - T_newmap) < dwBlockBytes)
dwBlockBytes = (DWORD)(qwFileSize - T_newmap);
lpbMapAddress = (char *)MapViewOfFile(hFileMap,FILE_MAP_READ,
(DWORD)(T_newmap >> 32), (DWORD)(T_newmap & 0xFFFFFFFF),dwBlockBytes);
// 修正参数
lpbMapAddress = lpbMapAddress + qwFileOffset - T_newmap;
T_newmap =T_newmap + 900 * dwGran;
if (lpbMapAddress == NULL)
{
TRACE("映射文件映射失败,错误代码:%d ", GetLastError());
return;
}
}


}
//释放最后数据块映射
UnmapViewOfFile(lpbMapAddress);
// 关闭文件映射对象句柄
CloseHandle(hFileMap);




////////////////////////////////////////////
以下是问题:


问题1:

T_newmap原本是900倍的系统粒度大小的内存。


if ((DWORD)(qwFileSize - T_newmap) < dwBlockBytes) //这句什么意思?
dwBlockBytes = (DWORD)(qwFileSize - T_newmap);
//这里为什么不是 (DWORD)(qwFileSize - 1000倍的系统粒度) ? 却是 用qwFileSize 减去T_newmap?


lpbMapAddress = (char *)MapViewOfFile(hFileMap,FILE_MAP_READ,
(DWORD)(T_newmap >> 32), (DWORD)(T_newmap & 0xFFFFFFFF),dwBlockBytes);//这里更是诡异,何为偏移?为什么用t_newmap来进行偏移?


// 修正参数
lpbMapAddress = lpbMapAddress + qwFileOffset - T_newmap;
T_newmap =T_newmap + 900 * dwGran;




问题2:
mapviewoffile的两个参数:

dwFileOffsetHigh
dwFileOffsetLow

到底该如何正确的使用?


[解决办法]
MapViewOfFile(
__in HANDLE hFileMappingObject,
__in DWORD dwDesiredAccess,
__in DWORD dwFileOffsetHigh,
__in DWORD dwFileOffsetLow,
__in SIZE_T dwNumberOfBytesToMap
);
hFile是创建共享文件的句柄。
lpFileMappingAttributes是文件共享的属性。
flProtect是当文件映射时读写文件的属性。
dwMaximumSizeHigh是文件共享的大小高位字节。
dwMaximumSizeLow是文件共享的大小低位字节。
lpName是共享文件对象名称。
hFileMappingObject是共享文件对象。
dwDesiredAccess是文件共享属性。
dwFileOffsetHigh是文件共享区的偏移地址。
dwFileOffsetLow是文件共享区的偏移地址。
dwNumberOfBytesToMap是共享数据长度。
[解决办法]
MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB。
[解决办法]
/ 修正参数
lpbMapAddress = lpbMapAddress + qwFileOffset - T_newmap;
T_newmap =T_newmap + 900 * dwGran;
newmap
所以用t_newmap来进行偏移


lpbMapAddress = (char *)MapViewOfFile(hFileMap,FILE_MAP_READ,
(DWORD)(T_newmap >> 32), (DWORD)(T_newmap & 0xFFFFFFFF),dwBlockB……
你写错了吧?

[解决办法]
代码的意思是一次每次映射dwBlockBytes个字节,即1000*64KB(只有在最后次映射不够这么多时才映射qwFileSize - T_newmap个字节),虽说每次映射1000*64KB字节,但当读取超过900*64KB个字节时,就进行下一次映射,下一次映射就从上次的900*64KB处开始,映射1000*64KB。所以每次的偏移量就是900*64KB的倍数,也就是用t_newmap来偏移。
什么叫偏移,举个例子,有个含有数据的数组char szSrc[200],现在你有个数组char szDest[10],现在需要把szSrc的数据读到szDest里,但是szDest的大小不够只有10个字节,于是就分批读取,类似这样
int offset = 0;//这个就是偏移
for(int i = 0;i<20;i++)
{
memcpy_s(szDest,10,szSrc+offset,10);
offset += 10;
}
随手写的代码不一定正确
如果szSrc太大,一个int表示不了,就需要64位表示,就有高位地位。

另说一句,顶楼代码并不一定总是能正确运行,有可能出现映射失败。
------解决方案--------------------



把文件 划分为 若干个 64kb block = filesize / = (64*1024); block_mod= filesize % (64*1024);

每次map相当于 从磁盘IO中读取 一个block大小的数据,然后下次的偏移量为 当前偏移量+64k
每次读取前进一个64k

读书人网 >VC/MFC

热点推荐