Windows進程間通信--共享內存映射文件(FileMapping)--VS2012下發送和接收


之前以為兩個互不相關的程序a.exe b.exe通信就只能通過網絡,人家說可以通過發消息,我還深以為不然,對此,我表示萬分慚愧。

之前課本上說的進程間通信,有共享內存、管道等之類的,但沒有自己操刀寫過程序的原理真心理解不了。

進程間通信的方法有很多,使用的條件也不太一樣,有些必須同時在本機使用,有些可以遠程,希望接下來的時間可以一個一個嘗試,並弄懂。

言歸正傳,下面用共享映射文件的方式實現進程間通信,代碼可以運行。但只是自己的學習過程,無過多實用價值,不足之處,請指正。

//-----------------------------------------------------------------------------------------------------------------------------------

 1.淺理解

每個進程有自己獨立的空間,一個進程無法訪問其他進程的數據。就好像兩個是互不干涉的個體,想讓它們進行通信(交換數據),就必須有一段它們都可以訪問到的空間,作為中間介質。

在計算機中,可以存放數據的地方分為內存和硬盤,進程是運行着的程序,肯定在內存當中。

                                    

為讓進程A和進程B進行通信,它們都可以訪問的空間可以是內存中它們以外的區域,或者是硬盤中的區域。

通過內存文件映射,則是將硬盤中的一個文件,映射到內存中,進程A,B都可以訪問該內存(文件),達到交換數據的目的。

如右圖是給用戶的直接感覺,兩個進程操作同一個物理文件,通過文件的讀寫,交換數據。

 

2.發送方(服務器)

 個人理解,雖然共享內存都可以讀寫,也沒有服務器和客戶端的概念,但是,有一方需要創建這個文件,而另一方只需要打開這個文件。

所以,我將創建文件的一方,認為是服務器,而打開文件,進行讀取的一方稱為客戶端。而事實上,服務器或者客戶端都可以對文件進行讀寫,類似於網絡編程中,都可以讀寫。

先貼代碼,再解釋

#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
#pragma warning(disable:4996)

int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE hFile = CreateFile(TEXT("c:\zj.dat"),GENERIC_READ|GENERIC_WRITE,
		0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
	if(hFile==NULL)
	{
		printf("create file error!");
		return 0;
	}

//	HANDLE hFile = (HANDLE)0xffffffff; //創建一個進程間共享的對象
	HANDLE hMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,	0,1024*1024,TEXT("ZJ"));
	int rst = GetLastError();
	if (hMap != NULL && rst == ERROR_ALREADY_EXISTS) 
	{ 
		printf("hMap error\n");
		CloseHandle(hMap); 
		hMap = NULL; 
		return 0;
	} 

	CHAR* pszText=NULL;
	pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024);
	if(pszText==NULL)
	{
		printf("view map error!");
		return 0;
	}
	sprintf(pszText,"hello my first mapping file!\n"); //其實是向文件中(共享內存中)寫入了
	while(1)
	{
		printf(pszText);
		Sleep(3000);
	}

	getchar();

	UnmapViewOfFile((LPCVOID)pszText);
	CloseHandle(hMap);
