当前位置:   article > 正文

windows C++:进程间通信高实时性、安全、数据量大的通信方式(一)文件映射 (File Mapping)_windows内存映射高速通讯

windows内存映射高速通讯

       

目录

一、文件映射 (File Mapping)

1. 简单的介绍

2. 文件映射的优势

3. 需要用到的API

CreateFile

CreateFileMapping

MapViewOfFile

UnmapViewOfFile

CloseHandle

4. 安全性

二、文件映射底层原理

1. 文件系统与文件句柄

2. 创建文件映射对象

3. 内存管理器与虚拟内存

4. 共享内存

5. 文件映射的拆解

关键数据结构和函数

底层调用流程图

三、父子进程间通信的示例

四、优、缺点总结

优点:

缺点:

总结

五、改进

sharedMemoryW.cpp:


        windows进程间通信是写多进程程序的必修课,高实时性、安全、数据量大的通信方式是很必要的,今天我们来看看文件映射

一、文件映射 (File Mapping)

1. 简单的介绍

        文件映射通过将文件的部分或全部内容映射到一个或多个进程的虚拟地址空间,使得这些进程可以像访问普通内存一样访问文件内容。这个过程涉及以下步骤:

  1. 创建或打开文件:进程首先需要创建或打开一个文件。
  2. 创建文件映射对象:通过调用 Windows API 函数 CreateFileMapping,创建一个文件映射对象。这个对象表示文件的映射视图。
  3. 映射视图到内存:使用 MapViewOfFile 函数将文件映射对象的一个视图映射到进程的地址空间中。

2. 文件映射的优势

  1. 高实时性:文件映射将文件内容直接映射到内存,减少了传统 I/O 操作的开销,实现了高效的数据访问。
  2. 高安全性:可以通过设置文件和内存映射对象的权限来控制不同进程对文件的访问。
  3. 处理大数据量:适合处理大数据量的文件,通过内存映射可以高效地进行读写操作。
  4. 共享内存:在同一台计算机上的多个进程可以共享同一块内存,简化了进程间通信的复杂性。

3. 需要用到的API

CreateFile

        用于创建或打开一个文件。

  1. HANDLE CreateFile(
  2. LPCSTR lpFileName,
  3. DWORD dwDesiredAccess,
  4. DWORD dwShareMode,
  5. LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  6. DWORD dwCreationDisposition,
  7. DWORD dwFlagsAndAttributes,
  8. HANDLE hTemplateFile
  9. );

参数:

  • lpFileName (LPCSTR): 文件名。
  • dwDesiredAccess (DWORD): 访问权限。常见值包括 GENERIC_READGENERIC_WRITE 等。
  • dwShareMode (DWORD): 共享模式。常见值包括 FILE_SHARE_READFILE_SHARE_WRITE 等。
  • lpSecurityAttributes (LPSECURITY_ATTRIBUTES): 安全属性指针,可为 NULL。
  • dwCreationDisposition (DWORD): 如何创建文件。常见值包括 CREATE_NEWCREATE_ALWAYSOPEN_EXISTING 等。
  • dwFlagsAndAttributes (DWORD): 文件属性和标志。常见值包括 FILE_ATTRIBUTE_NORMALFILE_FLAG_OVERLAPPED 等。
  • hTemplateFile (HANDLE): 模板文件句柄,可为 NULL。

        为了确保文件映射的安全性,可以使用 Windows 的安全属性(SECURITY_ATTRIBUTES)来设置访问控制权限。这样可以限制哪些进程可以访问或修改映射的文件内容。

