Windows进程通信(IPC)之共享内存

前言:我们知道windows os上有很多的api,编程语言对os进行操作,无论什么语言,最终都是通过windows api 的。

在windows编程中,有个概念是句柄,句柄指向资源(一切可以利用的物理的逻辑的资源),其中文件操作,可以将文件映射到内存,此处的文件是广义的文件,可以指内存对象,邮件槽等。

在windows中创建一个指向文件的虚拟内存,然后多个进程创建各个进程对这块内存的映射,通过访问各个进程的映射内存对这块虚拟内存进行访问,是共享内存实现的原理。

下图很好的标明了整个原理。

 

 一、相关的API函数

内存映射API函数CreateFileMapping创建一个有名的共享内存:

HANDLE CreateFileMapping(
HANDLE hFile,                         // 映射文件的句柄,设为0xFFFFFFFF以创建一个进程间共享的对象
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,   // 安全属性
DWORD flProtect,                                                                   // 保护方式
DWORD dwMaximumSizeHigh,                                           //对象的大小 
DWORD dwMaximumSizeLow, 
LPCTSTR lpName                                                                 // 必须为映射文件命名
);

HANDLE CreateFileMapping(
HANDLE hFile, //物理文件句柄
LPSECURITY_ATTRIBUTES lpAttributes, //安全设置
DWORD flProtect, //保护设置
DWORD dwMaximumSizeHigh, //高位文件大小
DWORD dwMaximumSizeLow, //低位文件大小
LPCTSTR lpName //共享内存名称
);
hFile 指定欲在其中创建映射的一个文件句柄。INVALID_HANDLE_VALUE,即0xFFFFFFFF表示在页面文件中创建一个可共享映射文件。
lpAttributes 它指明返回的句柄是否可以被子进程所继承,指定一个安全对象,在创建文件映射时使用。如果为NULL(用ByVal As Long传递零),表示使用默认安全对象
flProtect 有以下几种方式:
PAGE_READONLY 以只读方式打开映射
PAGE_READWRITE 以可读、可写方式打开映射
PAGE_WRITECOPY 为写操作留下备份也可组合使用下述一个或多个常数:
SEC_COMMIT 为文件映射一个小节中的所有页分配内存
SEC_IMAGE 文件是个可执行文件
SEC_RESERVE 为没有分配实际内存的一个小节保留虚拟内存空间
dwMaximumSizeHigh 文件映射的最大长度的高32位
dwMaximumSizeLow 文件映射的最大长度的低32位。如这个参数和dwMaximumSizeHigh都是零,就用磁盘文件的实际长度。
lpName 指定文件映射对象的名字。如存在这个名字的一个映射,函数就会打开它。用vbNullString可以创建一个无名的文件映射。

 

LPVOID WINAPI MapViewOfFile( //将一个文件映射对象映射到当前应用程序的地址空间。
  __in HANDLE hFileMappingObject,
  __in DWORD dwDesiredAccess,
  __in DWORD dwFileOffsetHigh,
  __in DWORD dwFileOffsetLow,
  __in SIZE_T dwNumberOfBytesToMap
  );
hFileMappingObject 为CreateFileMapping返回的文件映像对象句柄。
dwDesiredAccess 映射对象的文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。
可取以下值:
FILE_MAP_ALL_ACCESS 等价于CreateFileMapping的 FILE_MAP_WRITE|FILE_MAP_READ.文件映射对象被创建时必须指定PAGE_READWRITE 选项.
FILE_MAP_COPY 可以读取和写入文件.写入操作会导致系统为该页面创建一份副本.在调用 CreateFileMapping时 必须 传入PAGE_WRITECOPY保护属性.
FILE_MAP_EXECUTE 可以将文件中的数据在调用CreateFileMapping时可以传入PAGE_EXECUTE_READWRITE或PAGE_EXECUTE_READ保护属性.
FILE_MAP_READ 可以读取文件.在调用CreateFileMapping时可以传入PAGE_READONLY或PAGE_READWRITE保护属性.
FILE_MAP_WRITE 可以读取和写入文件.在调用CreateFileMapping时必须传入PAGE_READWRITE保护属性.
dwFileOffsetHigh 表示文件映射起始偏移的高32位.
dwFileOffsetLow 表示文件映射起始偏移的低32位.(64KB对齐不是必须的)
dwNumberOfBytes 指定映射文件的字节数.

三、写端的实现

#include <iostream>
#include <windows.h>
#include <string.h>
#include <cstring>
#include <istream>
using namespace std;

int main()
{
    unsigned long buff_size = 1024 * 5;

    PVOID pBuff=NULL;

    HANDLE hFile = CreateFile(L"shared_memory",    //如果不映射到物理磁盘上,可以不用创建
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hFile==INVALID_HANDLE_VALUE)
    {
        cout << "create file error." << endl;
    }

    HANDLE hFile_mapping = CreateFileMapping(
        INVALID_HANDLE_VALUE,                  // 如果需要创建,而不是内存对象,可以传入hFile
        NULL,
        PAGE_READWRITE,
        0,
        buff_size,
        L"shared_memory");

    if (NULL != hFile_mapping)
    pBuff = MapViewOfFile(
        hFile_mapping,
        FILE_MAP_ALL_ACCESS,
        0,
        0,
        buff_size
    );

    char str[1024];

    while (true)
    {
        cout << "please enter data .\"exit\" end app" << endl;
        cin.getline(str,1024);
        if (str!="exit")
        {
            if (NULL != pBuff)
                memcpy(pBuff, str, 1024);
        }
        else if (str == "exit")
        {
            break;
        }

    }

    if (NULL != pBuff)
        FlushViewOfFile(pBuff, buff_size);

    cout << "finish write, wait ..." << endl;
    Sleep(20000);

    if (NULL!=pBuff)
        UnmapViewOfFile(pBuff);

    if (NULL != hFile_mapping)
        CloseHandle(hFile_mapping);

    if (NULL != hFile)
        CloseHandle(hFile);

}

四、读端的实现

#include <iostream>
#include <windows.h>
using namespace std;

int main()
{
    const unsigned long buff_size = 1024*5;
    LPVOID pBuff = NULL;

    HANDLE handle_file_mapping = OpenFileMapping(
        FILE_MAP_ALL_ACCESS,
        NULL,
        L"shared_memory");

    if (NULL!= handle_file_mapping)
        pBuff = MapViewOfFile(
            handle_file_mapping,
        FILE_MAP_ALL_ACCESS,
        0,
        0,
        0);

    if (handle_file_mapping==NULL|| pBuff==NULL)
    {
        cout << "create shared memory faild,please waitting the memory created and exect the app again!" << endl
             <<"enter any key to  exit the app!";
        string temp;
        cin >> temp;
        return 0;
    }

    string str;

    while (true)
    {
        cout << "enter \"yes\" receive data, or enter \"exit\" " << endl;
        cin >> str;
        if (str=="yes")
        {
            char* share_buffer = (char*)pBuff;
            cout << share_buffer << endl;
        }
        else if (str == "exit")
        {
            break;
        }

    }

    if (NULL!= pBuff)
        UnmapViewOfFile(pBuff);
    if (NULL != handle_file_mapping)
        CloseHandle(handle_file_mapping);
    return 0;
}

五、结果如下

 

posted @ 2020-09-12 14:50  卖雨伞的小男孩  阅读(1651)  评论(0编辑  收藏  举报