程序間通訊之客戶程序 伺服器程序屬性

2021-09-08 17:07:22 字數 2969 閱讀 9783

下面詳細說明客戶程序和伺服器程序的某些屬性,這些屬性受到它們之間所使用的ipc型別的影響。最簡單的關係型別是使客戶呼叫fork然後呼叫exec執行所希望的伺服器程序。

在fork之前先建立兩個半雙工管道使資料可在兩個方向傳輸。中的圖15-8是這種形式的乙個例子。被執行的伺服器程式可能是設定使用者id的程式,這使它具有了特權。伺服器程序檢視客戶程序的實際使用者id就可以決定客戶程序的身份。(回憶從中可以了解到在exec前後實際使用者id和實際組id並沒有改變。)

在這種安排下,可以構築乙個開放式伺服器(open server)。它為客戶程序開啟檔案而不是客戶程序自己呼叫open函式。這樣就可以在正常的unix使用者/組/其他許可權之上或之外,增加附加的許可權檢查。假定伺服器程序執行的是設定使用者id程式,這給予了它附加的許可權(很可能是root許可權)。伺服器程序用客戶程序的實際使用者id以決定是否給予它對所請求檔案的訪問許可權。使用這種方式,可以構築乙個伺服器程序,它允許某種使用者獲得通常沒有的訪問許可權。

在此例子中,因為伺服器程序是父程序的子程序,所以它能做的一切是將檔案內容傳送給父程序。這種方式對普通檔案完全夠用,但是對特殊裝置檔案卻不能工作。我們所希望能做的是使伺服器程序開啟所要的檔案,並且送回檔案描述符。但是實際情況卻是父程序可向子程序傳送開啟檔案描述符,而子程序則不能向父程序傳回檔案描述符

圖15-12中示出了另一種型別的伺服器程序。這種伺服器程序是乙個守護程序,所有客戶程序用某種形式的ipc與其聯絡。對於這種形式的客戶程序-伺服器程序關係,不能使用管道。要求使用命名的ipc,例如fifo或訊息佇列。對於fifo,如果伺服器程序必須將資料送回客戶程序,則對每個客戶程序都要有單獨使用的fifo。如果客戶程序-伺服器程序應用程式只有客戶程序向伺服器程序傳送資料,則只需要乙個眾所周知的fifo。

使用訊息佇列則存在多種可能性:

(1)在伺服器程序和所有客戶程序之間只使用乙個佇列,使用訊息的型別字段指明誰是訊息的接收者。例如,客戶程序可以用型別欄位1傳送它們的訊息。在請求之中應包括客戶程序的程序id。此後,伺服器程序在傳送響應訊息時,將型別字段設定為客戶程序的程序id。伺服器程序只接收型別欄位為1的訊息(msgrcv的第四個引數),客戶程序則只接收型別字段等於它程序id的訊息。

(2)另一種方法是每個客戶程序使用乙個單獨的訊息佇列。在向伺服器程序傳送第乙個請求之前,每個客戶程序先建立它自己的訊息佇列,建立時使用鍵ipc_private。伺服器程序也有它自己的佇列,其鍵或識別符號是所有客戶程序都知道的。客戶程序將其第乙個請求送到伺服器的眾所周知的佇列上,該請求中應包含其客戶程序訊息佇列的佇列id。伺服器程序將其第乙個響應送至客戶程序佇列,此後的所有請求和響應都在此佇列上交換。

使用這種技術的乙個問題是:每個客戶程序專用佇列通常只有乙個訊息在其中——或者是對伺服器的乙個請求,或者是對客戶程序的響應。這似乎是對有限的系統資源(訊息佇列)的浪費,為此可以用乙個fifo來代替。另乙個問題是伺服器程序需從多個佇列讀訊息。對於訊息佇列,select和poll都不起作用。

使用訊息佇列的這兩種技術都可以用共享儲存段和同步方法(訊號量或記錄鎖)實現。

這種型別的客戶程序-伺服器程序關係(客戶程序和伺服器程序是無關係程序)的問題是:伺服器程序如何準確地標識客戶程序?除非伺服器程序正在執行一種非特權操作,否則伺服器程序知道客戶程序的身份是很重要的。例如,若伺服器程序是乙個設定使用者id程式,就有這種要求。雖然,所有這幾種形式的ipc都經由核心,但是它們並未提供任何措施使核心能夠標識傳送者。

