欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Windows内存体系(5)-- 内存映射文件技术的使用

程序员文章站 2022-04-03 08:04:49
...

前面的《Windows内存体系(3) – 内存映射文件》文章,对内存映射文件技术的原理进行了介绍,本篇文章着重介绍该技术的使用场景。

一、内存映射文件技术介绍

常用的有Win32 API的CreateFile()WriteFile()ReadFile()和MFC提供的CFile类都可以实现文件的读写操作。一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以平常的文件处理方法进行处理显然是行不通的(效率低下,而且内存没那么大)。目前,对于这种大文件的操作一般是以内存映射文件的方式来加以处理的。

内存映射文件也是Windows的一种内存管理方法,提供了一个统一的内存管理特征,使应用程序可以通过内存指针对磁盘上的文件进行访问。通过文件映射将磁盘文件内容(全部或者部分)与进程虚拟地址空间的某个区域建立映射关联,可以直接对被映射的文件进行访问,而不必执行文件I/O操作也无需对文件内容进行缓冲处理。内存文件映射的这种特性是非常适合于用来管理大尺寸文件的。

二、大文件读写实例

通过C++调用系统API实现文件映射的步骤大致如下:
Windows内存体系(5)-- 内存映射文件技术的使用

示例

本示例先在D:\生成一个大小为1GB的BigFile.data文件,然后使用内存映射技术将该文件内全部填充字符A,随后读取其中的第20000~20100字节,并将这些字节修改为字符B,然后再次读取已验证是否修改成功。

#include <windows.h>

void Test() {
    HANDLE file_ = CreateFile(TEXT("D:\\BigFile.data"), 
        GENERIC_READ | GENERIC_WRITE, 
        0, 
        NULL, 
        OPEN_ALWAYS, 
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (file_ == INVALID_HANDLE_VALUE) {
        printf("CreateFile failed, GLE:%d\n", GetLastError());
        return;
    }

    LARGE_INTEGER filesize;
    filesize.QuadPart = 1024 * 1024 * 1024; // 1GB

    HANDLE mapping_ = CreateFileMapping(file_, NULL, PAGE_READWRITE, filesize.HighPart, filesize.LowPart, NULL);
    if (mapping_ == NULL) {
        printf("CreateFileMapping failed, GLE:%d\n", GetLastError());
        return;
    }

    LARGE_INTEGER offset;
    offset.QuadPart = 0;

    LPVOID mapping_addr = MapViewOfFile(mapping_, FILE_MAP_WRITE | FILE_MAP_READ, offset.HighPart, offset.LowPart, 0);
    if (mapping_addr == NULL) {
        printf("MapViewOfFile failed, GLE:%d\n", GetLastError());
        return;
    }

    // 向文件中填充1GB的字符'A'
    //
    char buf[1024];
    for (int i = 0; i < 1024; i++) {
        buf[i] = 'A';
    }

    // 每次填充1024字节,填充1024*1024次
    for (long l = 0; l < 1024 * 1024; l++) {
        memcpy((LPVOID)((long)mapping_addr + l * 1024), buf, 1024);
    }



    // 填充完毕
    // 读取第20000~20100字节,共100字节
    //
    char read_content[101] = { 0 };
    memcpy(read_content, (LPVOID)((long)mapping_addr + 20000), 100);
    printf("%s\n", read_content);

    // 将第20000~20100字节,共100字节全部修改为字符'B'
    //
    char write_content[100];
    for (int i = 0; i < 100; i++) {
        write_content[i] = 'B';
    }

    memcpy((LPVOID)((long)mapping_addr + 20000), write_content, 100);

    // 再次读取第20000~20100字节,共100字节,验证修改是成功
    //
    memcpy(read_content, (LPVOID)((long)mapping_addr + 20000), 100);
    printf("%s\n", read_content);


    UnmapViewOfFile(mapping_addr);
    CloseHandle(mapping_);
    CloseHandle(file_);

    return;
}

int main()
{
    Test();
    return 0;
}
相关标签: 内存映射