歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Unix知識 >> Unix基礎知識

UNIX網絡編程:I/O復用:select和poll函數

我們看到TCP客戶同時處理兩個輸入:標准輸入和TCP套接字。我們遇到的問題是就在客戶阻塞於(標准輸入上)fgets調用,服務器進程會被殺死。服務器TCP雖然正確的給客戶TCP發送了一個FIN,但是既然客戶進程正阻塞於從標准輸入讀入的過程,它將看不到這個EOF,直到從套接字讀時為止(可能額已過了很長時間)。這樣的進程需要一種預先告知內核的能力,使得內核一旦發現進程指定的一個或多個I/O條件就緒(也就是說輸入已准備好被讀入,或者描述符已能承接更多的輸出),它就通知進程。這個能力稱為I/O復用,是由select和poll這兩個函數支持的。

I/O復用典型使用在下列網絡應用場合:

當客戶處理多個描述符(通常是交互式輸入和網絡套接字)時,必須使用I/O復用;

如果一個TCP服務器即要處理監聽套接字,又要處理已連接套接字,一般使用I/O復用;

一個客戶同時處理多個套接字時可能的,不過比較少見;

如果一個服務器即要處理TCP,又要處理UDP,一般就要使用I/O復用。

如果一個服務器要處理多個服務或者多個協議,一般就要使用I/O復用。

I/O復用並非只限於網絡編程,許多重要的應用程序也需要使用這項技術。

UNIX下可用的5種I/O模型的基本區別:

1.阻塞式I/O;

2.非阻塞式I/O;

3.I/O復用(select和poll);

4.信號驅動式I/O(SIGIO);

5.異步I/O(POSIX的aio_系列函數)

一個輸入操作通常包括兩個不同的階段:

(1)等待數據准備好;

(2)從內核向進城復制數據。

對於一個套接字上的輸入操作,第一步通常涉及等待數據從網絡中到達。當所等待分組到達時,它被復制到內核中的某個緩沖區。第二步就是把數據從內核緩沖區復制到應用進程緩沖區。

1.阻塞式I/O模型

我們在前面所說的I/O模型都是阻塞I/O,即調用recv系統調用,如果沒有數據則阻塞等待,當數據到來則將數據從內核空間(套接口緩沖區)拷貝到用戶空間(recv函數提供的buf),然後recv返回,進行數據處理。

查看本欄目更多精彩內容:http://www.bianceng.cn/OS/unix/

2.非阻塞式I/O模型

進程把一個套接字設置成非阻塞。前三次調用recvfrom時沒有數據可返回,因此內核轉而立即返回一個EWOULDBLOCK錯誤。第四次調用recvfrom時已有一個數據報准備好,它被復制到應用進程緩沖區,於是recvfrom成功返回。我們接著處理數據。

當一個應用進程像這樣對一個非阻塞描述符循環調用recvfrom時,我們稱之為輪詢,應用進程持續輪詢內核,以查看某個操作是否就緒,這麼做往往耗費大量的CPU 時間。

 

我們可以使用 fcntl(fd, F_SETFL, flag | O_NONBLOCK); 將套接字標志變成非阻塞,調用recv,如果設備暫時沒有數據可讀就返回-1,同時置errno為EWOULDBLOCK(或者EAGAIN,這兩個宏定義的值相同),表示本來應該阻塞在這裡,事實上並沒有阻塞而是直接返回錯誤,調用者應該試著再讀一次(again)。這種行為方式稱為輪詢(Poll),調用者只是查詢一下,而不是阻塞在這裡死等,這樣可以同時監視多個設備:

while(1)

{

非阻塞read(設備1);

if(設備1有數據到達)

處理數據;

非阻塞read(設備2);

if(設備2有數據到達)

處理數據;

..............................

}

如果read(設備1)是阻塞的,那麼只要設備1沒有數據到達就會一直阻塞在設備1的read調用上,即使設備2有數據到達也不能處理,使用非阻塞I/O就可以避免設備2得不到及時處理。

非阻塞I/O有一個缺點,如果所有設備都一直沒有數據到達,調用者需要反復查詢做無用功,如果阻塞在那裡,操作系統可以調度別的進程執行,就不會做無用功了,在實際應用中非阻塞I/O模型比較少用。

查看本欄目更多精彩內容:http://www.bianceng.cn/OS/unix/

3.I/O復用模型

我們調用select或poll阻塞在這兩個系統調用中的某一個之上,而不是阻塞在真正的I/O系統調用上。

我們阻塞於select調用,等待數據報套接字變為可讀。當select返回套接字可讀這一條件時,我們調用recvfrom把所讀數據報復制到應用進程緩沖區。

4.信號驅動I/O模型

我們可以用信號,讓內核在描述符就緒時發送SIGIO信號通知我們,我們稱這種模型為信號驅動式I/O。

我們首先開啟套接字的信號驅動式I/O功能,並通過sigaction系統調用安裝一個信號處理函數。該系統調用將立即返回,我們的進程繼續工作,也就是說它沒有被阻塞。當數據報准備好讀取時,內核就為進程產生一個SIGIO信號。我們隨後即可以在信號處理函數中調用recvfrom讀取數據報,並通知主循環數據已准備好待處理,也可以立即通知主循環,讓它讀取數據報。

查看本欄目更多精彩內容:http://www.bianceng.cn/OS/unix/

5.異步I/O模型

一般來說,這些函數的工作機制是:告知內核啟動某個操作,並讓內核在整個操作(包括將數據從內核復制到我們自己的緩沖區)完成後通知我們。這種模型與信號驅動式模型的主要區別是:信號驅動式I/O是由內核通知我們何時啟動一個I/O操作,而異步I/O模型是由內核通知我們I/O操作何時完成。

我們調用aio_read函數(POSIX異步I/O函數以aio_或lio_開頭),給內核傳遞描述符,緩沖區指針,緩沖區大小(read相同的三個參數)和文件偏移(與lseek類似),並告訴內核當整個操作完成時如何通知我們。該系統調用立即返回,而且在等待I/O完成期間,我們的進程不被阻塞。

各種I/O模型的比較:

前四種模型的主要區別在於第一階段,因為他們的第二階段是一樣的:在數據從內核復制到調用者的緩沖區期間,進程阻塞於recvfrom調用。相反,異步I/O模型在這兩個都要處理,從而不同於其他4種模型。

同步I/O和異步I/O對比:

POSIX把這兩個術語定義為:

同步I/O操作:導致請求進程阻塞,直到I/O操作完成;

異步I/O操作:不導致請求進程阻塞。

根據上述定義,我們的前4種模型--------------阻塞式I/O模型,非阻塞式I/O模型,I/O復用模型和信號驅動式I/O模型都是同步I/O模型,因為其中真正的I/O操作(recvfrom)將阻塞進程。只有異步I/O模型與POSIX定義的異步I/O相匹配。

作者:csdn博客 ctthuangcheng

Copyright © Linux教程網 All Rights Reserved