MFC:通过代码简单理解进程间的通讯机制——共享内存
下面用共享映射文件的方式實現進程間通信,代碼可以運行。
一、淺理解
每個進程有自己獨立的空間,一個進程無法訪問其他進程的數據。就好像兩個是互不干涉的個體,想讓它們進行通信(交換數據),就必須有一段它們都可以訪問到的空間,作為中間介質。在計算機中,可以存放數據的地方分為內存和硬盤,進程是運行著的程序,肯定在內存當中。為讓進程A和進程B進行通信,它們都可以訪問的空間可以是
內存中它們以外的區域
硬盤中的區域
內存文件映射是將硬盤中的一個文件映射到內存中,進程A,B都可以訪問該內存(文件),達到交換數據的目的。如右圖是給用戶的直接感覺,兩個進程操作同一個物理文件,通過文件的讀寫,交換數據。
二、發送方(服務器)
個人理解,雖然共享內存都可以讀寫,也沒有服務器和客戶端的概念。但是,需要有一方創建這個文件,而另一方只需要獲取并打開這個文件。為了方便,將創建文件的一方稱作服務器,而獲取并打開文件的一方稱為客戶端。而事實上,服務器或者客戶端都可以對文件進行讀寫,類似于網絡編程中的客戶端和服務器都可以發消息接消息。自己寫的服務器端C++代碼如下
#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
#pragma warning(disable:4996)
int_tmain(intargc, _TCHAR* argv[]){
?? ?HANDLEhFile =CreateFile(TEXT("c:\zj.dat"),GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
?? ?if(hFile==NULL){
?? ??? ?printf("create file error!");
?? ??? ?return0;
?? ?}
?? ?// HANDLE hFile = (HANDLE)0xffffffff; //創建一個進程間共享的對象
?? ?HANDLEhMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE, ?0,1024*1024,TEXT("ZJ"));
?? ?intrst = GetLastError();
?? ?if(hMap != NULL && rst == ERROR_ALREADY_EXISTS){
?? ??? ?printf("hMap error\n");
?? ??? ?CloseHandle(hMap);
?? ??? ?hMap = NULL;
?? ??? ?return0;
?? ?}
?? ?CHAR* pszText=NULL;
?? ?pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024);
?? ?if(pszText==NULL){
?? ??? ?printf("view map error!");
?? ??? ?return0;
?? ?}
?? ?sprintf(pszText,"hello my first mapping file!\n"); //其實是向文件中(共享內存中)寫入了
?? ?while(1){
?? ??? ?printf(pszText);
?? ??? ?Sleep(3000);
?? ?}
?? ?getchar();
?? ?UnmapViewOfFile((LPCVOID)pszText);
?? ?CloseHandle(hMap);
? ? CloseHandle(hFile);
?? ?return0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
當調用 CreateFileMapping 創建命名的內存映射文件對象時 , Windows 即在物理內存申請一塊指定大小的內存區域 , 返回文件映射對象的句柄 hMap ; 為了能夠訪問這塊內存區域必須調用 MapViewOfFile 函數 , 促使 Windows 將此內存空間映射到進程的地址空間中 ; 當在其他進程訪問這塊內存區域時 , 則必須使用 OpenFileMapping 函數取得對象句柄 hMap , 并調用 MapViewOfFile 函數得到此內存空間的一個映射 , 這樣系統就把同一塊內存區域映射到了不同進程的地址空間中 , 從而達到共享內存的目的 。
先創建一個文件 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 該文件名可以隨意取。
創建內存映射文件 CreateFileMapping
????將上述硬盤中的文件hFile映射成為一個虛擬的映射文件 hMap ,即將物理文件與虛擬文件綁定,或者理解成將硬盤中的文件映射到內存當中。
HANDLE hMap = CreateFileMapping( hFile, NULL, PAGE_READWRITE, 0,1024*1024,TEXT("ZJ"));
?????參數解釋: hFile是對應的物理文件的句柄。如果hFile=NULL,即沒有通過CreateFile創建一個實際存在的文件。有解釋為創建一個進程間共享的對象。個人認為也可能是在內存中隨機開辟了一段空間,或者在硬盤上有一個默認文件。
NULL: 安全屬性
PAGE_READWRITE: 可讀可寫
0, 1024*1024: 從物理文件的高0位到低,1024*1024為映射成虛擬文件。(個人是這樣理解的)
ZJ :是虛擬文件的名字/標識,客戶端讀時也用此來作為文件的標識,所以,這個名字應該是在進程外部注冊的。
加載并獲得內存映射文件 MapViewOfFile 在內存中的地址
?????將虛擬文件映射成內存地址,方便使用。即將文件與內存綁定,以后由此地址訪問該塊內存。
CHAR* pszText=NULL; //一個指針,不需要分配空間
pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024); //通過映射后,該指針就指向該文件,如果最后一個參數是0表示映射整個文件。
????參數解釋:hMap作為虛擬文件的標識(通俗點就是虛擬文件名);
FILE_MAP_ALL_ACCESS:設定訪問模式;
0,0:從虛擬文件的哪個位置開始映射成內存
1024*1024: 映射的內存大小
使用內存,即使用文件
????可以向這個內存(文件)讀寫數據了。
//寫:
sprintf(pszText,"hello my first mapping file!\n");
????語句本身的意思,是將句子寫入字符串pszText中,而這個字符串并沒有在程序中分配空間,即沒有new。但這句話也不會報錯。是因為該字符串地址指向了映射文件,即通過操作該指針,實際是操作了文件,句子寫入了文件當中。
//讀:
printf(pszText);
????語句本身是將字符串中的內容輸出到屏幕上,該字符串中沒有分配空間,也沒有賦值。但通過映射為文件,可以將文件中的內容通過該指針輸出到屏幕上。
?????注:個人理解,可以認為該指針pszText直接指向了硬盤空間(即通過內存文件和硬盤文件直接聯系在一起)。就是文件的操作符。
5. 載映射 :
UnmapViewOfFile((LPCVOID)pszText);//卸載映射
6. 關閉文件:
"__mceDel">CloseHandle(hMap); //關閉虛擬文件
"__mceDel">CloseHandle(hMap); //關閉虛擬文件
三、接收方(客戶端)
HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,TRUE,TEXT("ZJ")); ?
if(hMap == NULL){ ? ? ? ?
printf("open file map error!");
return0;
} ? ?
CHAR* pszText = NULL; ? ?
pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024); ? ?if(pszText==NULL){ ? ? ? ?
printf("map view error!\n"); ? ? ? ?
return0; ? ?
} ? ?
printf(pszText); //從文件中讀(共享內存) ? ? ? ??
sprintf(pszText,"second data!\n"); //寫入 ? ?
getchar(); ? ?
UnmapViewOfFile(pszText); ? ?
CloseHandle(hMap); ? ?
hMap = NULL; ? ??
return0;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
打開內存映射文件(虛擬文件)
HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,TRUE,TEXT("ZJ"));
通過內存映射文件名 ZJ 找到該文件的。
獲得地址
CHAR* pszText = NULL;
pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024);
????映射成內存 MapViewOfFile和服務器中的一樣。
使用內存
printf(pszText); //從文件中讀(共享內存)
????讀寫數據,此處寫入數據后,從服務器中讀出來的內容就改變了。
????可以證明,服務器客戶端同時使用著同一個文件,文件是它們之間的一個通道,用來交換數據
關閉映射,關閉文件。同服務器
UnmapViewOfFile(pszText);
CloseHandle(hMap);
四、注意點
服務器和客戶端必須同時為進程,即都在運行的時候,才可以交換數據。
????雖然是通過物理文件,交互數據的,但是ZJ是虛擬文件的名字,該名字必須在兩個進程中都能認識,才可以通過它來交互數據。
?????所以,如果服務器先打開,寫入文件后,關閉。 客戶端,再打開文件,則CreateFileMap會失敗,也無法進行文件映射了。就像網絡通信一樣,必須雙方都在,才可以通信。
當然客戶端也可以通過CreateFile打開一個存在的文件 zj.dat ,再用CreateFileMap去映射,同樣可以訪問文件中的數據。
內存映射文件的機制是單純的讓訪問文件變的簡單。
????就好像文件流fstream模擬了輸入輸出流iostream一樣,操作文件,就像操作cout,cin一樣方便。
????同理,通過文件映射,操作文件就可以向操作內存一樣方便。只是將這個機制用于進程間通信時,才需要考慮一端發送,一端接收,同步問題等。
共享內存中常存放結構體數組
????為了在各個進程當中共享數據,常常將自定義的結構體數組放到共享內存中(也就是說共享內存經常只存放一個變量,而這個變量是一個結構體數組)以此實現進程間數據的讀寫。本博客以字符串為例進行存儲,將來有時間再修改程序。
共享內存中結構體中的字符串變量
由于共享內存當中需要直接開辟內存大小,存放結構體數組,每個結構體數組的內存大小是相同且固定的。因此結構體當中不能存放CString之類的不定長數據類型,否則在賦值的時候會報錯(調試的時候可以發現,CString類型的變量此時所占內存大小為NULL)。可以用char來代替CString(如用char str[30]代替CSring str)。
---------------------?
原文:https://blog.csdn.net/dashumak/article/details/83107889?
?
總結
以上是生活随笔為你收集整理的MFC:通过代码简单理解进程间的通讯机制——共享内存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 揭秘IT人才特点:中美印日四国程序员比较
- 下一篇: ARM MOV和 LDR指令关系