對於訊息佇列,如果在客戶程序和伺服器程序之間使用乙個專用佇列(於是一次只有乙個訊息在該佇列上),那麼佇列的msg_lspid包含了對方程序的程序id。但是當客戶程序將請求傳送給伺服器程序時,我們想要的是客戶程序的有效使用者id,而不是它的程序id。現在還沒有一種可移植的方法,在已知程序id的情況下可以得到有效使用者id。(核心在程序表項中自然地保持有這兩種值,但是除非徹底檢查核心儲存空間,否則已知乙個,無法得到另乙個。)

使伺服器程序可以標識客戶程序:這一技術既可使用fifo、訊息佇列或訊號量,也可使用共享儲存。我們假定圖15-12使用了fifo。客戶程序必須建立它自己的fifo,並且設定該fifo的檔案訪問許可權,使得只允許使用者讀,使用者寫。假定伺服器程序具有超級使用者特權(或者它很可能並不關心客戶程序的真實標識),所以伺服器程序仍可讀、寫此fifo。當伺服器程序在眾所周知的fifo上接收到客戶程序的第乙個請求時(它應當包含客戶程序的專用fifo的標識),伺服器程序呼叫針對客戶程序專用fifo的stat或fstat。伺服器程序假設客戶程序的有效使用者id是fifo的所有者(stat結構的st_uid欄位)。伺服器程序驗證該fifo只有使用者讀、使用者寫許可權。伺服器程序還應檢查該fifo的三個時間量(stat結構中的st_atime,st_mtime和st_ctime欄位),要檢查它們與當前時間是否很接近(例如 不早於當前時間15s或30s)。如果乙個有預謀的客戶程序可以建立乙個fifo,使另乙個使用者成為其所有者,並且設定該檔案的許可權為使用者讀和使用者寫,那麼在系統中就存在了其他基礎性的安全問題。

為了用xsi ipc實現這種技術,與每個訊息佇列、訊號量以及共享儲存段相關的ipc_perm結構,其中cuid和cgid欄位標識ipc結構的建立者。以fifo為例,伺服器程序應當要求客戶程序建立該ipc結構,並使客戶程序將訪問許可權設定為只允許使用者讀和使用者寫。伺服器程序也應檢驗與該ipc相關的時間值與當前時間是否很接近(因為這些ipc結構在顯式地刪除之前一直存在)。

程序間通訊小結

說明了程序間通訊的多種形式:管道、命名管道(fifo)以及另外三種ipc形式(通常稱為xsi ipc),即訊息佇列、訊號量和共享儲存。訊號量實際上是同步原語而不是ipc,常用於共享資源的同步訪問。

要學會使用管道和fifo,因為在大量應用程式中仍可有效地使用這兩種基本技術。

在新的應用程式中,要盡可能避免使用訊息佇列以及訊號量,而應當考慮全雙工管道和記錄鎖,它們使用起來會簡單得多。

本篇博文內容摘自《unix環境高階程式設計》(第2版),僅作個人學習記錄所用。關於本書可參考:

程序管理之程序間通訊

四 訊息佇列 message queue 五 共享記憶體 shared memory 六 套接字 socket 程序作為人類的發明,自然也免不了脫離人類的習性,也有通訊的需求。如果程序之間不進行任何通訊,那麼程序所能完成的任務就要大打折扣。人類的通訊方式無外乎對白 通過聲音溝通 打手勢 寫信 發電報...

程序間通訊之

共享記憶體的概述 共享記憶體就是允許兩個不相關的程序訪問同乙個邏輯記憶體。共享記憶體是在兩個正在執行的程序之間共享和傳遞資料的一種非常有效的方式。不同程序之間共享的記憶體通常安排為同一段物理記憶體。程序可以將同一段共享記憶體連線到它們自己的位址空間中,所有程序都可以訪問共享記憶體中的位址,就好像它們...

程序間通訊之協同程序

unix系統過濾程式從標準輸入讀取資料,對其進行適當處理後寫到標準輸出。幾個過濾程式通常在shell管道命令列中線性地連線。當乙個程式產生某個過濾程式的輸入,同時又讀取該過濾程式的輸出時,則該過濾程式就成為協同程序 coprocess korn shell提供了協同程序。bourne shell b...