之前以為兩個互不相關的程序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一樣方便。
同理,通過文件映射,操作文件就可以向操作內存一樣方便。
只是將這個機制用於進程間通信時,才需要考慮一端發送,一端接收,同步問題等。