首页 > 系统相关 >17.5 稀疏调拨的内存映射文件--《Windows核心编程》

17.5 稀疏调拨的内存映射文件--《Windows核心编程》

时间:2022-11-21 12:13:46浏览次数:60  
标签:文件 -- 稀疏 Windows 内存 FILE 17.5 NULL hFile

原文链接: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处,很大了,不需要再设置文件尾了。

 

标签:文件,--,稀疏,Windows,内存,FILE,17.5,NULL,hFile
From: https://www.cnblogs.com/renleiguanchashi/p/16910984.html

相关文章

  • 笔记本主板烧了什么症状
    日常使用计算机的时候,有的用户可能就会遇到自己笔记本电脑出现了一些问题,不确定是不是主板损坏了。那么笔记本主板烧了什么症状呢?主板烧了可能会出现风扇转不显示、蓝屏、......
  • eslint插件屁事多,解决方案
    eslint插件会检测是否写了分号,写了分号就报错,那么我们就设置一个文件".prettierrc"文件在里面写"semi":false从而在格式化代码的时候自动将分号去掉;写"singleQuote"......
  • C#学习笔记
    目录Hello,world!基本语法经典实例标识符C#关键字数据类型值类型(Valuetypes)引用类型(Referencetypes)对象(Object)类型动态(Dynamic)类型字符串(String)类型指针类型(......
  • 迅为iTOP3568开发板Android11获取root权限关闭selinux
    本文档所需资料在网盘资料“iTOP-3568开发板\02_【iTOP-RK3568开发板】开发资料\06_Android系统开发配套资料\02_Android11获取root权限配套资料”目录下。本文档参......
  • WordPress编辑器支持一键粘贴
    ​如何做到ueditor批量上传word图片?1、前端引用代码<!DOCTYPE html PUBLIC "-//W3C//DTDXHTML1.0Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-......
  • Python爬取酷狗音乐Top500首歌曲并下载到本地
    #@Author:林云#@Time:2022/11/2018:05#@File:KuGouYinyue.py#@Project:PycharmProjectsimportjsonimportosfromtimeimportsleepimportrequestsfromlx......
  • Bot in Discord with discord.js (8)
    Chapter9-事件处理Eventhandling这一章只是根据新的discord.jsv14.6.0,对已有文件进行小修小补。如果你是跟着本教程前几章来的,不要跳过本章!根据官方的说法,Node......
  • The SSL connection could not be established, see inner exception.
    C#请求HTTPS地址的故障分析和TLS知识点总结  背景介绍近期收到同事反馈,在C#程序中通过HTTPClient请求一个HTTPS的地址时,在本地开发环境和测试环境均能正常执行,而......
  • 5款办公软件中的翘楚,免费无广告
    如今,工作和学习都离不开电脑,所以电脑里的软件自然也是必不可少的,但是电脑软件那么多,不可能每个都装上吧,所以我们要装好用的、实用的,下面给大家分享5款好用到爆的软件,很多懂......
  • java报错:The reference to entity
    java关于报错:Thereferencetoentity"characterEncoding"mustendwiththe';'delimiter.Java解析XML文件错误。错误信息提示代码类似如下:Thereferencetoentity"......