原文链接: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核心编程》的更多相关文章

  1. 文件映射(Windows核心编程)

    映射内存的可执行文件和dll 当一个线程调用CreateProcess的时候,系统会执行以下步骤: 系统会先确定CreateProcess所指定的可执行文件的所在位置.如果找不到文件,那么Create ...

  2. 第17章 内存映射文件(3)_稀疏文件(Sparse File)

    17.8 稀疏调拨的内存映射文件 17.8.1 稀疏文件简介 (1)稀疏文件(Sparse File):指的是文件中出现大量的0数据,这些数据对我们用处不大,但是却一样的占用空间.NTFS文件系统对此 ...

  3. 《windows核心编程》 17章 内存映射文件

    内存映射文件主要用于以下三种情况: 系统使用内存映射文件载入并运行exe和dll,这大量节省了页交换文件的空间以及应用程序的启动时间 开发人员可以使用内存映射文件来访问磁盘上的数据文件.这使得我们可以 ...

  4. 【WIN32进阶之路】:内存映射文件

    第一章:源起  遇到一个问题,如果一个客户数据文件有2g大,客户要通过界面查询文件中的数据并用列表控件显示数据,要怎么处理这个文件才能让应用程序不会长时间无响应,客户感觉不到程序的卡顿? 第二章:解决 ...

  5. 【NIO】NIO之浅谈内存映射文件原理与DirectMemory

    Java类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...

  6. JAVA NIO之浅谈内存映射文件原理与DirectMemory

    JAVA类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...

  7. 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  8. 《windows核心编程系列》十六谈谈内存映射文件

    内存映射文件允许开发人员预订一块地址空间并为该区域调拨物理存储器,与虚拟内存不同的是,内存映射文件的物理存储器来自磁盘中的文件,而非系统的页交换文件.将文件映射到内存中后,我们就可以在内存中操作他们了 ...

  9. C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 VC中进程与进程之间共享内存 .net环境下跨进程、高频率读写数据 使用C#开发Android应用之WebApp 分布式事务之消息补偿解决方案

    C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). ...

  10. Windows核心编程 第十七章 -内存映射文件(下)

    17.3 使用内存映射文件 若要使用内存映射文件,必须执行下列操作步骤: 1) 创建或打开一个文件内核对象,该对象用于标识磁盘上你想用作内存映射文件的文件. 2) 创建一个文件映射内核对象,告诉系统该 ...

随机推荐

  1. mysql 使用 trim去不掉空格 解决

    使用mysql8.0时 发现 有几个空字符串怎么也过滤不掉,使用 is not null.trim()<>''.length()>=1都不行,最后查了一些资料说 trim只能去除半角 ...

  2. 【译】 双向流传输和ASP.NET Core 3.0上的gRPC简介

    原文相关 原文作者:Eduard Los 原文地址:https://medium.com/@eddyf1xxxer/bi-directional-streaming-and-introduction- ...

  3. R语言—数据基础及练习

    ## 创建leadership数据框 manager <- c(1,2,3,4,5) date <-c("10/24/08","10/28/08", ...

  4. ORACLE Enterprise Manager Database Express(OEM-express)(遇到localhost拒绝访问情况)配置端口和启动方法

    1.问题 之前一直进不去ORACLE Enterprise Manager Database Express,显示的是localhost拒绝了访问,经过查阅知道是没有配置相应端口. 2.解决方法 转载 ...

  5. js jquery - 获取元素(父节点,子节点,兄弟节点),元素筛选 (转载)

    一 ,  js 获取元素(父节点,子节点,兄弟节点) var test = document.getElementById("test"); var parent = test.p ...

  6. JMS 服务器健康检查

    JMS所有服务器程序,包括Gateway.GatewayReferee.Proxy.TokenServer.以及编写的微服务器,都支持使用第三方工具进行健康检查. 使用telnet 进行健康检查 向任 ...

  7. k8s~istio的安装与核心组件

    安装istio 在线安装:https://istio.io/latest/docs/setup/getting-started/#download 或者直接在这里下载:https://github.c ...

  8. Redis监控方法之二

    Redis监控方法之二 背景 前期整理过使用 exporter + prometheus 方式进行Redis监控的搭建过程 最近给同事研究clickhouse时发现 clickhouse 有对应的pl ...

  9. [转帖]【JVM】JVM源码分析之Metaspace解密

    概述 metaspace,顾名思义,元数据空间,专门用来存元数据的,它是jdk8里特有的数据结构用来替代perm,这块空间很有自己的特点,前段时间公司这块的问题太多了,主要是因为升级了中间件所致,看到 ...

  10. [转帖]UTF8 和 AL32UTF8 的区别

    本文章向大家介绍UTF8 和 AL32UTF8 的区别,主要内容包括 .使用实例.应用技巧.基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下.  UTF8 和 AL32UTF8 ...