为了确保文件映射的安全性,可以使用 Windows 的安全属性(SECURITY_ATTRIBUTES)来设置访问控制权限。这些安全属性允许您指定一个安全描述符,该描述符定义了哪些用户和组可以访问或修改映射的文件内容。以下是如何使用 SECURITY_ATTRIBUTES 结构来设置访问控制权限的步骤:

  1. 创建一个安全描述符:使用 InitializeSecurityDescriptor 函数初始化一个安全描述符。
  2. 设置访问控制列表 (ACL):使用 SetSecurityDescriptorDacl 函数为安全描述符设置一个访问控制列表 (ACL),定义访问权限。
  3. 初始化 SECURITY_ATTRIBUTES 结构:将安全描述符包含在 SECURITY_ATTRIBUTES 结构中。
  4. 传递 SECURITY_ATTRIBUTES 结构:在调用 CreateFileMapping 或其他相关 API 时,将 SECURITY_ATTRIBUTES 结构传递给它们。
  1. // 定义安全描述符字符串 (SDDL)
  2. // 这里设置为允许所有用户读取和写入
  3. LPCSTR sddl = "D:P(A;;GA;;;WD)";
  4. // 创建一个安全描述符
  5. PSECURITY_DESCRIPTOR pSD = NULL;
  6. if (!ConvertStringSecurityDescriptorToSecurityDescriptorA(sddl, SDDL_REVISION_1, &pSD, NULL)) {
  7. // 处理错误
  8. printf("ConvertStringSecurityDescriptorToSecurityDescriptorA 错误: %d\n", GetLastError());
  9. return 1;
  10. }
  11. // 初始化 SECURITY_ATTRIBUTES 结构
  12. SECURITY_ATTRIBUTES sa;
  13. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  14. sa.lpSecurityDescriptor = pSD;
  15. sa.bInheritHandle = FALSE;
  16. // 创建或打开文件
  17. HANDLE hFile = CreateFile(
  18. "example.txt",
  19. GENERIC_READ | GENERIC_WRITE,
  20. 0,
  21. &sa, // 使用自定义的安全属性
  22. CREATE_ALWAYS,
  23. FILE_ATTRIBUTE_NORMAL,
  24. NULL
  25. );

 关键点说明:

  • 安全描述符字符串 (SDDL): 示例中的 SDDL "D:P(A;;GA;;;WD)" 设置了一个允许所有用户读取和写入的安全描述符。您可以根据需求调整这个字符串以设置不同的权限。
  • ConvertStringSecurityDescriptorToSecurityDescriptorA: 将 SDDL 转换为实际的安全描述符。
  • SECURITY_ATTRIBUTES: 包含了安全描述符,用于设置文件和文件映射对象的安全属性。

通过上述步骤和示例代码,您可以设置文件映射的安全属性,控制哪些进程可以访问或修改映射的文件内容。

        总之,文件映射是一种高效、灵活的进程间通信机制,特别适合需要处理大块数据并且要求高实时性和高安全性的场景。通过合理的权限设置和内存管理,可以充分利用文件映射的优势,提高系统的整体性能。

CreateFileMapping

        用于创建一个文件映射对象。

  1. HANDLE CreateFileMapping(
  2. HANDLE hFile,
  3. LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
  4. DWORD flProtect,
  5. DWORD dwMaximumSizeHigh,
  6. DWORD dwMaximumSizeLow,
  7. LPCSTR lpName
  8. );

参数:

  • hFile (HANDLE): 文件句柄。
  • lpFileMappingAttributes (LPSECURITY_ATTRIBUTES): 安全属性指针,可为 NULL。
  • flProtect (DWORD): 保护属性。常见值包括 PAGE_READONLYPAGE_READWRITE 等。
  • dwMaximumSizeHigh (DWORD): 映射文件的最大尺寸(高位)。
  • dwMaximumSizeLow (DWORD): 映射文件的最大尺寸(低位)。
  • lpName (LPCSTR): 文件映射对象的名字,可为 NULL。

返回值: 成功返回文件映射对象句柄,失败返回 NULL。

  1. HANDLE hFileMapping = CreateFileMapping(
  2. hFile,
  3. NULL,
  4. PAGE_READWRITE,
  5. 0,
  6. 1024,
  7. "Local\\MyFileMapping"
  8. );
  9. if (hFileMapping == NULL) {
  10. // 处理错误
  11. }

