最近一直在找共享內存同步的操做,剛好這篇文章有講解。本文轉載:https://blog.csdn.net/bing_bing_bing_/article/details/82875302ios
方便記錄,copy了一份。編程
2.進程間的通訊windows
2.1進程緩存
本章講解windows平臺下,進程間的通訊方式。安全
進程是一個具備必定獨立功能的程序關於某個數據集合的一次運行活動,是操做系統動態執行的基本單元。簡單的說,進程就是一段程序的執行過程。服務器
進程和線程:進程是系統動態執行的基本單位,也是系統分配資源的基本單位;線程是進程中執行的最小單位,它能夠訪問進程的共享資源。網絡
進程之間對共享內存等進行讀寫操做,須要使用互斥機制,常使用Mutex;進程的同步機制包括Event、Semaphore,常使用Semaphore。app
進程間的通訊不只包括進程的同步互斥,還包括進程間數據的傳輸。函數
進程間經常使用的通訊方式:共享內存、管道、信號量、端口。其中Mutex和Event放在共享內存中講解,端口會在網絡編程那章講解。spa
2.2 Shared Memory
Shared Memory稱爲共享內存,它是進程間數據傳輸最快的通訊方式。因爲共享內存是全部進程均可以訪問,所以共享內存的操做須要加鎖。
如今實現進程A和進程B,進程A對共享內存寫數據,進程B對共享內存讀數據。
//進程A
#include <iostream>
#include <Windows.h>
using namespace std;
#define BUF_SIZE 4096
HANDLE H_Mutex = NULL;
HANDLE H_Event = NULL;
int main(int argc,char ** argv)
{
//步驟1:建立共享文件句柄
HANDLE shared_file = CreateFileMapping(
INVALID_HANDLE_VALUE,//物理文件句柄
NULL, //默認安全級別
PAGE_READWRITE, //PAGE_READWRITE表示可讀可寫,PAGE_READONLY表示只讀,PAGE_WRITECOPY表示只寫
0, //高位文件大小
BUF_SIZE, //低位文件大小
L"ShareMemory" //共享內存名稱
);
if (shared_file == NULL)
{
cout<<"Could not create file mapping object..."<<endl;
return 1;
}
//步驟2:映射緩存區視圖,獲得指向共享內存的指針
LPVOID lpBUF = MapViewOfFile(
shared_file, //已建立的文件映射對象句柄
FILE_MAP_ALL_ACCESS,//訪問模式:可讀寫
0, //文件偏移的高32位
0, //文件偏移的低32位
BUF_SIZE //映射視圖的大小
);
if (lpBUF == NULL)
{
cout << "Could not create file mapping object..." << endl;
CloseHandle(shared_file);
return 1;
}
H_Mutex = CreateMutex(NULL, FALSE, L"sm_mutex");//建立一個互斥器
H_Event = CreateEvent(NULL,//表示安全控制,通常爲NULL
FALSE,//肯定事件是手動置位仍是自動置位,傳入TRUE表示手動置位,傳入FALSE表示自動置位。若是爲自動置位,則對該事件調用WaitForSingleObject()後會自動調用ResetEvent()使事件變成未觸發狀態。
FALSE,//表示事件的初始狀態,傳入TRUE表示已觸發
L"sm_event"//表示事件的名稱,傳入NULL表示匿名事件
);
//步驟3:操做共享內存
char Buffer[97];
while (1)
{
cout << "A proccess:Please input the content to the process B" << endl;
cin.getline(Buffer,97);
cout << "Buffer: " <<Buffer<< endl;
WaitForSingleObject(H_Mutex, INFINITE); //使用互斥體加鎖 得到互斥器的擁有權
memcpy(lpBUF, Buffer, strlen(Buffer)+1);
ReleaseMutex(H_Mutex); //放鎖
SetEvent(H_Event);/激活等待的進程
}
CloseHandle(H_Mutex);
CloseHandle(H_Event);
//步驟4:解除映射和關閉句柄
UnmapViewOfFile(lpBUF);
CloseHandle(shared_file);
return 0;
}
//進程B
#include <iostream>
#include <Windows.h>
using namespace std;
HANDLE H_Mutex = NULL;
HANDLE H_Event = NULL;
int main(int argc, char ** argv)
{
//步驟1:打開共享文件句柄
HANDLE shared_file = OpenFileMapping(
FILE_MAP_ALL_ACCESS,//訪問模式:可讀寫
FALSE,
L"ShareMemory" //共享內存名稱
);
if (shared_file == NULL)
{
cout << "Could not open file mapping object..." << endl;
return 1;
}
//步驟2:映射緩存區視圖,獲得指向共享內存的指針
LPVOID lpBUF = MapViewOfFile(
shared_file, //已建立的文件映射對象句柄
FILE_MAP_ALL_ACCESS,//訪問模式:可讀寫
0, //文件偏移的高32位
0, //文件偏移的低32位
0 //映射視圖的大小,0表示從偏移量到文件映射的末尾,由於共享文件open端不知道其大小,因此寫0
);
if (lpBUF == NULL)
{
cout << "Could not create file mapping object..." << endl;
CloseHandle(shared_file);
return 1;
}
H_Mutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, L"sm_mutex");
if (H_Mutex == NULL)
{
cout << "open mutex failed..." <<endl;
return 1;
}
H_Event = OpenEvent(EVENT_ALL_ACCESS, FALSE, L"sm_event");
if (H_Event == NULL)
{
cout << "open mutex failed..." << endl;
return 1;
}
char Buffer[97];
//步驟3:操做共享內存
while (1)
{
cout << "B proccess:Receive data from process A" << endl;
WaitForSingleObject(H_Event, INFINITE);
WaitForSingleObject(H_Mutex, INFINITE); //使用互斥體加鎖
memcpy(Buffer, lpBUF, strlen((char *)lpBUF) + 1);
ReleaseMutex(H_Mutex); //放鎖
cout << Buffer << endl;
//system("PAUSE ");
}
CloseHandle(H_Event);
CloseHandle(H_Mutex);
//步驟4:解除映射和關閉句柄
UnmapViewOfFile(lpBUF);
CloseHandle(shared_file);
return 0;
}
以管理員的身份運行命令提示符,先執行進程A的exe文件,再執行進程B的exe文件。
進程A運行結果:
A proccess:Please input the content to the process B
Hello China
Buffer: Hello China
A proccess:Please input the content to the process B
你好,中國!
Buffer: 你好,中國!
A proccess:Please input the content to the process B
進程B運行結果:
B proccess:Receive data from process A
Hello China
B proccess:Receive data from process A
你好,中國!
B proccess:Receive data from process A
2.3 PIPE
pipe稱爲管道,其分爲命名管道、匿名管道。
2.3.1命名管道
//Server.cpp
#include <Windows.h>
#include <iostream>
using namespace std;
#define BUF_SIZE 4096
HANDLE h_Mypipe = NULL;
//步驟1:定義管道名,點表示當前主機,pipe表示管道
#define MY_NAMED_PIPE L"\\\\.\\pipe\\Named_Pipe"
int main(int argc, char ** argv)
{
//步驟2:建立命名管道
h_Mypipe = CreateNamedPipe(
MY_NAMED_PIPE, //爲命名管道建立名稱
PIPE_ACCESS_DUPLEX, //管道訪問方式:PIPE_ACCESS_DUPLEX指雙向模式
PIPE_TYPE_MESSAGE | //命名管道句柄的寫入方式:以數據塊的方式寫入管道
PIPE_READMODE_MESSAGE | //命名管道句柄的讀取方式:以數據塊的方式從管道讀取
PIPE_WAIT, //命名管道句柄的等待方式:阻塞方式
PIPE_UNLIMITED_INSTANCES, //管道所能建立的最大實例個數:1~255,
0, //管道的輸出緩衝區容量,0表示默認大小
0, //管道的輸入緩衝區容量,0表示默認大小 1000, //管道的默認等待超時,單位毫秒
NULL); //管道的安全性,NULL表示windows提供的默認安全
//INVALID_HANDLE_VALUE是CreateNamedPipe返回值,表示建立失敗
if (h_Mypipe == INVALID_HANDLE_VALUE)
{
cout << "Create Named_Pipe Failed..." << endl;
return 1;
}
//步驟3:等待客戶端的鏈接
if (!ConnectNamedPipe(h_Mypipe, NULL))
{
cout << "Connect Failed..." << endl;
return 1;
}
else
cout << "Connect Successed..." << endl;
DWORD wLen = 0;
DWORD rLen = 0;
char szBuffer[BUF_SIZE] = { 0 };
//步驟4:讀寫管道
while (1)
{
//向客戶端發送數據
cin.getline(szBuffer, BUF_SIZE);
cout << "服務器端發送數據:" << szBuffer<< endl;
if (!WriteFile(h_Mypipe, szBuffer, strlen(szBuffer) + 1, &wLen, NULL))
cout << "Write Failed..." << endl;
else
cout<<"服務器端發送成功:共"<< wLen<<"byte"<<endl;
//清除緩衝區
//memset(szBuffer, 0, BUF_SIZE);
//讀取客戶端數據
if (!ReadFile(h_Mypipe, szBuffer, BUF_SIZE, &rLen, NULL))
cout << "Read Failed..." << endl;
else
cout << "服務器接收客戶端數據:" << szBuffer << ", 共" << rLen << "byte" << endl;
}
//步驟5:關閉管道
DisconnectNamedPipe(h_Mypipe);
CloseHandle(h_Mypipe);
return 0;
}
//Client.cpp
#include <Windows.h>
#include <iostream>
using namespace std;
#define BUF_SIZE 4096
HANDLE h_Mypipe = NULL;
//步驟1:定義管道名,點表示當前主機,pipe表示管道
#define MY_NAMED_PIPE L"\\\\.\\pipe\\Named_Pipe"
int main(int argc, char ** argv)
{
//步驟2:判斷是否有可用的命名管道
//函數WaitNamedPipe:等待某個管道變成可用狀態
//形參1:表示命名管道的名稱
//形參2:NMPWAIT_USE_DEFAULT_WAIT使用管道建立時的默認超時設定;NMPWAIT_WAIT_FOREVER永遠等待
if (!WaitNamedPipe(MY_NAMED_PIPE, NMPWAIT_USE_DEFAULT_WAIT))
{
cout << "No Named_Pipe Accessible..." << endl;
return 1;
}
else
cout << "Named_Pipe Accessible..." << endl;
//步驟3:打開指定命名管道
//函數CreateFile:建立或打開對象(這裏對象指的是管道)
h_Mypipe = CreateFile(
MY_NAMED_PIPE, //建立或打開的對象(管道)名稱
GENERIC_READ | //對象的訪問方式:讀訪問
GENERIC_WRITE, //對象的訪問方式:寫訪問
0, //對象是否共享:0表示不共享
NULL, //指向一個SECURITY_ATTRIBUTES結構的指針
OPEN_EXISTING, //對象的建立方式:OPEN_EXISTING表示打開對象(管道)
FILE_ATTRIBUTE_NORMAL, //設置對象的屬性和標誌
NULL);
if (h_Mypipe == INVALID_HANDLE_VALUE)
{
cout << "Open Named_Pipe Failed..." << endl;
return 1;
}
DWORD wLen = 0;
DWORD rLen = 0;
char szBuffer[BUF_SIZE] = { 0 };
//步驟4:讀寫管道
while (1)
{
//讀取服務器端數據
if (!ReadFile(h_Mypipe, szBuffer, BUF_SIZE, &rLen, NULL))
cout << "Read Failed..." << endl;
else
cout << "客戶端接收服務器端數據:" << szBuffer << ", 共" << rLen << "byte" << endl;
//清除緩衝區
//memset(szBuffer, 0, BUF_SIZE);
//客戶端發送數據
cin.getline(szBuffer, BUF_SIZE);
cout << "客戶端發送數據:" << szBuffer << endl;
if (!WriteFile(h_Mypipe, szBuffer, strlen(szBuffer) + 1, &wLen, NULL))
cout << "Write Failed..." << endl;
else
cout << "客戶端發送成功:共" << wLen << "byte" << endl;
}
//步驟5:關閉管道
CloseHandle(h_Mypipe);
return 0;
}
服務器端運行結果:
Connect Successed...
hello world
服務器端發送數據:hello world
服務器端發送成功:共12byte
服務器接收客戶端數據:你好,中國, 共11byte
客戶端運行結果:
Named_Pipe Accessible...
客戶端接收服務器端數據:hello world, 共12byte
你好,中國
客戶端發送數據:你好,中國
客戶端發送成功:共11byte
命名管道能夠實現進程之間的全雙工通訊。服務器端惟一有權建立管道,並等待客戶端的鏈接請求;客戶端只能使用已建立的管道,並打開管道與服務器端通訊。服務器端和客戶端都是可讀可寫的。
CreateNamedPipe(參數1,參數2,參數3,參數4,參數5,參數6,參數7,參數8)函數做用是建立命名管道。
參數1爲命名管道建立名稱。
參數2是命名管道的訪問方式,PIPE_ACCESS_DUPLEX是雙向模式,即服務器進程和客戶端進程均可以從管道讀寫數據,PIPE_ACCESS_INBOUND是服務器只能從管道讀數據,客戶端只能向管道寫數據,PIPE_ACCESS_OUTBOUND是服務器只能向管道寫數據,客戶端只能從管道讀數據。
參數3分爲3部分,分別是命名管道句柄的寫入方式、命名管道句柄的讀取方式、命名管道句柄的等待方式。命名管道句柄的寫入方式分爲PIPE_TYPE_BYTE和PIPE_TYPE_MESSAGE,PIPE_TYPE_BYTE是以字節流的形式寫入管道,PIPE_TYPE_MESSAGE是以數據塊(名爲消息或報文)的形式寫入管道。命名管道句柄的讀取方式分爲PIPE_READMODE_BYTE和PIPE_READMODE_MESSAGE,PIPE_READMODE_BYTE是以字節流的形式從管道讀取數據,PIPE_READMODE_MESSAGE是以數據塊(名爲消息或報文)的形式從管道讀取數據。在字節流模式中,數據以連續的字節在管道中傳輸,數據之間沒有邊界,適合大容量數據的傳輸;在數據塊(消息)模式,數據以不連續的消息爲基本單位在管道中傳輸,消息與消息之間有邊界,適合小容量數據的傳輸。命名管道句柄的等待方式分爲PIPE_WAIT和PIPE_NOWAIT,PIPE_WAIT是阻塞模式,PIPE_NOWAIT是非阻塞模式,這是對函數WaitNamedPipe()的設置,通常都設置爲阻塞模式。
參數4是命名管道能建立的最大實例個數,範圍是1~255。它的意思是有多少進程能夠等待使用此命名管道,其中PIPE_UNLIMITED_INSTANCES表示255。
參數5是管道的輸出緩衝區容量,0表示默認大小;也能夠設爲實際數據,如4096。
參數6是管道的輸入緩衝區容量,0表示默認大小;也能夠設爲實際數據,如4096。
參數7是管道的默認等待超時,單位毫秒。這是對函數WaitNamedPipe()的等待時間設置。
參數8表示管道的安全性,NULL表示windows提供的默認安全。
返回值:執行成功,返回管道的句柄;執行失敗,返回INVALID_HANDLE_VALUE。
WriteFile(參數1, 參數2, 參數3, 參數4, 參數5)函數是將數據寫入一個文件或I/O設備。這裏的做用是向管道寫數據。
參數1是寫入數據的文件句柄,這裏指的是命名管道的句柄。
參數2是數據存放的緩衝區地址。
參數3是寫入的數據長度,單位是字節。
參數4是實際寫入管道的字節數。
參數5是指向OVERLAPPED結構體的指針,默認爲NULL,代表使用默認的同步I/O方式。
ReadFile()同理,須要注意的是參數3不能使用strlen,由於數據還沒讀取出來,沒法得知數據的大小,必須手動設置須要讀取的數據長度或使用sizeof。
WaitNamedPipe(參數1,參數2)函數的做用是等待某個管道變成可用狀態。
參數1是管道的名稱。
參數2中NMPWAIT_USE_DEFAULT_WAIT使用管道建立時的默認超時設定,NMPWAIT_WAIT_FOREVER表示一直等待。
CreateFile(參數1, 參數2,參數3,參數4,參數5,參數6,參數7)函數的做用是打開或建立文件或I/O設備。
參數1是打開或建立的對象名稱,這裏指的是命名管道的名稱。
參數2是對象的訪問方式,GENERIC_READ是讀訪問,GENERIC_WRITE是寫訪問。
參數3表示對象是否共享,0表示不共享。
參數4是指向一個SECURITY_ATTRIBUTES結構的指針,不須要了解。
參數5是對象的打開或建立方式,OPEN_EXISTING表示打開已存在的對象。
參數6是設置對象的屬性和標誌,FILE_ATTRIBUTE_NORMAL表示該對象沒有其餘屬性設置。
參數7是指定具備GENERIC_READ訪問方式的模板文件的句柄,不須要了解。
注:以上函數都是windows平臺提供的API。