CloseHandle(hFile); return 0; }

  1)創建一個文件  CreateFile

          HANDLE hFile = CreateFile(...);  參數可參見MSDN,就是創建一般的文件,此處不詳說。個人認為這個文件的目的,就是共享內存的實體(物理存在的)。

         CreateFile(TEXT("c:\zj.dat"),GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

         在C盤的確有文件zj.dat  該文件名可以隨意取。

     2)創建內存映射文件 CreateFileMapping

         將上述真正存在的文件(物理文件) hFile映射成為一個虛擬的映射文件 hMap ,即將物理文件與虛擬文件綁定

   HANDLE hMap = CreateFileMapping( hFile, NULL, PAGE_READWRITE, 0,1024*1024,TEXT("ZJ"));

        參數解釋: hFile: 是1)中對應的物理文件。如果hFile=NULL,即沒有通過CreateFile創建一個實際存在的文件,有解釋為創建一個進程間共享的對象。

                              個人認為也可能是在內存中開辟了一段空間,或者在硬盤上有一個默認文件。

                     NULL: 安全屬性

                     PAGE_READWRITE: 可讀可寫

                     0,  1024*1024: 從物理文件的高0位到低1024*1024位映射成虛擬文件。(個人是這樣理解的)

                    ZJ :是虛擬文件的名字,客戶端讀時也用這個名字,所以,可能這個名字是會在進程外部注冊的。

      3)加載內存映射文件  MapViewOfFile  :映射成內存地址

           將虛擬文件映射成內存地址,方便使用。即將文件與內存綁定,以后操作該內存其實就是操作該文件。

    CHAR* pszText=NULL; //一個指針,不需要分配空間

     pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024); //通過映射后,該指針就指向該文件。

          參數解釋:hMap:虛擬文件名

                       FILE_MAP_ALL_ACCESS:訪問模式

                      0,0:從虛擬文件的哪個位置開始映射成內存

                     1024*1024: 映射多大的內存

     4)使用內存,即使用文件

        可以向這個內存(文件)讀寫數據了。

        //寫

   sprintf(pszText,"hello my first mapping file!\n"); //語句本身的意思,是將句子寫入字符串pszText中,而這個字符串並沒有在程序中分配空間,即沒有new

        但這句話也不會報錯。是因為該字符串地址指向了映射文件,即通過操作該指針,實際是操作了文件,句子寫入了文件當中。

       //讀

       printf(pszText); //語句本身是將字符串中的內容輸出到屏幕上,該字符串中沒有分配空間,也沒有賦值。但通過映射為文件,可以將文件中的內容通過該指針輸出到屏幕上。

      注:個人理解,可以認為該指針pszText直接指向了硬盤空間。就是文件的操作符。

   5)卸載映射       UnmapViewOfFile((LPCVOID)pszText);

   6)關閉文件

CloseHandle(hMap); //關閉虛擬文件
CloseHandle(hFile); //關閉物理文件

 

3.接收方(客戶端)

 

HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,TRUE,TEXT("ZJ"));
    if(hMap == NULL)
    {
        printf("open file map error!");
        return 0;
    }

    CHAR* pszText = NULL;
    pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024);
    if(pszText==NULL)
    {
        printf("map view error!\n");
        return 0;
    }
    printf(pszText); //從文件中讀(共享內存)
     
    sprintf(pszText,"second data!\n"); //寫入

    getchar();

    UnmapViewOfFile(pszText);
    CloseHandle(hMap);

    hMap = NULL; 
    return 0;

 1)打開內存映射文件(虛擬文件)

    HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,TRUE,TEXT("ZJ"));

     是通過內存映射文件名 ZJ  找到該文件的。

 2)映射成內存 MapViewOfFile

      和服務器中的一樣。

3)使用內存

    讀寫數據,此處寫入數據后,從服務器中讀出來的內容就改變了。可以證明,服務器客戶端同時使用着同一個文件,文件是它們之間的一個通道,用來交換數據

4)關閉映射,關閉文件。同服務器

 

注:1)服務器和客戶端必須同時為進程,即都在運行的時候,才可以交換數據。

         雖然是通過物理文件,交互數據的,但是ZJ是虛擬文件的名字,該名字必須在兩個進程中都能認識,才可以通過它來交互數據。

         所以,如果,服務器先打開,寫入文件后,關閉。 客戶端,再打開文件,則CreateFileMap會失敗,也無法進行文件映射了。

               這是我犯的一個錯誤,不知道大家會不會理解,就是,像網絡通信一樣,必須雙方都在,才可以通信。

當然客戶端也可以通過CreateFile打開一個存在的文件 zj.dat  ,再用CreateFileMap去映射,同樣可以訪問文件中的數據。

   2)內存映射文件的機制是單純的讓訪問文件變的簡單。

    就好像文件流fstream模擬了輸入輸出流iostream一樣,操作文件,就像操作cout,cin一樣方便。

    同理,通過文件映射,操作文件就可以向操作內存一樣方便。

    只是將這個機制用於進程間通信時,才需要考慮一端發送,一端接收,同步問題等。

  


免責聲明!

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



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