MapViewOfFile

        将文件映射对象的一个视图映射到进程的地址空间中。

  1. LPVOID MapViewOfFile(
  2. HANDLE hFileMappingObject,
  3. DWORD dwDesiredAccess,
  4. DWORD dwFileOffsetHigh,
  5. DWORD dwFileOffsetLow,
  6. SIZE_T dwNumberOfBytesToMap
  7. );

参数:

  • hFileMappingObject (HANDLE): 文件映射对象句柄。
  • dwDesiredAccess (DWORD): 访问权限。常见值包括 FILE_MAP_READFILE_MAP_WRITE 等。
  • dwFileOffsetHigh (DWORD): 文件偏移量的高位。
  • dwFileOffsetLow (DWORD): 文件偏移量的低位。
  • dwNumberOfBytesToMap (SIZE_T): 映射的字节数。

 返回值: 成功返回映射视图的指针,失败返回 NULL。

  1. LPVOID lpBaseAddress = MapViewOfFile(
  2. hFileMapping,
  3. FILE_MAP_READ,
  4. 0,
  5. 0,
  6. 0
  7. );
  8. if (lpBaseAddress == NULL) {
  9. // 处理错误
  10. }

UnmapViewOfFile

        解除文件视图的映射。

  1. BOOL UnmapViewOfFile(
  2. LPCVOID lpBaseAddress
  3. );
  1. if (!UnmapViewOfFile(lpBaseAddress)) {
  2. // 处理错误
  3. }

CloseHandle

        关闭文件、文件映射对象或文件视图的句柄。

  1. BOOL CloseHandle(
  2. HANDLE hObject
  3. );
  1. if (!CloseHandle(hFile)) {
  2. // 处理错误
  3. }
  4. if (!CloseHandle(hFileMapping)) {
  5. // 处理错误
  6. }

4. 安全性

        为了确保文件映射的安全性,可以使用 Windows 的安全属性(SECURITY_ATTRIBUTES)来设置访问控制权限。这样可以限制哪些进程可以访问或修改映射的文件内容。

        总之,文件映射是一种高效、灵活的进程间通信机制,特别适合需要处理大块数据并且要求高实时性和高安全性的场景。通过合理的权限设置和内存管理,可以充分利用文件映射的优势,提高系统的整体性能。

二、文件映射底层原理

        在 Windows 底层,文件映射的实现涉及多个关键组件和机制,包括内存管理器、文件系统、内核对象和虚拟内存管理。以下是 Windows 底层实现文件映射的主要步骤和机制:

1. 文件系统与文件句柄

        当应用程序调用 CreateFile 函数时,Windows 内核通过文件系统驱动程序将文件打开并创建一个文件句柄。文件系统驱动程序负责处理文件的底层访问,包括读写操作、权限检查和文件缓存。

2. 创建文件映射对象

        调用 CreateFileMapping 时,Windows 内核创建一个文件映射对象。这涉及到以下几个步骤:

  • 分配内核对象:Windows 内核分配一个内核对象来表示文件映射对象。这个对象包含文件的元数据、权限信息和映射视图的信息。
  • 建立文件与内存的关联:内核为文件映射对象分配一个区域,在文件和虚拟内存之间建立关联。

3. 内存管理器与虚拟内存

        当应用程序调用 MapViewOfFile 时,Windows 内核的内存管理器开始工作:

  • 虚拟地址空间:内存管理器为文件映射对象分配虚拟地址空间。这个虚拟地址空间是应用程序可以访问的内存区域。
  • 页表:内存管理器更新进程的页表,将虚拟地址映射到物理内存。如果物理内存不足,内存管理器会将部分页面换出到分页文件。
  • 懒加载:文件内容不会立即加载到内存。当进程访问映射视图时,内存管理器通过页错误机制将所需的文件内容加载到内存。

