UNIX網路程式設計6 IO復用

2021-09-04 05:28:04 字數 4569 閱讀 3587

i/o多路復用是指核心一旦發現程序指定的乙個或者多個i/o條件準備就緒,它就通知該程序。i/o復用適用於以下場合:

當客戶處理多個描述符(一般是互動式輸入或網路套接字),必須適用i/o復用(套接字是乙個抽象出來的概念,本質上也是乙個檔案描述符)

當乙個客戶處理多個套接字時,這種情況很少見,但也可能出現

當乙個tcp伺服器既要處理監聽套接字,又要處理已連線套接字,一般就要使用i/o復用

如果乙個伺服器既要適用tcp,又要適用udp,一般就要使用i/o復用

如果乙個伺服器要處理多個服務或者多個協議,一般就要使用i/o復用

與多執行緒和多程序技術相比,i/o復用技術的最大優勢就是系統開銷小,系統不必建立程序/執行緒,也不必維護這些程序/程序,從而大大減小了系統的開銷。

unix下常見的i/o模型有五種,分別是:阻塞式i/o,非阻塞式i/o,i/o復用,訊號驅動式i/o和非同步i/o。

unix下對於乙個輸入操作,通常包含兩個不同的階段

1、等待資料準備好

2、從核心向程序複製資料

例如:對於一次read函式操作來說,資料先會被拷貝到作業系統核心的緩衝區去,然後才會從作業系統核心的緩衝區拷貝到應用程式的位址空間。

再比如對於一次socket流傳輸來說,首先等待網路上的資料到達,然後複製到核心的某個緩衝區,然後再把核心緩衝區的資料複製到程序緩衝區。

預設情形下,所使用的套接字模型都是阻塞式i/o模型。

圖中示例是以udp為例子,udp較為簡單,要麼資料報到了,要麼沒有。tcp則需要額外考慮一些其他的變數,關注資料報是否完全到了等,比較複雜。

該例子中,recvfrom為系統呼叫。程序呼叫recvfrom,其系統呼叫直到資料報到達且被複製到應用程序的緩衝區才返回成功(遇到錯誤返回錯誤)。 程序從呼叫recvfrom到返回指示這段時間內處於阻塞狀態,該種io方式稱為阻塞式io模型。

優點:能夠及時獲得資料,沒有延遲,

缺點:對使用者來說,這段時間一直要處於等到狀態,不能去做其他的事情,在效能方面付出了代價。

程序把套接字設定成非阻塞是在通知核心:當所請求的i/o操作需要把程序投入阻塞時,不要處理,而是返回乙個錯誤。

如圖所示,程序前三次呼叫recvfrom時,資料報沒有準備好,核心返回ewouldblock的錯誤,直到準備好資料報,才返回成功指示。

優點在於:應用程序不必阻塞在recvfrom呼叫中,而是可以去處理其他事情

缺點在於:在網路模型中即可以表現在任務完成的響應延遲增大了,隔一段時間輪詢一次recvform,資料報可能在兩次輪詢之間的任意時間內準備好,這將會導致整體資料吞吐量的降低。

有了i/o復用模型,可以呼叫select 和 poll,阻塞在這兩個系統呼叫中的某乙個之上,而不是阻塞在真正的i/o系統呼叫上。

如上圖所示,程序受阻於select呼叫,等待可能多個套接字中的任乙個變為可讀。當select返回套接字可讀這一條件時,應用程序就呼叫recvfrom把所讀的資料報複製到應用程序緩衝區。

程序阻塞在select,如果程序還有其他的任務的話就能體現到i/o復用技術的好處,那個任務先返回可讀條件,就去執行哪個任務。從單一的等待變成多個任務的同時等待。

這種模型較之前的模型來說,可以不必多次輪詢核心,而是等到核心的通知。

核心在就緒後傳送sigio訊號通知程式,稱為訊號驅動式的i/o模型。

如上圖所示,程序建立sigio的訊號處理程式,並通過sigaction系統呼叫安裝乙個訊號處理函式,該系統呼叫將立即返回,程序繼續工作,知道資料報準備好後,核心產生乙個sigio訊號,告知應用程序以及準備好,於是就在訊號處理程式中呼叫recvfrom讀取資料報,並通知主迴圈資料已準備好待處理,也可以立即通知主迴圈讓他讀取資料報。

這種模型的好處就是,在資料報沒有準備好的期間,應用程序不必阻塞,繼續執行主迴圈,只要等待來自訊號處理函式的通知即可。

告知核心啟動某個操作,並讓核心在整個操作(包括將資料從核心複製到我們自己的緩衝區)完成後通知我們。這種模型與前一節介紹的訊號驅動模型的主要區別在於:訊號驅動i/o是由核心通知我們如何啟動乙個i/o操作,而非同步i/o模型是由核心通知我們i/o操作何時完成。

