Windows编程之文件映射

一、文件映射概述

  • 内存映射文件:文件映射可以用于将磁盘上的文件映射到内存中。这样,文件的内容可以直接从内存中读取或写入,而不必通过磁盘I/O进行数据交换。这对于处理大型文件或需要频繁读写的文件非常有用,可以提高性能。

  • 进程间通信:文件映射也可用于进程间通信(IPC),允许不同进程之间共享数据。多个进程可以将同一个文件映射到各自的地址空间中,并在其中写入或读取数据,从而实现进程间通信和数据共享。

  • 共享内存:文件映射还可以用于在同一进程的不同线程之间共享内存数据。不同线程可以共享同一个文件映射对象,以实现线程间数据共享和同步。

下面是关于Windows中文件映射的一些关键概念和使用方法:

  • 文件映射对象:文件映射对象是一个内核对象,充当了内存中的文件映射区域的控制点。每个文件映射对象都有一个唯一的名称,可以用于在不同进程之间共享。使用CreateFileMapping函数创建文件映射对象。

  • 文件映射视图:文件映射视图是文件映射对象在进程地址空间中的一个特定部分。它允许进程直接读取或写入文件映射区域的数据。使用MapViewOfFile函数将文件映射对象映射到进程的地址空间中。

  • 访问权限:文件映射对象可以使用不同的保护级别来控制对映射视图的访问权限,包括只读、读写和写时复制等。

  • 文件句柄:文件映射通常需要与文件句柄关联,以便将文件内容映射到内存中。如果不关联文件句柄,可以创建匿名的文件映射对象。

文件映射是一种强大的技术,可以用于解决多进程协作、内存映射文件、大型数据集的高效访问等多种问题。在使用文件映射时,要小心处理共享资源的同步和互斥,以确保数据的完整性和正确性。

二、相关API函数介绍

1.CreateFileMapping函数

CreateFileMapping 函数用于创建一个文件映射对象,该对象可以用于在不同进程之间共享内存数据,或者在同一进程的不同地址空间中共享数据。这个函数通常用于在多个进程之间共享数据的同步和通信,以及在内存中映射大文件的部分内容,以提高文件I/O性能。

函数原型:

HANDLE CreateFileMapping(
  HANDLE                hFile,
  LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
  DWORD                 flProtect,
  DWORD                 dwMaximumSizeHigh,
  DWORD                 dwMaximumSizeLow,
  LPCTSTR               lpName
);

参数说明:

  • hFile:可选参数,可以是一个文件句柄或者一个无效句柄(INVALID_HANDLE_VALUE)。如果为文件句柄,将文件的内容映射到内存中;如果为 INVALID_HANDLE_VALUE,将创建一个匿名的文件映射对象,用于共享内存。

  • lpFileMappingAttributes:可选参数,用于设置文件映射对象的安全属性。通常设置为 NULL,表示使用默认安全属性。

  • flProtect:用于设置文件映射对象的访问保护级别,控制对映射内存的访问权限。可以使用以下常量之一:

    1. PAGE_READONLY:只读
    2. PAGE_READWRITE:读写
    3. PAGE_WRITECOPY:写时复制
    4. PAGE_EXECUTE_READ:可执行和只读
    5. PAGE_EXECUTE_READWRITE:可执行和读写
  • dwMaximumSizeHighdwMaximumSizeLow:用于指定文件映射对象的最大大小。通常,可以将 dwMaximumSizeHigh 设置为 0,而 dwMaximumSizeLow 设置为映射的内存大小。

  • lpName:可选参数,用于指定文件映射对象的名称。如果在不同进程之间共享文件映射对象,可以使用相同的名称。如果为 NULL,则创建一个匿名的文件映射对象。

返回值:

如果函数调用成功,它将返回文件映射对象的句柄(HANDLE),否则返回 NULL。

2.MapViewOfFile函数

MapViewOfFile 函数用于将文件映射对象映射到当前进程的地址空间,从而允许进程访问和操作共享内存中的数据。这个函数通常与 CreateFileMapping 函数一起使用,用于创建文件映射对象并创建映射视图。 

函数原型:

HANDLE CreateFileMapping(
  HANDLE                hFile,
  LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
  DWORD                 flProtect,
  DWORD                 dwMaximumSizeHigh,
  DWORD                 dwMaximumSizeLow,
  LPCTSTR               lpName
);