4. 共享内存

        多个进程可以通过文件映射对象共享内存。不同进程调用 OpenFileMappingMapViewOfFile 来访问同一个文件映射对象:

  • 内核对象共享:文件映射对象在内核中是全局的,多个进程可以通过内核对象句柄访问同一文件映射对象。
  • 虚拟地址空间独立:虽然虚拟地址空间在每个进程中是独立的,但它们都可以映射到相同的物理内存区域,这实现了内存共享。

5. 文件映射的拆解

        当进程调用 UnmapViewOfFile 时,内存管理器解除文件视图的映射:

  • 页表清理:内存管理器从进程的页表中移除映射条目,释放对应的虚拟地址空间。
  • 引用计数:文件映射对象有一个引用计数,当所有进程都解除映射并关闭文件映射对象时,内核释放文件映射对象和相关的资源。

关键数据结构和函数

  • 文件映射对象:由 FILE_OBJECTSECTION_OBJECT 数据结构表示。
  • 页表(Page Table):内存管理器使用页表来管理虚拟地址到物理地址的映射。
  • 内存管理器函数MmCreateSectionMmMapViewOfSectionMmUnmapViewOfSection 等函数用于管理文件映射的创建、映射和解除映射。

底层调用流程图

  1. graph TD;
  2. A[应用程序] -->|调用 CreateFile| B[文件系统驱动]
  3. B -->|返回文件句柄| A
  4. A -->|调用 CreateFileMapping| C[内核]
  5. C -->|创建文件映射对象| D[内存管理器]
  6. D -->|建立文件与内存关联| C
  7. A -->|调用 MapViewOfFile| D
  8. D -->|分配虚拟地址空间| A
  9. A -->|访问映射视图| D
  10. D -->|通过页错误加载内容| E[物理内存]
  11. A -->|调用 UnmapViewOfFile| D
  12. D -->|解除映射视图| A
  13. C -->|引用计数清零时释放资源| D

三、父子进程间通信的示例

