17.5 稀疏调拨的内存映射文件--《Windows核心编程》
原文链接:https://www.likecs.com/show-306421749.html,原文中代码是C++MFC程序,更详细。本文是C语言测试代码。
(1)稀疏文件(Sparse File)定义
指的是文件中出现大量的0数据,这些数据对我们用处不大,但是却一样的占用空间。NTFS文件系统对此进行了优化,那些无用的0字节被用一定的算法压缩起来。例如声明一个很大的稀疏文件(如100GB),这个文件实际上并不需要占用那么大的空,内部都是一些无用的0数据,那么NTFS就会利用算法释放这些无用的0字节空间,这是对磁盘占用空间的一种优化。但要注意FAT32并不支持稀疏文件的压缩。
实例场景:假设我们要创建一个内存映射文件来存储录音的数据。当用户说话的时候,我们把数字音频数据写入到内存缓存,并以磁盘文件作为内存缓存的后备存储器。一个部分调拨的内存映射文件当然是最简单和最高效的方式。问题在于我们并不知道用户在停止录制之前会说多久,可能是五分钟,可能是五小时…这差距还是很大的。所以我们需要一个足够大的文件来保存这些数据。单是,在使用稀疏调拨的内存映射文件时,大小并没有多大关系。
(2)与稀疏文件操作有关的函数
①判断系统是否支持稀疏文件:GetVolumeInformation 函数
通过传出的参数lpFileSystemFlags & FILE_SUPPORTS_SPARSE_FILES判断结果是否为FILE_SUPPORTS_SPARSE_FILES。
②判断一个文件是否是稀疏文件:GetFileInformationByHandle 函数
BY_HANDLE_FILE_INFORMATION stFileInfo;
GetFileInformationByHandle(hFile, &stFileInfo);
当stFileInfo.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE为TRUE时表示稀疏文件。
③产生一个稀疏文件:DeviceIoControl(hFile,FSCTL_SET_SPARS,...);
大部分文件,在改变它的EndOfFile的时候,中间的空白会被操作系统填0,也就是说,如果用SetFilePointer和SetEndOfFile来产生一个很大的文件,那么这个文件它占用的是真正的磁盘空间,即使里面全是0,系统默认的也会在DeviceIoControl()中的ControlCode里用FSCTL_SET_ZERO_DATA标记,这个标记使得那些文件空洞被0所填充。为了节省磁盘空间,我们必须把一个文件声明为稀疏文件,以便让系统把那些无用的0字节压缩,并释放相应的磁盘空间,要将标记改为FSCTL_SET_SPARSE。
④查找稀疏文件中包含非零数据的范围:DeviceIoControl(hFile, FSCTL_QUERY_ALLOCATED_RANGES,...);
(3)以稀疏文件为后备存储器的内存映射文件(一般的使用步骤)
① 创建文件:hFile = CreateFile(...);
② 产生稀疏文件
DeviceIoControl(hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dw, NULL));
③ 创建内存映射文件对象
hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE,…);
④ 映射到进程的地址空间
pvFile = MapViewOfFile(hFileMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0);
⑤ 读、写文件(像直接读写内存一样的读写文件)
⑥ 撤消映射:UnmapViewOfFile(pvFile);
⑦ 关闭句柄:CloseHandle(hFileMap);CloseHandle(hFile);
#include <windows.h>
#include <iostream>
#include <string>
#include <winioctl.h>
using namespace std;
#define FL (1024*1024)
#define FH 0
char g_szPath[MAX_PATH];
char g_Volume[MAX_PATH];
//(3797960)
int main()
{
// 先检查当前磁盘是否支持稀疏文件
memset(g_Volume, 0, MAX_PATH);
GetCurrentDirectory(MAX_PATH, g_szPath);
strncpy_s(g_Volume, g_szPath, 3);
DWORD dwFileSystemFlags = 0;
bool bOK = GetVolumeInformation(g_Volume, NULL, 0, NULL, NULL, &dwFileSystemFlags, NULL, 0);// 获取磁盘信息
bOK = bOK && (dwFileSystemFlags & FILE_SUPPORTS_SPARSE_FILES);// dwFileSystemFlags & FILE_SUPPORTS_SPARSE_FILES 查看标志位判断对稀疏文件的支持
if (!bOK)
{
// 如果系统不支持稀疏文件,就直接关闭程序
return 1;
}
// ① 创建文件
DWORD dw;
string FileName = "MMF.txt";
HANDLE hFile = CreateFile(FileName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
int err = GetLastError();
if (hFile == INVALID_HANDLE_VALUE)
{
cout << "打开文件失败" << endl;
return 1;
}
// ② 产生稀疏文件
bool ret = DeviceIoControl(hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dw, NULL);// 设置稀疏文件
err = GetLastError();
if (!ret)
{
cout << "err no:" << err << endl;
return 2;
}
// ③ 创建内存映射文件对象
HANDLE hFileMapping = ::CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 1024 * 1024, NULL); // 申请1M,但实际测试只有64KB
if (!hFileMapping)
{
CloseHandle(hFileMapping);
cout << "创建文件映像失败" << endl;
return 2;
}
// ④ 映射到进程的地址空间
LPVOID pFile = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
// ⑤ 读、写文件(像直接读写内存一样的读写文件)
char WriteIn[] = "写入测试";
memcpy((LPVOID)((INT64)pFile + 0x120), WriteIn, strlen(WriteIn) + 1);
FlushViewOfFile(pFile, 4); // 不强制刷新的话,后面获取文件磁盘占用大小会失败,因为还没刷新到文件中。
LARGE_INTEGER lpFileSize;
GetFileSizeEx(hFile, &lpFileSize);
printf("文件实际大小(压缩前大小):\n%lld bytes\n%4.2f KB\n%4.2f MB\n%4.2f GB\n", lpFileSize.QuadPart, (float)lpFileSize.QuadPart / 1024, (float)lpFileSize.QuadPart / (1024 * 1024), (float)lpFileSize.QuadPart / (1024 * 1024 * 1024));
ULARGE_INTEGER ulFileSize;
ulFileSize.LowPart = GetCompressedFileSize("C:\\Users\\Lenovo\\source\\repos\\test2333\\test2333\\MMF.txt", &ulFileSize.HighPart);
err = GetLastError();
printf("文件磁盘大小\n%lld bytes\n%4.2f KB\n%4.2f MB\n%4.2f GB\n", ulFileSize.QuadPart, (float)ulFileSize.QuadPart / 1024, (float)ulFileSize.QuadPart / (1024 * 1024), (float)ulFileSize.QuadPart / (1024 * 1024 * 1024));
// ⑥ 撤消映射
// ⑦ 关闭句柄
UnmapViewOfFile(pFile);
CloseHandle(hFileMapping);
CloseHandle(hFile);
system("pause");
return 0;
}
测试结果:申请1M,但实际测试只有64KB。
测试写入时也不用考虑文件尾,因为1M大小就是文件尾。像是之前写的读写文件的测试代码(https://www.cnblogs.com/renleiguanchashi/p/16910946.html),如果写入超过文件尾,会写不进去,要先设置文件尾更大。这里因为文件尾是1M处,很大了,不需要再设置文件尾了。


17.5 稀疏调拨的内存映射文件--《Windows核心编程》的更多相关文章
- 文件映射(Windows核心编程)
映射内存的可执行文件和dll 当一个线程调用CreateProcess的时候,系统会执行以下步骤: 系统会先确定CreateProcess所指定的可执行文件的所在位置.如果找不到文件,那么Create ...
- 第17章 内存映射文件(3)_稀疏文件(Sparse File)
17.8 稀疏调拨的内存映射文件 17.8.1 稀疏文件简介 (1)稀疏文件(Sparse File):指的是文件中出现大量的0数据,这些数据对我们用处不大,但是却一样的占用空间.NTFS文件系统对此 ...
- 《windows核心编程》 17章 内存映射文件
内存映射文件主要用于以下三种情况: 系统使用内存映射文件载入并运行exe和dll,这大量节省了页交换文件的空间以及应用程序的启动时间 开发人员可以使用内存映射文件来访问磁盘上的数据文件.这使得我们可以 ...
- 【WIN32进阶之路】:内存映射文件
第一章:源起 遇到一个问题,如果一个客户数据文件有2g大,客户要通过界面查询文件中的数据并用列表控件显示数据,要怎么处理这个文件才能让应用程序不会长时间无响应,客户感觉不到程序的卡顿? 第二章:解决 ...
- 【NIO】NIO之浅谈内存映射文件原理与DirectMemory
Java类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...
- JAVA NIO之浅谈内存映射文件原理与DirectMemory
JAVA类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...
- 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...
- 《windows核心编程系列》十六谈谈内存映射文件
内存映射文件允许开发人员预订一块地址空间并为该区域调拨物理存储器,与虚拟内存不同的是,内存映射文件的物理存储器来自磁盘中的文件,而非系统的页交换文件.将文件映射到内存中后,我们就可以在内存中操作他们了 ...
- C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 VC中进程与进程之间共享内存 .net环境下跨进程、高频率读写数据 使用C#开发Android应用之WebApp 分布式事务之消息补偿解决方案
C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). ...
- Windows核心编程 第十七章 -内存映射文件(下)
17.3 使用内存映射文件 若要使用内存映射文件,必须执行下列操作步骤: 1) 创建或打开一个文件内核对象,该对象用于标识磁盘上你想用作内存映射文件的文件. 2) 创建一个文件映射内核对象,告诉系统该 ...
随机推荐
- 《深入理解计算机系统》(CSAPP)读书笔记 —— 第五章 优化程序性能
写程序最主要的目标就是使它在所有可能的情况下都正确工作.一个运行得很快但是给出错误结果的程序没有任何用处.程序员必须写出清晰简洁的代码,这样做不仅是为了自己能够看懂代码,也是为了在检査代码和今后需要修 ...
- 玛珍,玛珍,margin!
最近在整理巩固面试相关的资料,又看到了熟悉的老朋友:margin,当时觉得其读起来很亲切,现在又发现很多遗忘的知识点. 了解margin margin,译为"外边缘",在CSS作为 ...
- 缓存选型:Redis or MemCache
★ Redis24篇集合 1 背景 互联网产品为了保证高性能和高可用性,经常会使用缓存来进行架构设计.最常用的就是使用Redis了,也有部分企业会选择使用Memcache. 所以了解 Redis 和 ...
- 【C/C++】 变参函数
#include <stdio.h> #include <stdbool.h> #include <stdarg.h> #define MLA_ASSERT(exp ...
- 【ARM】重新定义低级库函数,以便能够直接使用 C 库中的高级库函数
Redefining low-level library functions to enable direct use of high-level library functions in the C ...
- CSS - 使图片自适应
img { height: 100%; object-fit: cover; }
- [转帖]JVM随笔 --- 安全点(safe point)与 安全区域( safe region)
https://zhuanlan.zhihu.com/p/461298916 11 人赞同了该文章 最近回顾 JVM safe point 与 safe region 又有一些新的感悟与收获,特别写篇 ...
- [转帖]tgz 安装clickhouse
一.什么是clickhouse ClickHouse是开源的列式存储数据库(DBMS),主要用于在线处理查询(OLAP),能够使用SQL查询实时生成数据分析报告. 下面介绍下安装clickhouse. ...
- [转帖]JMeter设置Http代理对web或者app进行录制
https://www.cnblogs.com/jingdenghuakai/p/11125846.html 一.录制web 1.首先保证JMeter的安装环境都正确.启动JMeter:在安装路径的b ...
- [转帖]VMware Workstation PRO 17.0.2正式版+激活密钥
https://www.isharepc.com/36181.html VMware Workstation PRO 17是一个简化的桌面虚拟化应用程序. 它在同一台计算机上运行一个或多个操作系统而无 ...