参数说明:

  • hFile:可选参数,可以是一个文件句柄或者一个无效句柄(INVALID_HANDLE_VALUE)。如果为文件句柄,将文件的内容映射到内存中;如果为 INVALID_HANDLE_VALUE,将创建一个匿名的文件映射对象,用于共享内存。

  • lpFileMappingAttributes:可选参数,用于设置文件映射对象的安全属性。通常设置为 NULL,表示使用默认安全属性。

  • flProtect:用于设置文件映射对象的访问保护级别,控制对映射内存的访问权限。可以使用以下常量之一:

    1. PAGE_READONLY:只读
    2. PAGE_READWRITE:读写
    3. PAGE_WRITECOPY:写时复制
    4. PAGE_EXECUTE_READ:可执行和只读
    5. PAGE_EXECUTE_READWRITE:可执行和读写
  • dwMaximumSizeHighdwMaximumSizeLow:用于指定文件映射对象的最大大小。通常,可以将 dwMaximumSizeHigh 设置为 0,而 dwMaximumSizeLow 设置为映射的内存大小。

  • lpName:可选参数,用于指定文件映射对象的名称。如果在不同进程之间共享文件映射对象,可以使用相同的名称。如果为 NULL,则创建一个匿名的文件映射对象。

返回值:

如果函数调用成功,它将返回文件映射对象的句柄(HANDLE),否则返回 NULL。

3.OpenFileMapping函数

OpenFileMapping 函数用于打开现有的文件映射对象,以便不同进程可以共享相同的内存区域。这个函数通常与 CreateFileMapping 函数一起使用,以实现在不同进程之间共享数据的同步和通信。  

函数原型:

HANDLE OpenFileMapping(
  DWORD   dwDesiredAccess,
  BOOL    bInheritHandle,
  LPCTSTR lpName
);

参数说明:

  • dwDesiredAccess:指定打开文件映射对象时的访问权限。可以使用以下常量之一或它们的组合:

    1. FILE_MAP_READ:只读
    2. FILE_MAP_WRITE:读写
    3. FILE_MAP_EXECUTE:可执行
    4. FILE_MAP_COPY:写时复制
    5. FILE_MAP_ALL_ACCESS:所有权限(读、写、执行)
  • bInheritHandle:一个布尔值,指定返回的句柄是否可被子进程继承。通常设置为 FALSE,表示不允许子进程继承句柄。

  • lpName:要打开的文件映射对象的名称。这个名称通常由创建文件映射对象的进程指定,并用于在不同进程之间标识相同的文件映射对象。

返回值:

如果函数调用成功,它将返回文件映射对象的句柄(HANDLE),否则返回 NULL

三、文件映射实现进程间的通信  

创建映射内存并写入数据:

#include <iostream>
#include <Windows.h>

int main() {
    HANDLE hFileMapping;
    LPVOID pFileView;

    //创建文件映射对象
    hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, L"ShareFile");
    if (hFileMapping == NULL) {
        std::cout << "CreateFileMapping Failed:" << GetLastError() << std::endl;

        return -1;
    }

    //映射文件视图到当前进程的地址空间
    pFileView = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
    if (pFileView == NULL) {
        CloseHandle(hFileMapping);
        std::cout << "MapViewOfFile Failed" << std::endl;

        return -1;
    }

    while (true) {
        std::cout << "Hello from WriteMapping Process!" << std::endl;

        Sleep(1000 * 3);
        //写入数据到共享内存
        strcpy((char*)pFileView, "Hello from WriteMapping Process!");
    }

    //解除映射并关闭文件映射对象
    UnmapViewOfFile(pFileView);
    CloseHandle(hFileMapping);

    return 0;
}

打开映射内存并读出数据:

#include <iostream>
#include <Windows.h>

int main() {
	HANDLE hFileMapping;
	LPVOID pFileView;

	hFileMapping = OpenFileMapping(FILE_MAP_READ, FALSE, L"ShareFile");
	if (hFileMapping == NULL) {
		std::cout << "OpenFileMapping Failed:" << GetLastError() << std::endl;

		return -1;
	}

	pFileView = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
	if (pFileView == NULL) {
		CloseHandle(hFileMapping);

		return -1;
	}

	//从共享内存中读取数据
	while (true) {
		Sleep(1000 * 3);

		std::cout << "Received data in WriteMapping Process:" << (char*)pFileView << std::endl;
	}
	
	//解除映射和关闭文件映射对象
	UnmapViewOfFile(pFileView);
	CloseHandle(hFileMapping);
}

效果展示:  

posted @ 2023-11-07 16:04  TechNomad  阅读(54)  评论(0编辑  收藏  举报