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;
}

五、結果如下

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM