1. 程式人生 > >程序間通訊(IPC)

程序間通訊(IPC)

    程序間通訊IPC,也就是Inter-Process Communication的縮寫。

    首先我們明白一個程序其實就是一個狹義上的程式。

    一個伺服器也就是一個程序。比如客戶端和伺服器的連線就是兩個程序在通訊,只是這兩個程序並不在同一臺計算機上,它們程序間的通訊方式就是我們非常熟悉的sockt介面,更下層一些可能就是TCP/IP協議。

    那麼程序間為什麼要通訊呢?簡單說來,單程序的程式已經遠遠不能滿足我們的需要了,程序與程序間通訊就組成了更大的一個系統。比如P2P的軟體就是兩個不同計算機的程式進行資料互動,從而完成下載或通訊的任務。

   程序由於是核心(OS kernel)的功能,因此程序的建立和通訊其實和各種OS緊密相關的。當然現在OS在架構上有很多思想是想通的,所以程序間通訊在各種作業系統上有不同也有相同的地方。

   另外,當前很多語言和框架(Framework)為了實現跨平臺性,都封裝了程序的操作,將執行緒的使用提高到了語言的層面。而不用developer再去進行系統呼叫(或系統API)來操作程序了。比如Java語言,程序和執行緒就是其語言提供的。再比如QT框架,也提供了程序執行緒的操作,彌補了C/C++語言上沒有提供程序執行緒的遺憾。我們這裡還是有必要了解下各個OS在系統呼叫層次的程序操作。

    一. 在Windows OS上,有幾種常用的程序間通訊方式:

        1 檔案對映

        檔案對映(Memory-Mapped Files)能使程序把檔案內容當作程序地址區間一塊記憶體那樣來對待。因此,程序不必使用檔案I/O操作,只需簡單的指標操作就可讀取和修改檔案的內容。

Win32 API允許多個程序訪問同一檔案對映物件,各個程序在它自己的地址空間裡接收記憶體的指標。通過使用這些指標,不同程序就可以讀或修改檔案的內容,實現了對檔案中資料的共享。

應用程式有三種方法來使多個程序共享一個檔案對映物件。

(1)繼承:第一個程序建立檔案對映物件,它的子程序繼承該物件的控制代碼。

(2)命名檔案對映:第一個程序在建立檔案對映物件時可以給該物件指定一個名字(可與檔名不同)。第二個程序可通過這個名字開啟此檔案對映物件。另外,第一個程序也可以通過一些其它IPC機制(有名管道、郵件槽等)把名字傳給第二個程序。

(3)控制代碼複製:第一個程序建立檔案對映物件,然後通過其它IPC機制(有名管道、郵件槽等)把物件控制代碼傳遞給第二個程序。第二個程序複製該控制代碼就取得對該檔案對映物件的訪問許可權。

檔案對映是在多個程序間共享資料的非常有效方法,有較好的安全性。但檔案對映只能用於本地機器的程序之間,不能用於網路中,而開發者還必須控制程序間的同步。

        2 共享記憶體

        Win32 API中共享記憶體(Shared Memory)實際就是檔案對映的一種特殊情況。程序在建立檔案對映物件時用0xFFFFFFFF來代替檔案控制代碼(HANDLE),就表示了對應的檔案對映物件是從作業系統頁面檔案訪問記憶體,其它程序開啟該檔案對映物件就可以訪問該記憶體塊。由於共享記憶體是用檔案對映實現的,所以它也有較好的安全性,也只能運行於同一計算機上的程序之間。

       3 匿名管道

       管道(Pipe)是一種具有兩個端點的通訊通道:有一端控制代碼的程序可以和有另一端控制代碼的程序通訊。管道可以是單向-一端是隻讀的,另一端點是隻寫的;也可以是雙向的一管道的兩端點既可讀也可寫。

匿名管道(Anonymous Pipe)是 在父程序和子程序之間,或同一父程序的兩個子程序之間傳輸資料的無名字的單向管道。通常由父程序建立管道,然後由要通訊的子程序繼承通道的讀端點控制代碼或寫 端點控制代碼,然後實現通訊。父程序還可以建立兩個或更多個繼承匿名管道讀和寫控制代碼的子程序。這些子程序可以使用管道直接通訊,不需要通過父程序。