不同於訊號驅動式i/o模型,訊號是在資料已複製到程序緩衝區才產生的。

各種i/o模型的比較 以一張圖來說明五種i/o操作的差異:

同步i/o操作:導致請求程序阻塞,直到i/o操作完成

非同步i/o操作:不導致程序阻塞

可知,前四種都屬於同步i/o操作慢系統都會阻塞與recvfrom操作,而非同步i/o不會。

select函式用於i/o復用,該函式允許程序指示核心等待多個事件中的任何乙個發生,並只在有乙個或多個事件發生或經歷一段指定的事件才喚醒它。

即,我們呼叫select函式告知核心對哪些描述符感興趣及等待多長時間,

#include

#include

intselect

(int maxfdp1, fd_set *readset, fd_set *writeset , fd_set *exceptset ,

const

struct timeval *timeout)

;//若有就緒描述符則返回其數目,超時則為0,若出錯則為-1

對於timeout引數:

(1) timeout==null,表示要永遠等待下去,直到有乙個描述符準備好i/o時才返回

(2) *timeout的值為0,表示不等待,檢查描述符就立即返回,這稱為輪詢。

(2) *timeout的值不為0,表示等待一段固定的時間,再有乙個描述符準備好i/o時返回,但是不能超過由該引數制定的時間。

對於readset,writeset和exceptset三個引數:

這三個描述符說明了可讀,可寫和處於異常條件的描述符集合

對於描述集fd_set結構,提供了如下四個操作函式

#include

int fd_isset(int fd,fd_set *fdset); //設定描述集中的某個描述符

void fd_clr(int fd,fd_set *fdset);//關掉描述集中的某個描述符

void fd_set(int fd,fd_set *fdset);//開啟描述集中的某個描述符

void fd_zero(fd_set *fdset);//清除集合內所有元素

對於maxfdp1引數:

指定待測試的描述符個數,它的值時待測試的最大描述符編號加1,即從上面三個描述符集中的最大描述符編號加1。

對於返回值:

select返回值有三種情況:

(1) 返回值為-1時,表示出錯,如果在指定的描述符乙個都沒有準備好時捕捉乙個訊號,則返回-1

2) 返回0,表示沒有描述符準備好,指定的時間就超過了

(3) 返回正數,表示已經準備好的描述符個數,在這種情況下,三個描述符集中依舊開啟的位對應於已準備好的描述符

#include

"unp.h"

void

str_cli

(file *fp,

int sockfd)

//如果標準輸入可讀,就先用fgets讀入一行文字if(

fd_isset

(fileno

(fp)

,&rset))}

}

在上節的程式中,存在問題,假設客戶在標準輸入中批量輸入資料,在輸入完最後乙個資料後,碰到了eof,str_cli返回到main函式,main函式隨後終止。但是,在這個過程中,標準輸入的eof終止符並不意味著我們也同時完成了從套接字的讀入,可能仍有請求在去往伺服器的路上,或者仍有應答在返回客戶的路上。

原因為

if (fgets(sendline, maxline, fp) == null)

return; /* all done */

當碰到eof終止符的時候,str_cli函式選擇了立即返回,而此時,我們更需要的是找到乙個條件來判斷套接字的讀取是否完成。

可使用shutdown函式解決問題,

poll函式的功能與select相似,不過在處理流裝置時,它能夠提供額外的資訊。

#include

intpoll

(struct pollfd *fdarray,nfds_t nfds,

int timeout)

;//若有就緒描述符就返回其數目,如超時則返回0,若出錯就返回-1

UNIX網路程式設計 I O多路復用

目錄 unix下可用的5種i o模型 阻塞式i o模型 非阻塞式i o模型 i o復用模型 訊號驅動式i o模型 非同步i o模型 各種i o模型的比較 參考 阻塞式i o 非阻塞式i o i o復用 訊號驅動式i o sigio 非同步i o poxis的aio 系列函式 比如乙個輸入操作通常包含...

unix下網路程式設計之I O復用(五)

本章節是用基本的linux unix基本函式加上select呼叫編寫乙個完整的伺服器和客戶端例子,可在linux ubuntu 和unix freebsd 上執行,客戶端和服務端的功能如下 客戶端從標準輸入讀入一行,傳送到服務端 服務端從網路讀取一行,然後輸出到客戶端 客戶端收到服務端的響應,輸出這...

unix下網路程式設計之I O復用(二)

select函式 該函式允許程序指示核心等待多個事件中的任何乙個發生,並僅在有乙個或是多個事件發生或經歷一段指定的時間後才喚醒它。我們呼叫select告知核心對哪些描述字 就讀 寫或異常條件 感興趣以及等待多長時間。我們感興趣的描述字不侷限於套介面,任何描述字都可以使用select來測試。selec...