6.1概述
在5.12節中,我們看到tcp客戶同時處理兩個輸入:標準輸入和tcp套介面。我們遇到的問題是客戶阻塞於(標準輸入上的)fgets呼叫,而伺服器程序又被殺死。伺服器tcp雖正確地給客戶tcp發了乙個fin,但客戶程序正阻塞於從標準輸入讀入,它直到從套介面讀時才能看到此檔案結束符(可能已經過了很長時間)。我們需要這樣的能力:如果乙個或多個i/o條件滿足(例如,輸入已準備好被讀,或者描述字可以承接更多的輸出)時,我們就被通知到。這個能力被稱為i/o復用,是由函式select和poll支援的,我們也對較新的posix.lg的變種(稱為pselect)作介紹。
i/o復用典型地用在下列網路應用場合:
i/o復用並非只限於網路程式設計,許多正式應用程式也需要使用這項技術。
6.2 i/o模型
在介紹函式select和poll之前,我們需要回過頭來看看
你在第一次閱讀時,可能想略讀到本節,到後面的章節中詳細介紹不同的i/o模型時才回來看。
正如本節我們所給出的所有例子所述,乙個輸入操作一般有兩個不同的階段:
等待資料準備好。
從核心到程序拷貝資料。
對於乙個套介面上的輸入操作,第一步一般是等待資料到達網路,當分組到達時,它被拷貝到核心中的某個緩衝區,第二步是將資料從核心緩衝區拷貝到應用緩衝區。
阻塞i/o模型
最流行的i/o模型是阻塞i/o模型,本書中到目前為止的所有例子都使用此模型。預設時,所有套介面都是阻塞的。以資料報套介面作為例子,我們有示例圖6.1中的情形。
此例中,我們用udp而不是tcp,因為對於udp來說,資料準備好的概念要簡單些:整個資料報是否已接收,而對於tcp則要複雜得多,需考慮諸如套介面的低潮限度(lowwater mark)這樣的許多附加變數。
在本節的例子中,我們將行數recvfrom視為系統呼叫,因為我們正考慮應用程序與核心的區別。不論函式recvfrom如何實現(在源自berkeley的核心中作為系統呼叫,在系統v核心中作為呼叫系統呼叫getmsg的函式)。一般都有乙個從乙個應用程序中執行到核心中執行的切換,一段時間後再跟乙個返回到應用的程序的切換。
在圖6.1中,程序呼叫recvfrom,此系統呼叫直到資料報到達且拷貝到應用緩衝區或是出錯才返回。最常見的錯誤是系統呼叫被訊號中斷,如5.9節所述。我們所說程序阻塞的整段時間是指從呼叫recvfrom開始到它返回的這段時間,當程序返回成功指示時,應用程序開始處理資料報。
非阻塞i/o模型
當我們把乙個套介面設定成非阻塞方式時,即通知核心:當請求的i/o操作非得讓程序睡眠不能完成時,不要讓程序睡眠,而應返回乙個錯誤。我們將在第15章節詳細介紹非阻塞i/o,但為了說明我們所考慮的例子,在圖6.2中作乙個小結性描述。
前三次呼叫recvfrom時仍無資料返回,因此核心立即被返回乙個 ewouldblock 錯誤,第四次呼叫recvfrom時,資料報已準備好,被拷貝到應用緩衝區,recvfrom返回成功指示,接著就是我們處理資料。
當乙個應用程序像這樣對乙個非阻塞描述字迴圈呼叫recvfrom時,我們稱此過程為輪詢(polling)。應用程序連續不斷地查詢核心,看看某操作是否準備好,這對cpu時間是極大的浪費,但這種模式指示偶爾才遇到,一般只是專門提供某種功能的系統中才有。
i/o復用模型
有了i/o復用,我們就可以呼叫select或poll,在這兩個系統呼叫中的某乙個上阻塞,而不是阻塞於真正的i/o系統呼叫。圖6.3是i/o復用模型的乙個小結。
我們阻塞於select呼叫,等待資料報套介面可讀。當select返回套介面可讀條件時,我們呼叫recvfrom將資料報拷貝到應用緩衝區中。
將圖6.3與圖6.1進行比較,似乎沒有顯示什麼優越性,實際上,因使用了系統呼叫select,要求兩次系統呼叫不是一次,好像變得還有點差。但是,在本章的後面我們將看到,使用select的好處在於我們可以等待多個描述字準備好。
訊號驅動i/o模型
我們也可以用訊號,讓核心在描述字準備好時用訊號sigio通知我們,我們將此方法稱為訊號驅動i/o,圖6.4對此作了乙個小結。
首先,我們允許套介面進行訊號驅動i/o(我們將在22.2節對此進行討論),並通過系統呼叫sigaction安裝乙個訊號處理程式。此系統呼叫立即返回,程序繼續工作,它是非阻塞的。當資料報準備好被讀時,就為該程序生成乙個sigio訊號。我們隨即可以在訊號處理程式中呼叫recvfrom來讀資料報,並通知主迴圈資料已準備好被處理(這正是我們在22.3節中所要做的事情)。也可以通知主迴圈,讓它來讀取資料報。
無論我們如何處理sigio訊號,這種模式的好處是當等待資料報到達時,可以不阻塞。主迴圈可以繼續執行,只是等待訊號處理程式的通知;或者資料已準備好被處理,或者資料報已準備好被讀。
非同步i/o模型
非同步i/o是posix.1的1993版本中的新內容("實時"擴充套件)。我們讓核心啟動操作,並在整個操作完成後(包括資料從核心拷貝到我們自己的緩衝區)通知我們。因為這種模型還沒有廣泛使用,本書不做討論。這種模型與前一節介紹的訊號驅動模型的主要區別在於:訊號驅動i/o是由核心通知我們何時可以啟動乙個i/o操作,而非同步是由核心通知我們i/o操作何時完成。圖6.5給出乙個例子。
我們呼叫函式aio_read(posix 非同步i/o函式以 aio_ 或 lio_ 開頭),給核心傳遞描述字,緩衝區指標、緩衝區大小(與read相同的三個引數)、檔案偏移(與lseek類似),並告訴核心當整個操作完成時如何通知我們。此系統呼叫立即返回,我們的程序不阻塞與等待i/o操作的完成。在此例子中,我們假設要求核心在操作完成時生成乙個訊號,此訊號知道資料已拷貝到應用緩衝區才產生,這一點是與訊號驅動i/o模型不同的。
正如本書說述,很少有系統支援posix.1的非同步i/o模型。例如,我們還不能確定系統是否支援套介面上的這種模型。這兒我們用它,只是作為乙個與訊號驅動i/o模型進行比較的例子。各種i/o模型的比較
圖6.6示出了上述五種不同i/o模型的比較。它表明:前四種模型的主要區別都在第一階段,因為前四種魔性的第二階段基本相同:在資料從核心拷貝到呼叫者的緩衝區時,程序阻塞於recvfrom呼叫。然後,非同步i/o模型處理的兩個階段都不用於前四個模型。
同步i/o與非同步i/o
posix.1定義這兩個術語如下:根據上述定義,我們的前四個模型————阻塞i/o模型、i/o復用模型和訊號驅動i/o模型都是同步i/o模型,因為真正的i/o操作(recvfrom)阻塞程序,只有非同步i/o模型與此非同步i/o的定義相匹配。
UNIX網路程式設計卷1 第1章 簡介
要編寫通過計算機網路通訊的程式,首先要確定這些程式相互通訊所用的協議。一般認為web伺服器程式是乙個長時間執行的程式 守護程式,daemon 它只在響應來自網路的請求時才傳送網路訊息。協議的另一端是web客戶程式,如某種瀏覽器,與伺服器程序的通訊總是由客戶程序發起。在設計網路應用時,確定總是由客戶發...
Unix 網路程式設計卷1
伺服器被動開啟 passive open socket bind listen。客戶端通過socket connect主動開啟 active open accept和connect是阻塞的 把目標位元組串指定數目的位元組置為c void memset void dest,int c,size t l...
UNIX環境高階程式設計 第2版 pdf格式
unix環境高階程式設計 第2版 本書描述unix系統的程式設計介面 系統呼叫介面和標準c庫提供的很多函式。本 書針對的是所有的程式設計師。與大多數作業系統一樣,unix為程式執行提供了大量的服務 開啟檔案,讀檔案,啟 動乙個新程式,分配儲存區以廈獲得當前時間等。這些服務被稱為系統呼叫介面 syst...