使用Win32/WINAPI通过内存映射文件传输数据

9
我需要将一些数据 - char buffer[100000]; - 传输到由我启动的子进程中。
目前,我正在使用普通文件来实现,父进程将数据写入磁盘上的文件,然后子进程从磁盘读取并删除该文件。但是,这会导致不必要的磁盘写入,因此我想改为使用内存映射文件。
如何创建、写入和读取内存映射文件,以使除在使用页面文件或交换文件时外,不会将任何数据写入磁盘?
编辑:我忘记指定我想要使用原始的WINAPI函数,所以代码没有依赖性。我正在调研如何实现它,并在准备好时发布自己的答案,但如果有人有现成的代码,欢迎为我节省一些精力 :)
3个回答

7

在调用CreateFileMapping时,将INVALID_HANDLE_VALUE作为文件句柄传递:

如果hFile为INVALID_HANDLE_VALUE,则调用进程还必须在dwMaximumSizeHigh和dwMaximumSizeLow参数中指定文件映射对象的大小。在这种情况下,CreateFileMapping会创建一个指定大小的文件映射对象,该对象由系统分页文件而不是文件系统中的文件支持。

您可以使用匿名文件映射对象(将可继承的句柄传递给子处理进程),也可以使用命名文件映射。


由于某些原因,我认为无文件映射始终是匿名的。但我想这就是为什么映射本身可以有名称,而不是映射文件的名称。无论如何,继承可以避免名称冲突问题。 - Ben Voigt
@BenVoigt没错,两种方式都可以。我总是倾向于使用命名对象,但我想那只是我最先学到的技巧。 - David Heffernan

3
我刚在 MSDN 上找到了 一篇很棒的文章, 介绍如何完成这个,包含示例代码,我已将其粘贴在下面。

第一个流程(发送数据):

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>

#define BUF_SIZE 256
TCHAR szName[]=TEXT("Global\\MyFileMappingObject");
TCHAR szMsg[]=TEXT("Message from first process.");

int _tmain()
{
   HANDLE hMapFile;
   LPCTSTR pBuf;

   hMapFile = CreateFileMapping(
                 INVALID_HANDLE_VALUE,    // use paging file
                 NULL,                    // default security
                 PAGE_READWRITE,          // read/write access
                 0,                       // maximum object size (high-order DWORD)
                 BUF_SIZE,                // maximum object size (low-order DWORD)
                 szName);                 // name of mapping object

   if (hMapFile == NULL)
   {
      _tprintf(TEXT("Could not create file mapping object (%d).\n"),
             GetLastError());
      return 1;
   }
   pBuf = (LPTSTR) MapViewOfFile(hMapFile,   // handle to map object
                        FILE_MAP_ALL_ACCESS, // read/write permission
                        0,
                        0,
                        BUF_SIZE);

   if (pBuf == NULL)
   {
      _tprintf(TEXT("Could not map view of file (%d).\n"),
             GetLastError());

       CloseHandle(hMapFile);

      return 1;
   }


   CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
    _getch();

   UnmapViewOfFile(pBuf);

   CloseHandle(hMapFile);

   return 0;
}

第二个进程(接收数据):

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#pragma comment(lib, "user32.lib")

#define BUF_SIZE 256
TCHAR szName[]=TEXT("Global\\MyFileMappingObject");

int _tmain()
{
   HANDLE hMapFile;
   LPCTSTR pBuf;

   hMapFile = OpenFileMapping(
                   FILE_MAP_ALL_ACCESS,   // read/write access
                   FALSE,                 // do not inherit the name
                   szName);               // name of mapping object

   if (hMapFile == NULL)
   {
      _tprintf(TEXT("Could not open file mapping object (%d).\n"),
             GetLastError());
      return 1;
   }

   pBuf = (LPTSTR) MapViewOfFile(hMapFile, // handle to map object
               FILE_MAP_ALL_ACCESS,  // read/write permission
               0,
               0,
               BUF_SIZE);

   if (pBuf == NULL)
   {
      _tprintf(TEXT("Could not map view of file (%d).\n"),
             GetLastError());

      CloseHandle(hMapFile);

      return 1;
   }

   MessageBox(NULL, pBuf, TEXT("Process2"), MB_OK);

   UnmapViewOfFile(pBuf);

   CloseHandle(hMapFile);

   return 0;
}

1
第一个进程中的一个错误:CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));没有复制空终止符 - 请使用 _tcscpy(pBuf, szMsg);memcpy(pBuf, szMsg, sizeof szMsg); 进行替换。 - beerboy
从安全角度来看,命名内存映射文件是一个不好的想法。 - Sergey Skoblikov

3
您可以使用匿名文件映射(David Heffernan的回答更详细地介绍了这一步骤),并进行句柄继承/句柄复制。例如,将父进程中的句柄通过命令行传递,在子进程中使用DuplicateHandle获取有效的句柄。

CreateFileMapping文档说明:

多个进程可以通过使用单个共享文件映射对象或创建由同一文件支持的单独文件映射对象来共享相同文件的视图。单个文件映射对象可以通过在进程创建时继承句柄、复制句柄或通过名称打开文件映射对象来被多个进程共享。有关更多信息,请参见CreateProcess、DuplicateHandle和OpenFileMapping函数。

但是,使用RAM磁盘可能更容易。


Windows 中有 RAM 磁盘吗?我认为这只是 Linux 专属的。 - sashoalm
1
@sashoalm:在Windows中有很多第三方软件可以创建RAM磁盘。 - Ben Voigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接