匿名管道是單機上實現子程序標準I/O重定向的有效方法,它不能在網上使用,也不能用於兩個不相關的程序之間。

      4 命名管道

      命名管道(Named Pipe)是伺服器程序和一個或多個客戶程序之間通訊的單向或雙向管道。不同於匿名管道的是命名管道可以在不相關的程序之間和不同計算機之間使用,伺服器建立命名管道時給它指定一個名字,任何程序都可以通過該名字開啟管道的另一端,根據給定的許可權和伺服器程序通訊。

命名管道提供了相對簡單的程式設計介面,使通過網路傳輸資料並不比同一計算機上兩程序之間通訊更困難,不過如果要同時和多個程序通訊它就力不從心了。

     5 動態連線庫

     Win32動態連線庫(DLL)中的全域性資料可以被呼叫DLL的所有程序共享,這就又給程序間通訊開闢了一條新的途徑,當然訪問時要注意同步問題。

雖然可以通過DLL進行程序間資料共享,但從資料安全的角度考慮,我們並不提倡這種方法,使用帶有訪問許可權控制的共享記憶體的方法更好一些。

     6 遠端過程呼叫

     Win32 API提供的遠端過程呼叫(RPC)使應用程式可以使用遠端呼叫函式,這使在網路上用RPC進行程序通訊就像函式呼叫那樣簡單。RPC既可以在單機不同程序間使用也可以在網路中使用。

由於Win32 API提供的RPC服從OSF-DCE(Open Software Foundation Distributed Computing Environment)標準。所以通過Win32 API編寫的RPC應用程式能與其它作業系統上支援DEC的RPC應用程式通訊。使用RPC開發者可以建立高效能、緊密耦合的分散式應用程式。

     7 Sockets

    Windows Sockets規範是以U.C.Berkeley大學BSD UNIX中流行的Socket介面為範例定義的一套Windows下的網路程式設計介面。除了Berkeley Socket原有的庫函式以外,還擴充套件了一組針對Windows的函式,使程式設計師可以充分利用Windows的訊息機制進行程式設計。

現在通過Sockets實現程序通訊的網路應用越來越多,這主要的原因是Sockets的跨平臺性要比其它IPC機制好得多,另外WinSock 2.0不僅支援TCP/IP協議,而且還支援其它協議(如IPX)。Sockets的唯一缺點是它支援的是底層通訊操作,這使得在單機的程序間進行簡單資料傳遞不太方便,這時使用下面將介紹的WM_COPYDATA訊息將更合適些。

    8 WM_COPYDATA訊息

    WM_COPYDATA是一種非常強大卻鮮為人知的訊息。當一個應用向另一個應用傳送資料時,傳送方只需使用呼叫SendMessage函式,引數是目的視窗的控制代碼、傳遞資料的起始地址、WM_COPYDATA訊息。接收方只需像處理其它訊息那樣處理WM_COPY DATA訊息,這樣收發雙方就實現了資料共享。

WM_COPYDATA是一種非常簡單的方法,它在底層實際上是通過檔案對映來實現的。它的缺點是靈活性不高,並且它只能用於Windows平臺的單機環境下。

   二. 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的變種都支援套接字。

    三. 在Symbian系統上,IPC的主要實現方式就是client-server框架。它可是symbian OS 上的元老之一。在Psion Series 5上就已經實現了,直到symbian OS v9.5都在不斷的改進擴充套件新的功能。client-server在OS上隨處可見,比如F32(檔案服務)、ESOCK(socket服務)、大名鼎鼎的WSERV(window 服務)以及ETEL(電話服務)等等。從使用client-server的角度來看,developer只需要關注4個介面類:CServer2、CSession2、RSessionBase和RSubSessionBase,其中前兩個類的例項運行於user端的server端,後兩個類實際上是提供給client使用的介面,運行於user端的client端。由此可見client-server框架非常便利,它的工作過程可以大體描述為:

1.        client使用全域性唯一的server名字與server建立起連線,建立一個session。

2.        如果server沒有啟動,就啟動之。

3.        server一啟動就是發出一個request檢查kernel端訊息佇列是不是有待處理的訊息。

4.        client傳送訊息給server。實際上將訊息放入到kernel端的訊息佇列中。

5.        client等待訊息的處理結果。

6.        一旦有訊息,server就取出一個訊息

7.        server處理訊息

8.        server處理完訊息後,通知client訊息已經處理完。

注意,訊息佇列FIFO佇列,並且server一次只能取出一個訊息,不能並行處理。