• 共享内存是最快的IPC方式
  • 读写一轮需要两次拷贝:用户空间->共享内存,共享内存->用户空间
  • 共享内存一般通过memcpy()进行拷贝,因此共享内存数据并不会自动清空
  • CreateFileMapping()第一个参数INVALID_HANDLE_VALUE时,实际是在内核内存创建一个磁盘无关的内存,不属于任何进程空间里的,不使用时需要释放(任何进程都可拿着句柄去释放?有待验证);当第一个参数不为INVALID_HANDLE_VALUE时,而是磁盘实际存在的文件时,系统同样会在内核申请一块内存,然后返回该文件的内存映射文件对象实例(大小,安全,访问权限,名字等),MapViewOfFile()也是将实际内存地址和进程空间地址作映射
  • MapViewOfFile()是将共享内存的地址映射到进程的地址空间里,进程里操作返回的地址/指针/句柄(通过页表寻址到共享内存)实际就等于直接操作了内存

缺点:
同步/异步问题需要用户自己控制,复杂度随操作对象增加而增加

进程间通信

**说明:**服务端写,客户端读
一共创建了两个event,一个服务写就绪hEventSrv,一个客户端读就绪hEventClient
服务端代码

// ConsoleAppShareMemSrv.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "ConsoleAppShareMemSrv.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 唯一的应用程序对象

CWinApp theApp;

using namespace std;

DWORD dwSize = 1024 * 10;
HANDLE hEventSrv = NULL;
HANDLE hEventClient = NULL;

int main()
{
    int nRetCode = 0;

    HMODULE hModule = ::GetModuleHandle(nullptr);

    if (hModule != nullptr)
    {
        // 初始化 MFC 并在失败时显示错误
        if (!AfxWinInit(hModule, nullptr, ::GetCommandLine(), 0))
        {
            // TODO: 更改错误代码以符合您的需要
            wprintf(L"错误: MFC 初始化失败\n");
            nRetCode = 1;
        }
        else
        {
            // TODO: 在此处为应用程序的行为编写代码。
			HANDLE hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, NULL, dwSize, TEXT("shareMemory"));
        
			if (!hFileMapping)
			{
				int err = GetLastError();
				printf("CreateFileMapping failed,error code: %d", err);
				return -1;
			}

			LPVOID lpMapView = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, NULL, NULL, NULL);

			if (!lpMapView)
			{
				int err = GetLastError();
				printf("MapViewOfFile failed,error code: %d", err);
				return -1;
			}

			hEventSrv = CreateEvent(NULL, TRUE, TRUE, TEXT("shareEventSrv"));

			if (!hEventSrv)
			{
				int err = GetLastError();
				printf("CreateEvent failed,error code: %d", err);
				return -1;
			}

			hEventClient = CreateEvent(NULL, TRUE, FALSE, TEXT("shareEventClient"));

			if (!hEventClient)
			{
				int err = GetLastError();
				printf("CreateEvent failed,error code: %d", err);
				return -1;
			}

			while (WAIT_OBJECT_0  == WaitForSingleObject(hEventSrv, INFINITE))	//等待服务端可写,阻塞
			{
				char p[1024] = {0};
				cin.getline(p, sizeof(p));

				int len = strlen(p);
				int len2 = sizeof(p);
				memcpy(lpMapView, p, len2);	//选用len只拷贝实际字节数,即没有被重新覆盖的内容,仍存在内存里;len2是将1024都cp到内存,全部覆盖
				ResetEvent(hEventSrv);	//将服务写重置为false
				SetEvent(hEventClient);	//将客户端读置为true
				Sleep(1500);
			}
		}
    }
    else
    {
        // TODO: 更改错误代码以符合您的需要
        wprintf(L"错误: GetModuleHandle 失败\n");
        nRetCode = 1;
    }

	system("pause");
    return nRetCode;
}

客户端代码

// ConsoleAppShareMemClient.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "ConsoleAppShareMemClient.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 唯一的应用程序对象

CWinApp theApp;

using namespace std;

DWORD dwSize = 1024 * 10;
HANDLE hEventSrv = NULL;
HANDLE hEventClient = NULL;

int main()
{
    int nRetCode = 0;

    HMODULE hModule = ::GetModuleHandle(nullptr);

    if (hModule != nullptr)
    {
        // 初始化 MFC 并在失败时显示错误
        if (!AfxWinInit(hModule, nullptr, ::GetCommandLine(), 0))
        {
            // TODO: 更改错误代码以符合您的需要
            wprintf(L"错误: MFC 初始化失败\n");
            nRetCode = 1;
        }
        else
        {
            // TODO: 在此处为应用程序的行为编写代码。
			HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE , TEXT("shareMemory"));

			if (!hFileMapping)
			{
				int err = GetLastError();
				printf("OpenFileMapping failed,error code: %d", err);
				return -1;
			}

			LPVOID lpMapView = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, NULL, NULL, NULL);

			if (!lpMapView)
			{
				int err = GetLastError();
				printf("MapViewOfFile failed,error code: %d", err);
				return -1;
			}

			hEventSrv = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("shareEventSrv"));

			if (!hEventSrv)
			{
				int err = GetLastError();
				printf("OpenEvent failed,error code: %d", err);
				return -1;
			}

			hEventClient = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("shareEventClient"));

			if (!hEventClient)
			{
				int err = GetLastError();
				printf("OpenEvent failed,error code: %d", err);
				return -1;
			}

			while (WAIT_OBJECT_0 == WaitForSingleObject(hEventClient, INFINITE))	//等待客户端可读,阻塞
			{
				char p[1024];
				memcpy(p, lpMapView, sizeof(p));	//将内存1024字节全部拷贝出来,只是拷贝,内存仍有数据
				cout << "read from share memory: " << p << endl;
				ResetEvent(hEventClient);	//将客户端读重置为false
				SetEvent(hEventSrv);	//将服务写置为true
				Sleep(1500);
			}
        }
    }
    else
    {
        // TODO: 更改错误代码以符合您的需要
        wprintf(L"错误: GetModuleHandle 失败\n");
        nRetCode = 1;
    }

    return nRetCode;
}

同/异步

总结一下小编所熟悉的多进程同/异步控制方法:

  1. 互斥量
  2. 信号量
  3. 事件
  4. atomic原子操作(pv操作)
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