1,管道,FIFO
2, 信號
3,消息隊列
4,共享類存linux
5.文件映射程序員
6.socket服務器
(1)管道(Pipe):管道可用於具備親緣關係進程間的通訊,容許一個進程和另外一個與它有共同祖先的進程之間進行通訊。
(2)命名管道(named pipe):命名管道克服了管道沒有名字的限制,所以,除具備管道所具備的功能外,它還容許無親緣關係進程間的通訊。命名管道在文件系統中有對應的文件名。命名管道經過命令mkfifo或系統調用mkfifo來建立。
(3)信號(Signal):信號是比較複雜的通訊方式,用於通知接受進程有某種事件發生,除了用於進程間通訊外,進程還能夠發送信號給進程自己;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數sigaction(實際上,該函數是基於BSD的,BSD爲了實現可靠信號機制,又可以統一對外接口,用sigaction函數從新實現了signal函數)。
(4)消息(Message)隊列:消息隊列是消息的連接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程能夠向隊列中添加消息,被賦予讀權限的進程則能夠讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩衝區大小受限等缺
(5)共享內存:使得多個進程能夠訪問同一塊內存空間,是最快的可用IPC形式。是針對其餘通訊機制運行效率較低而設計的。每每與其它通訊機制,如信號量結合使用,來達到進程間的同步及互斥。
(6)內存映射(mapped memory):內存映射容許任何多個進程間通訊,每個使用該機制的進程經過把一個共享的文件映射到本身的進程地址空間來實現它。
(7)信號量(semaphore):主要做爲進程間以及同一進程不一樣線程之間的同步手段。
(8)套接口(Socket):更爲通常的進程間通訊機制,可用於不一樣機器之間的進程間通訊。起初是由Unix系統的BSD分支開發出來的,但如今通常能夠移植到其它類Unix系統上:Linux和System V的變種都支持套接字。
通訊就是說進程之間傳遞數據。常見的方法有 pipe(管道),FIFO(命名管道),socket(套接字),SysVIPC 的 shm(共享內存)、msg queue(消息隊列),mmap(文件映射)。之前還有 STREAM,不過如今比較少見了(好像)。
同步的意思是說,讓不一樣進程可以在同時到達一個已知的特定狀態以前等待另外一方的執行。Linux 下常見的同步方法有SysVIPC 的 sem(信號量)、file locking / record locking(經過 fcntl 設定的文件鎖、記錄鎖)、futex(基於共享內存的快速用戶態互斥鎖)。針對線程(pthread)的還有 pthread_mutex 和 pthread_cond(條件變量)。
除了這些特定的同步對象以外,還有一些同步方法是與通訊方法不可分離的,包括:對 pipe/FIFO/socket 和 msg queue 的阻塞等待、對子進程退出事件的等待(wait族)、對線程退出時間的等待(pthread_join)
另外還有一個不能不提的,就是信號。Unix 信號是異步處理的、能夠終端接收進程執行過程的特殊 IPC 方式——其實既能夠算同步也能夠選通信了。網絡
#####################################################################################################################數據結構
linux下進程間通訊的幾種主要手段簡介:
1. 管道(Pipe)及有名管道(named pipe):管道可用於具備親緣關係進程間的通訊,有名管道克服了管道沒有名字的限制,所以,除具備管道所具備的功能外,它還容許無親緣關係進程間的通 信;
2. 信號(Signal):信號是比較複雜的通訊方式,用於通知接受進程有某種事件發生,除了用於進程間通訊外,進程還能夠發送信號給進程自己;linux除 了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數 sigaction(實際上,該函數是基於BSD的,BSD爲了實現可靠信號機制,又可以統一對外接口,用sigaction函數從新實現了signal 函數);
3. 報文(Message)隊列(消息隊列):消息隊列是消息的連接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程能夠向隊列中添加消息,被賦予讀權限的進程則能夠讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字 節流以及緩衝區大小受限等缺點。
4. 共享內存:使得多個進程能夠訪問同一塊內存空間,是最快的可用IPC形式。是針對其餘通訊機制運行效率較低而設計的。每每與其它通訊機制,如信號量結合使 用,來達到進程間的同步及互斥。
5. 信號量(semaphore):主要做爲進程間以及同一進程不一樣線程之間的同步手段。
6. 套接口(Socket):更爲通常的進程間通訊機制,可用於不一樣機器之間的進程間通訊。起初是由Unix系統的BSD分支開發出來的,但如今通常能夠移植 到其它類Unix系統上:Linux和System V的變種都支持套接字。app
#####################################################################################################異步
用於進程間通信(IPC)的四種不一樣技術:
1. 消息傳遞(管道,FIFO,posix和system v消息隊列)
2. 同步(互斥鎖,條件變量,讀寫鎖,文件和記錄鎖,Posix和System V信號燈)
3. 共享內存區(匿名共享內存區,有名Posix共享內存區,有名System V共享內存區)
4. 過程調用(Solaris門,Sun RPC)
消息隊列和過程調用每每單獨使用,也就是說它們一般提供了本身的同步機制.相反,共享內存區一般須要由應用程序提供的某種同步形式才能正常工做.解決某個特定問題應使用哪一種IPC不存在簡單的斷定,應該逐漸熟悉各類IPC形式提供的機制,而後根據特定應用的要求比較它們的特性.
必須考慮的四個前提:
1. 聯網的仍是非聯網的.IPC適用於單臺主機上的進程或線程間的.若是應用程序有可能分佈到多臺主機上,那就要考慮使用套接字代替IPC,從而簡化之後向聯網的應用程序轉移的工做.
2. 可移植性.
3. 性能,在具體的開發環境下運行測試程序,比較幾種IPC的性能差別.
4. 實時調度.若是須要這一特性,並且所用的系統也支持posix實時調度選項,那就考慮使用Posix的消息傳遞和同步函數.
各類IPC之間的一些主要差別:
1. 管道和FIFO是字節流,沒有消息邊界.Posix消息和System V消息則有從發送者向接受者維護的記錄邊界(eg:TCP是沒有記錄邊界的字節流,UDP則提供具備記錄邊界的消息).
2. 當有一個消息放置到一個空隊列中時,Posix消息隊列可向一個進程發送一個信號,或者啓動一個新的線程.System V則不提供相似的通知形式.
3. 管道和FIFO的數據字節是先進先出的.Posix消息和System V消息具備由發送者賦予的優先級.從一個Posix消息隊列讀出時,首先返回的老是優先級最高的消息.從一個System V消息隊列讀出時,讀出者能夠要求想要的任意優先級的消息.
4. 在衆多的消息傳遞技術—管道,FIFO,Posix消息隊列和System V消息隊列—中,可從一個信號處理程序中調用的函數只有read和write(適用於管道和FIFO).
比較不一樣形式的消息傳遞時,咱們感興趣的有兩種測量尺度:
1. 帶寬(bandwidth):數據經過IPC通道轉移的速度.爲測量該值,咱們從一個進程向另外一個進程發送大量數據(幾百萬字節).咱們還給不一樣大小的I/O操做(例如管道和FIFO的write和read操做)測量該值,期待發現帶寬隨每一個I/O操做的數據量的增加而增加的規律.
2. 延遲(latency):一個小的IPC消息從一個進程到令一個進程再返回來所花的時間.咱們測量的是隻有一個1個字節的消息從一個進程到令一個進程再回來的時間(往返時間)
在現實世界中,帶寬告訴咱們大塊數據經過一個IPC通道發送出去需花多長時間,然而IPC也用於傳遞小的控制信息,系統處理這些小消息所需的時間就由延遲提供.這兩個數都很重要.
#########################################################################################################################socket
程序員必須讓擁有依賴關係的進程 集協調,這樣才能達到進程的共同目標。可使用兩種技術來達到協調。第一種技術在具備通訊依賴關係的兩個進程間傳遞信息。這種技術稱作進程間通訊(interprocess communication)。第二種技術是同步,當進程間相互具備合做依賴時使 用。這兩種類型的依賴關係能夠同時存在。分佈式
通常而言,進程有單獨的地址空 間。咱們能夠了解下可執行程序被裝載到內存後創建的一系列映射等理解這一點。如此以來意味着若是咱們有兩個進程(進程A和進程B),那麼,在進程A中聲明的數據對於進程B是不可用的。並且,進程B看不到進程A中發生的事件,反之亦然。若是進程A和B一塊兒工做來完成某個任務,必須有一個在兩個進程 間通訊信息和時間的方法。咱們這裏能夠去看看基本的進程組件。注意進程有一個文本、數據以及堆棧片段。進程可能也有從自由存儲空間中分配的其它內存。進程 所佔有的數據通常位於數據片段、堆棧片段或進程的動態分配內存中。數據對於其它進程來講是受保護的。爲了讓一個進程訪問另一個進程的數據,必須最終使用 操做系統調用。與之相似,爲了讓一個進程知道另外一個進程中文本片段中發生的事件,必須在進程間創建一種通訊方式。這也須要來自操做系統API的幫助。當進程將數據發送到另外一進程時,稱作IPC(interprocess communication,進程間通訊)。下面先列舉幾種不一樣類型的進程間通訊 方式:函數
進程間通訊 描 述
環境變量/文件描述符 子進程接受父進程環境數據的拷貝以及全部文件描述符。父進程能夠在它的數據片段或環境中設置必定的變量,同時子進程接收這 些值。父進程能夠打開文件,同時推動讀/寫指針的位置,並且子進程使用相同的偏移訪問該文件。
命令行參數 在調用exec或派生函數期間,命令行參數能夠傳遞給子進程。
管道 用於相關和無關進程間的通訊,並且造成兩個進程間的一個通訊通道,一般使用文件讀寫程序訪問。
共享內存 兩個進程以外的內存塊,兩個進程都可以訪問它。
DDE (動態數據交換Dynamic data exchange),
使用客戶機/服務器模型(C/S),服務器對客戶的數據 或動做請求做出反應。
1、 環境變量、文件描述符:
當建立一個子進程時,它接受了父進程許多資源的拷貝。子進程接受了父進程的文本、堆棧
以及數據片段的拷貝。子進程也接受了父進程的環境數據以及全部文件描述符的拷貝。子進
程從父進程繼承資源的過程創造了進程間通訊的一個機會。父進程能夠在它的數據片段或環
境中設置必定的變量,子進程因而接受這些值。一樣,父進程也能夠打開一個文件,推動到
文件內的指望位置,子進程接着就能夠在父進程離開讀/寫指針的準確位置訪問該文件。
這類通訊的缺陷在於它是單向的、一次性的通訊。也就是說,除了文件描述外,若是子進程
繼承了任何其它數據,也僅僅是父進程拷貝的全部數據。 一旦建立了子進程,由子進程對
這些變量的任何改變都不會反映到父進程的數據中。一樣,建立子進程後,對父進程數據的
任何改變也不會反映到子進程中。因此,這種類型的進程間通訊更像指揮棒傳遞。一旦父進
程傳遞了某些資源的拷貝,子進程對它的使用就是獨立的,必須使用原始傳遞資源。
2、命令行參數:
經過命令行參數(command-line argument)能夠完成另外一種單向、一次性的進程間通訊
我前面的文章已經提到過使用命令行參數。命令行參數在調用一個exec或派生調用操做系
統時傳遞給子進程。命令行參數一般在其中一個參數中做爲NULL終 止字符串傳遞給exec
或派生函數調用。這些函數能夠按單向、一次性方式給子進程傳遞值。WINDOWS有調用執行
exe程序的API。你們能夠去參考一下ShellExecuteA函數相關。
除了文件描述符外,繼承資源是IPC的單向、一次 性形式。
傳遞命令參數也是單向、一次性的IPC方 法。
這些方法也只有限制於關聯進程,如 果不關聯,命令行參數和繼承資源不能使用。
還有另外一種結構,稱作管道(Pipe), 它 能夠用於在關聯進程間以及無關聯進程間進行通訊。
3、管道通訊:
繼承資源以及命令行參數是最簡單形式的進程間通訊。它們同時有兩個主要限制。
管道是一種數據結構,像一個序列化文件同樣訪問。它造成了兩個進程間的一種通訊渠道。
管道結構經過使用文本和寫方式來訪問。若是進程A但願經過管道發送數據給進程B,那麼
進程A向管道寫入數據。爲了讓進程B接收此數據,進程B必須讀取管道,與命令行參數的
IPC形式不同。管道能夠雙向通訊。兩進程間的數據流是雙向通訊的。管道能夠在程序的
整個執行期間使用,在進程間發送和接收數據。因此,管道充當可訪問管道的進程間的一種
可活連接,有兩種基本管道類型:
1. 匿名管道
2. 命名管道
上面的圖能夠看出在沒有管道時,兩進程是不能互寫的。
創建管道後就能夠相互通訊了。
只有關聯進程可使用匿名管道來通訊。
無關聯進程必須使用命名管道。
匿名管道:經過文件描述符或文件句柄提供對匿名管道的訪問。對系統API的調用建立一個管道,並返回一個文件描述符。
這個文件描述符是用做read()或write()函數的一個參數。
當經過文件描述符調用read()或write()時,數據的源和目標就是管道。
例如,在OS/2環境中使用操做系統函數DosCreatePipe()建立匿名管道:
int mian( void )
{
PFHILE readHandle;
PFHILE writeHandle;
DosCreatePipe( readHandle, writeHandle, size );
…
…
…
}
在WINDOWS下例如我寫的ASM集 成環境經過管道與DOS命令行通訊的MFC下 代碼塊:
void CIDEManager::Commond( CString cmd, char* buf, unsigned int bufsize )
{
SECURITY_ATTRIBUTES sa;
HANDLE hRead, hWrite;
sa.nLength = sizeof( SECURITY_ATTRIBUTES );
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if ( !CreatePipe( &hRead, &hWrite, &sa, 0 ) ) // 建立管道
{
return;
}
STARTUPINFO si;
PROCESS_INFORMATION pi;
si.cb = sizeof( STARTUPINFO );
GetStartupInfo( &si );
si.hStdError = hWrite;
si.hStdOutput = hWrite;
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
if ( !CreateProcess( NULL, ( LPTSTR )( LPCTSTR )cmd, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi ) )
{
return;
}
CloseHandle( hWrite );
DWORD bytesRead;
while ( TRUE )
{
memset( buf, 0, bufsize );
if ( ReadFile( hRead, buf, bufsize, &bytesRead, NULL ) != NULL )
{
break;
}
Sleep( 200 );
}
CloseHandle( hRead );
return;
}
命名管道:將管道用做兩個無關聯進程間的通訊渠道,程序員必須使用命名管道,它能夠看做一種具備某名字的特殊類型文件。進程能夠根據它的 名字訪問這個管道。經過匿名管道,父和子進程能夠單獨使用文件描述符來訪問他們所共享的管道,由於子進程繼承了父進程的文件描述符,同時文件描述符用read()或write()函數的參數。由於無關進 程不能訪問彼此的文件描述符,因此不能使用匿名管道。因爲命名管道提供該管道的一個等價文件名,任何知道此管道名字的進程均可以訪問它。
下面是命名管道相對於匿名管道的優勢:
命名管道能夠被無關聯進程使用。
命名管道能夠持久。建立它的程序退出後,它們仍然能夠存在。
命名管道能夠在網絡或分佈式環境中使用。
命名管道容易用於多對一關係中。
與訪問匿名管道同樣,命名管道也是經過read()或write()函數來訪問。
二者之間的主要區別在於命名管道的建立方式以及誰能夠訪問它們。
命名管道能夠創建一個進程間通訊的C/S模型。
訪問命名管道的進程可能都位於同一臺機器上,或位於經過網絡通訊的不一樣 機器上。因爲管道的名字能夠經過管道所在服務器的邏輯名,因此可以跨網絡訪問管道。例如,\\\\ServerName\\Pipe\\MyPipe(不 區分大小寫)能夠做爲一個管道名字。假如Server1是網絡服務器的名字。當打開或訪問這個管道 的調用解析文件名時,首先應該定位Server1,而後訪問MyPipe。 例子以下:
服務器端:
int main( void )
{
HANDLE pipehandle;
char buf[ 256 ];
DWORD bytesRead;
if( ( pipehandle = CreateNamedPipe( "\\\\.\\Pipe\\cao", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 0, 0, 5000, NULL ) ) == INVALID_HANDLE_VALUE )
{
printf( "CreateNamedPipe failed with error %d\n", GetLastError() );
system( "pause" );
return 0;
}
printf( "server is running\n" );
if( ConnectNamedPipe( pipehandle, NULL ) == 0 )
{
printf( "connectNamedPipe failed with error %d\n", GetLastError() );
CloseHandle( pipehandle );
system( "pause" );
return 0;
}
if( ReadFile( pipehandle, buf, sizeof( buf ), &bytesRead, NULL ) == 0 )
{
printf( "ReadFile failed with error %d\n", GetLastError() );
CloseHandle( pipehandle );
system( "pause" );
return 0;
}
printf( "%s\n", buf );
if ( DisconnectNamedPipe( pipehandle ) == 0 )
{
printf( "DisconnectNamedPipe failed with error %d\n", GetLastError() );
CloseHandle( pipehandle );
system( "pause" );
return 0;
}
system( "pause" );
return 0;
}
客戶端:
int main( void )
{
HANDLE pipehandle;
DWORD writesbytes;
char buff[ 256 ];
if( WaitNamedPipe( "\\\\.\\Pipe\\cao", NMPWAIT_WAIT_FOREVER ) == 0 )
{
printf( "WaitNamedPipe failed with error %d\n", GetLastError() );
system( "pause" );
return 0;
}
if( ( pipehandle = CreateFile( "\\\\.\\Pipe\\cao", GENERIC_READ | GENERIC_WRITE, 0, ( LPSECURITY_ATTRIBUTES )NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, ( HANDLE )NULL ) ) == INVALID_HANDLE_VALUE )
{
printf( "CreateFile failed with error %d\n", GetLastError() );
system( "pause" );
return 0;
}
ZeroMemory( &buff, sizeof( buff ) );
gets( buff );
if( WriteFile( pipehandle, buff, sizeof( buff ), &writesbytes, NULL ) == 0 )
{
printf( "WriteFile failed with error %d\n", GetLastError() );
CloseHandle( pipehandle );
system( "pause" );
return 0;