下面詳細說明客戶進程和服務器進程的某些屬性,這些屬性受到它們之間所使用的ipC類型的影響。最簡單的關系類型是使客戶調用fork然后調用exec執行所希望的服務器進程。
在fork之前先創建兩個半雙工管道使數據可在兩個方向傳輸。http://www.CUOXin.com/nufangrensheng/p/3561379.html中的圖15-8是這種形式的一個例子。被執行的服務器程序可能是設置用戶ID的程序,這使它具有了特權。服務器進程查看客戶進程的實際用戶ID就可以決定客戶進程的身份。(回憶http://www.CUOXin.com/nufangrensheng/p/3510821.html,從中可以了解到在exec前后實際用戶ID和實際組ID并沒有改變。)
在這種安排下,可以構筑一個開放式服務器(open server)。它為客戶進程打開文件而不是客戶進程自己調用open函數。這樣就可以在正常的UNIX用戶/組/其他權限之上或之外,增加附加的權限檢查。假定服務器進程執行的是設置用戶ID程序,這給予了它附加的權限(很可能是root權限)。服務器進程用客戶進程的實際用戶ID以決定是否給予它對所請求文件的訪問權限。使用這種方式,可以構筑一個服務器進程,它允許某種用戶獲得通常沒有的訪問權限。
在此例子中,因為服務器進程是父進程的子進程,所以它能做的一切是將文件內容傳送給父進程。這種方式對普通文件完全夠用,但是對特殊設備文件卻不能工作。我們所希望能做的是使服務器進程打開所要的文件,并且送回文件描述符。但是實際情況卻是父進程可向子進程傳送打開文件描述符,而子進程則不能向父進程傳回文件描述符。
http://www.CUOXin.com/nufangrensheng/p/3561632.html圖15-12中示出了另一種類型的服務器進程。這種服務器進程是一個守護進程,所有客戶進程用某種形式的IPC與其聯系。對于這種形式的客戶進程-服務器進程關系,不能使用管道。要求使用命名的IPC,例如FIFO或消息隊列。對于FIFO,如果服務器進程必須將數據送回客戶進程,則對每個客戶進程都要有單獨使用的FIFO。如果客戶進程-服務器進程應用程序只有客戶進程向服務器進程發送數據,則只需要一個眾所周知的FIFO。
使用消息隊列則存在多種可能性:
(1)在服務器進程和所有客戶進程之間只使用一個隊列,使用消息的類型字段指明誰是消息的接收者。例如,客戶進程可以用類型字段1發送它們的消息。在請求之中應包括客戶進程的進程ID。此后,服務器進程在發送響應消息時,將類型字段設置為客戶進程的進程ID。服務器進程只接收類型字段為1的消息(msgrcv的第四個參數),客戶進程則只接收類型字段等于它進程ID的消息。
(2)另一種方法是每個客戶進程使用一個單獨的消息隊列。在向服務器進程發送第一個請求之前,每個客戶進程先創建它自己的消息隊列,創建時使用鍵IPC_PRIVATE。服務器進程也有它自己的隊列,其鍵或標識符是所有客戶進程都知道的??蛻暨M程將其第一個請求送到服務器的眾所周知的隊列上,該請求中應包含其客戶進程消息隊列的隊列ID。服務器進程將其第一個響應送至客戶進程隊列,此后的所有請求和響應都在此隊列上交換。
使用這種技術的一個問題是:每個客戶進程專用隊列通常只有一個消息在其中——或者是對服務器的一個請求,或者是對客戶進程的響應。這似乎是對有限的系統資源(消息隊列)的浪費,為此可以用一個FIFO來代替。另一個問題是服務器進程需從多個隊列讀消息。對于消息隊列,select和poll都不起作用。
使用消息隊列的這兩種技術都可以用共享存儲段和同步方法(信號量或記錄鎖)實現。
這種類型的客戶進程-服務器進程關系(客戶進程和服務器進程是無關系進程)的問題是:服務器進程如何準確地標識客戶進程?除非服務器進程正在執行一種非特權操作,否則服務器進程知道客戶進程的身份是很重要的。例如,若服務器進程是一個設置用戶ID程序,就有這種要求。雖然,所有這幾種形式的IPC都經由內核,但是它們并未提供任何措施使內核能夠標識發送者。
對于消息隊列,如果在客戶進程和服務器進程之間使用一個專用隊列(于是一次只有一個消息在該隊列上),那么隊列的msg_lspid包含了對方進程的進程ID。但是當客戶進程將請求發送給服務器進程時,我們想要的是客戶進程的有效用戶ID,而不是它的進程ID?,F在還沒有一種可移植的方法,在已知進程ID的情況下可以得到有效用戶ID。(內核在進程表項中自然地保持有這兩種值,但是除非徹底檢查內核存儲空間,否則已知一個,無法得到另一個。)
使服務器進程可以標識客戶進程:這一技術既可使用FIFO、消息隊列或信號量,也可使用共享存儲。我們假定http://www.CUOXin.com/nufangrensheng/p/3561632.html圖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版),僅作個人學習記錄所用。關于本書可參考:http://www.apuebook.com/。
新聞熱點
疑難解答