fileMap基础共享通信代码:

  1. #include <windows.h>
  2. #include <iostream>
  3. #include <string>
  4. #include <stdexcept>
  5. #include <memory>
  6. class SharedMemory {
  7. public:
  8. SharedMemory(const std::wstring& name, size_t size);
  9. ~SharedMemory();
  10. bool create();
  11. bool open();
  12. void write(const std::wstring& data);
  13. std::wstring read();
  14. void close();
  15. void signal(); // 用于通知另一个进程数据已写入
  16. void wait(); // 用于等待另一个进程写入数据
  17. private:
  18. std::wstring name_;
  19. size_t size_;
  20. HANDLE hFile_;
  21. HANDLE hMapFile_;
  22. LPVOID lpBase_;
  23. HANDLE hEvent_;
  24. void cleanup();
  25. void checkAndThrow(bool condition, const std::wstring& errorMessage);
  26. };
  27. #include "sharedMemoryW.h"
  28. // 构造函数
  29. SharedMemory::SharedMemory(const std::wstring& name, size_t size)
  30. : name_(name), size_(size), hFile_(NULL), hMapFile_(NULL), lpBase_(NULL), hEvent_(NULL) {}
  31. // 析构函数
  32. SharedMemory::~SharedMemory() {
  33. close();
  34. }
  35. // 创建文件映射对象和事件对象
  36. bool SharedMemory::create() {
  37. hFile_ = CreateFileW(name_.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  38. checkAndThrow(hFile_ != INVALID_HANDLE_VALUE, L"Unable to create file.");
  39. hMapFile_ = CreateFileMappingW(hFile_, NULL, PAGE_READWRITE, 0, static_cast<DWORD>(size_), name_.c_str());
  40. checkAndThrow(hMapFile_ != NULL, L"Unable to create file mapping object.");
  41. lpBase_ = MapViewOfFile(hMapFile_, FILE_MAP_ALL_ACCESS, 0, 0, size_);
  42. checkAndThrow(lpBase_ != NULL, L"Unable to map view of file.");
  43. hEvent_ = CreateEventW(NULL, TRUE, FALSE, (name_ + L"_Event").c_str());
  44. checkAndThrow(hEvent_ != NULL, L"Unable to create event object.");
  45. return true;
  46. }
  47. // 打开现有的文件映射对象和事件对象
  48. bool SharedMemory::open() {
  49. hMapFile_ = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, name_.c_str());
  50. checkAndThrow(hMapFile_ != NULL, L"Unable to open file mapping object.");
  51. lpBase_ = MapViewOfFile(hMapFile_, FILE_MAP_ALL_ACCESS, 0, 0, size_);
  52. checkAndThrow(lpBase_ != NULL, L"Unable to map view of file.");
  53. hEvent_ = OpenEventW(EVENT_ALL_ACCESS, FALSE, (name_ + L"_Event").c_str());
  54. checkAndThrow(hEvent_ != NULL, L"Unable to open event object.");
  55. return true;
  56. }
  57. // 写入数据到映射内存
  58. void SharedMemory::write(const std::wstring& data) {
  59. if (lpBase_ != NULL) {
  60. memcpy(lpBase_, data.c_str(), (data.size() + 1) * sizeof(wchar_t)); // 包括终止符
  61. signal(); // 通知另一个进程数据已写入
  62. }
  63. }
  64. // 读取数据从映射内存
  65. std::wstring SharedMemory::read() {
  66. if (lpBase_ != NULL) {
  67. return std::wstring(static_cast<wchar_t*>(lpBase_));
  68. }
  69. return L"";
  70. }
  71. // 关闭文件映射和事件对象
  72. void SharedMemory::close() {
  73. cleanup();
  74. }
  75. // 用于通知另一个进程数据已写入
  76. void SharedMemory::signal() {
  77. if (hEvent_ != NULL) {
  78. SetEvent(hEvent_);
  79. }
  80. }
  81. // 用于等待另一个进程写入数据
  82. void SharedMemory::wait() {
  83. if (hEvent_ != NULL) {
  84. WaitForSingleObject(hEvent_, INFINITE);
  85. }
  86. }
  87. void SharedMemory::cleanup() {
  88. if (lpBase_ != NULL) {
  89. UnmapViewOfFile(lpBase_);
  90. lpBase_ = NULL;
  91. }
  92. if (hMapFile_ != NULL) {
  93. CloseHandle(hMapFile_);
  94. hMapFile_ = NULL;
  95. }
  96. if (hFile_ != NULL) {
  97. CloseHandle(hFile_);
  98. hFile_ = NULL;
  99. }
  100. if (hEvent_ != NULL) {
  101. CloseHandle(hEvent_);
  102. hEvent_ = NULL;
  103. }
  104. }
  105. void SharedMemory::checkAndThrow(bool condition, const std::wstring& errorMessage) {
  106. if (!condition) {
  107. DWORD errorCode = GetLastError();
  108. throw std::runtime_error(std::string(errorMessage.begin(), errorMessage.end()) + " Error code: " + std::to_string(errorCode));
  109. }
  110. }

        父进程:

  1. #include <windows.h>
  2. #include <iostream>
  3. #include <string>
  4. #include "sharedMemoryW.h"
  5. void CreateChildProcess(const std::wstring& sharedMemoryName, size_t sharedMemorySize) {
  6. STARTUPINFOW si;
  7. PROCESS_INFORMATION pi;
  8. ZeroMemory(&si, sizeof(si));
  9. si.cb = sizeof(si);
  10. ZeroMemory(&pi, sizeof(pi));
  11. std::wstring commandLine = L"ChildProcess.exe " + sharedMemoryName + L" " + std::to_wstring(sharedMemorySize);
  12. if (!CreateProcessW(NULL, &commandLine[0], NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
  13. std::wcerr << L"CreateProcess failed (" << GetLastError() << L")." << std::endl;
  14. return;
  15. }
  16. CloseHandle(pi.hProcess);
  17. CloseHandle(pi.hThread);
  18. }
  19. int main() {
  20. const std::wstring sharedMemoryName = L"SharedMemoryExample";
  21. const size_t sharedMemorySize = 1024;
  22. SharedMemory sharedMemory(sharedMemoryName, sharedMemorySize);
  23. try {
  24. sharedMemory.create();
  25. }
  26. catch (const std::exception& e) {
  27. std::cerr << "Error: " << e.what() << std::endl;
  28. return 1;
  29. }
  30. CreateChildProcess(sharedMemoryName, sharedMemorySize);
  31. std::wstring message = L"Hello from Parent Process";
  32. sharedMemory.write(message);
  33. std::wcout << L"Parent process waiting for child process to read the message..." << std::endl;
  34. sharedMemory.wait(); // 等待子进程读取数据
  35. std::wcout << L"Parent process read: " << sharedMemory.read() << std::endl;
  36. sharedMemory.close();
  37. system("pause");
  38. return 0;
  39. }

子进程:

  1. #include <windows.h>
  2. #include <iostream>
  3. #include <string>
  4. #include "sharedMemoryW.h" // 假设SharedMemory类的代码放在这个头文件中
  5. int wmain(int argc, wchar_t* argv[]) {
  6. if (argc != 3) {
  7. std::wcerr << L"Usage: ChildProcess <SharedMemoryName> <SharedMemorySize>" << std::endl;
  8. return 1;
  9. }
  10. std::wstring sharedMemoryName = argv[1];
  11. size_t sharedMemorySize = std::stoull(argv[2]);
  12. SharedMemory sharedMemory(sharedMemoryName, sharedMemorySize);
  13. try {
  14. sharedMemory.open();
  15. }
  16. catch (const std::exception& e) {
  17. std::cerr << "Error: " << e.what() << std::endl;
  18. return 1;
  19. }
  20. std::wcout << L"Child process read: " << sharedMemory.read() << std::endl;
  21. std::wstring response = L"Hello from Child Process";
  22. sharedMemory.write(response);
  23. std::wcout << L"Child process waiting for parent process to read the message..." << std::endl;
  24. sharedMemory.signal(); // 通知父进程数据已写入
  25. sharedMemory.close();
  26. system("pause");
  27. return 0;
  28. }

更多代码请访问我的GitHub:

GitHub - bowenliu1996/SharedMemoryW: Windows file mapping communication, including demo

四、优、缺点总结

优点:

  1. 高效性:共享内存允许两个或多个进程直接访问同一块内存区域,避免了频繁的数据拷贝,提高了数据传输的速度。
  2. 低延迟:共享内存通信是一种低延迟的通信方式,因为数据直接写入和读取内存,没有网络或其他中间媒介的延迟。
  3. 高吞吐量:由于直接内存访问,共享内存适用于需要传输大量数据的场景,可以实现高吞吐量的数据传输。
  4. 灵活性:共享内存可以用于各种类型的数据传输,无论是简单的字符串还是复杂的结构体。

缺点:

  1. 复杂的同步机制:共享内存本身不提供同步机制,需要使用额外的同步手段(如互斥锁、事件等)来确保数据读写的正确性和一致性。这增加了开发的复杂性,容易引入竞态条件(Race Conditions)。

    深刻分析:同步问题是共享内存使用中的主要挑战之一。没有正确处理同步,可能导致数据竞争、死锁、饿死等问题。这些问题不仅难以调试,而且可能导致数据损坏或程序崩溃。

  2. 内存管理难度:需要显式地管理共享内存的分配和释放,容易导致内存泄漏或内存碎片化。

    深刻分析:内存管理错误是程序员常犯的错误之一。在共享内存的场景中,内存泄漏不仅影响一个进程,还会影响到所有使用该共享内存的进程,可能导致系统资源耗尽。

  3. 安全性问题:共享内存区域可以被所有拥有权限的进程访问,存在潜在的安全风险。如果没有严格的访问控制,可能导致敏感数据泄露或被恶意篡改。

    深刻分析:安全性问题尤其在多用户环境中显得突出。恶意进程可以利用共享内存中的数据进行攻击或篡改,这需要额外的措施来保护数据的完整性和机密性。

  4. 平台依赖性:共享内存的实现通常依赖于操作系统的底层机制,不同操作系统(如Windows和Unix)的实现细节不同,导致代码的可移植性差。

    深刻分析:跨平台开发时,共享内存的实现需要针对不同平台进行适配。这增加了开发和维护成本,同时也可能导致在不同平台上出现难以预见的问题。

  5. 调试困难:共享内存调试相对困难,因为多个进程同时操作同一内存区域,调试工具和技术支持较少。

    深刻分析:调试共享内存中的问题往往需要了解多个进程的状态和行为,这增加了调试的难度和复杂性。开发人员需要具备更高的调试技巧和经验。

总结

        共享内存通信在高效数据传输方面有明显优势,但其复杂的同步机制、内存管理、安全性问题、平台依赖性和调试困难都是使用过程中必须慎重考虑的缺点。这些缺点不仅增加了开发和维护的难度,还可能带来潜在的系统稳定性和安全性风险。因此,在使用共享内存时,需要综合权衡其优缺点,选择适合具体应用场景的通信机制。

五、改进

        根据复杂的同步机制、内存管理难度、平台依赖性、安全性问题、调试困难等问题。

我们优化代码:
sharedMemoryW.h

  1. #include <windows.h>
  2. #include <iostream>
  3. #include <string>
  4. #include <stdexcept>
  5. #include <memory>
  6. class SharedMemory {
  7. public:
  8. SharedMemory(const std::wstring& name, size_t size);
  9. ~SharedMemory();
  10. bool create();
  11. bool open();
  12. void write(const std::wstring& data);
  13. std::wstring read();
  14. void close();
  15. void signal(); // 用于通知另一个进程数据已写入
  16. void wait(); // 用于等待另一个进程写入数据
  17. private:
  18. std::wstring name_;
  19. size_t size_;
  20. HANDLE hFile_;
  21. HANDLE hMapFile_;
  22. LPVOID lpBase_;
  23. HANDLE hEvent_;
  24. HANDLE hMutex_; // 新增的互斥锁
  25. void cleanup();
  26. void checkAndThrow(bool condition, const std::wstring& errorMessage);
  27. };

sharedMemoryW.cpp:

  1. #include "sharedMemoryW.h"
  2. // 构造函数
  3. SharedMemory::SharedMemory(const std::wstring& name, size_t size)
  4. : name_(name), size_(size), hFile_(NULL), hMapFile_(NULL), lpBase_(NULL), hEvent_(NULL), hMutex_(NULL) {}
  5. // 析构函数
  6. SharedMemory::~SharedMemory() {
  7. close();
  8. }
  9. // 创建文件映射对象和事件对象
  10. bool SharedMemory::create() {
  11. SECURITY_ATTRIBUTES sa;
  12. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  13. sa.bInheritHandle = FALSE;
  14. sa.lpSecurityDescriptor = NULL; // 可以自定义安全描述符
  15. hFile_ = CreateFileW(name_.c_str(), GENERIC_READ | GENERIC_WRITE, 0, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  16. checkAndThrow(hFile_ != INVALID_HANDLE_VALUE, L"Unable to create file.");
  17. hMapFile_ = CreateFileMappingW(hFile_, NULL, PAGE_READWRITE, 0, static_cast<DWORD>(size_), name_.c_str());
  18. checkAndThrow(hMapFile_ != NULL, L"Unable to create file mapping object.");
  19. lpBase_ = MapViewOfFile(hMapFile_, FILE_MAP_ALL_ACCESS, 0, 0, size_);
  20. checkAndThrow(lpBase_ != NULL, L"Unable to map view of file.");
  21. hEvent_ = CreateEventW(&sa, TRUE, FALSE, (name_ + L"_Event").c_str());
  22. checkAndThrow(hEvent_ != NULL, L"Unable to create event object.");
  23. hMutex_ = CreateMutexW(&sa, FALSE, (name_ + L"_Mutex").c_str());
  24. checkAndThrow(hMutex_ != NULL, L"Unable to create mutex object.");
  25. return true;
  26. }
  27. // 打开现有的文件映射对象和事件对象
  28. bool SharedMemory::open() {
  29. hMapFile_ = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, name_.c_str());
  30. checkAndThrow(hMapFile_ != NULL, L"Unable to open file mapping object.");
  31. lpBase_ = MapViewOfFile(hMapFile_, FILE_MAP_ALL_ACCESS, 0, 0, size_);
  32. checkAndThrow(lpBase_ != NULL, L"Unable to map view of file.");
  33. hEvent_ = OpenEventW(EVENT_ALL_ACCESS, FALSE, (name_ + L"_Event").c_str());
  34. checkAndThrow(hEvent_ != NULL, L"Unable to open event object.");
  35. hMutex_ = OpenMutexW(MUTEX_ALL_ACCESS, FALSE, (name_ + L"_Mutex").c_str());
  36. checkAndThrow(hMutex_ != NULL, L"Unable to open mutex object.");
  37. return true;
  38. }
  39. // 写入数据到映射内存
  40. void SharedMemory::write(const std::wstring& data) {
  41. WaitForSingleObject(hMutex_, INFINITE); // 加锁
  42. if (lpBase_ != NULL) {
  43. memcpy(lpBase_, data.c_str(), (data.size() + 1) * sizeof(wchar_t)); // 包括终止符
  44. signal(); // 通知另一个进程数据已写入
  45. std::wcout << L"Data written: " << data << std::endl; // 日志记录
  46. }
  47. ReleaseMutex(hMutex_); // 解锁
  48. }
  49. // 读取数据从映射内存
  50. std::wstring SharedMemory::read() {
  51. WaitForSingleObject(hMutex_, INFINITE); // 加锁
  52. std::wstring result;
  53. if (lpBase_ != NULL) {
  54. result = std::wstring(static_cast<wchar_t*>(lpBase_));
  55. std::wcout << L"Data read: " << result << std::endl; // 日志记录
  56. }
  57. ReleaseMutex(hMutex_); // 解锁
  58. return result;
  59. }
  60. // 关闭文件映射和事件对象
  61. void SharedMemory::close() {
  62. cleanup();
  63. }
  64. // 用于通知另一个进程数据已写入
  65. void SharedMemory::signal() {
  66. if (hEvent_ != NULL) {
  67. SetEvent(hEvent_);
  68. ResetEvent(hEvent_); // 重置事件
  69. }
  70. }
  71. // 用于等待另一个进程写入数据
  72. void SharedMemory::wait() {
  73. if (hEvent_ != NULL) {
  74. WaitForSingleObject(hEvent_, INFINITE);
  75. }
  76. }
  77. void SharedMemory::cleanup() {
  78. if (lpBase_ != NULL) {
  79. UnmapViewOfFile(lpBase_);
  80. lpBase_ = NULL;
  81. }
  82. if (hMapFile_ != NULL) {
  83. CloseHandle(hMapFile_);
  84. hMapFile_ = NULL;
  85. }
  86. if (hFile_ != NULL) {
  87. CloseHandle(hFile_);
  88. hFile_ = NULL;
  89. }
  90. if (hEvent_ != NULL) {
  91. CloseHandle(hEvent_);
  92. hEvent_ = NULL;
  93. }
  94. if (hMutex_ != NULL) {
  95. CloseHandle(hMutex_);
  96. hMutex_ = NULL;
  97. }
  98. }
  99. void SharedMemory::checkAndThrow(bool condition, const std::wstring& errorMessage) {
  100. if (!condition) {
  101. DWORD errorCode = GetLastError();
  102. throw std::runtime_error(std::string(errorMessage.begin(), errorMessage.end()) + " Error code: " + std::to_string(errorCode));
  103. }
  104. }

        这样改进优化,代码似乎安全了些。。。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/888878
推荐阅读
相关标签
  

闽ICP备14008679号