歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux管理 >> Linux網絡

linux網絡編程之socket(八) 五種I/O模型和select函數簡介

一、五種I/O模型

1、阻塞I/O

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

2、非阻塞 I/O

我們可以使用 fcntl(fd, F_SETFL, flag | O_NONBLOCK); 將套接字標志變成非阻塞,調用recv,如果設備暫時 沒有數據可讀就返回-1,同時置errno為EWOULDBLOCK(或者EAGAIN,這兩個宏定義的值相同),表示本來應該阻塞在這裡( would block,虛擬語氣),事實上並沒有阻塞而是直接返回錯誤,調用者應該試著再讀一次(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模型比較少用。

3、I/O復用

用select來管理多個I/O,當沒有數據時select阻塞,如果在超時時間內數據到來則select返回,再調用recv進行數據的 復制,recv返回後處理數據。

4、信號驅動I/O

先注冊SIGIO信號的處理函數,進程繼續執行其他操作,當數據到來時會發送SIGIO信號給進程,然後可以在信號處理函 數中調用recv進行數據的復制,然後recv返回進行數據處理。

5、異步I/O

aio_read 函數也會提供一個buf,系統調用進入內核,如果沒有數據則立即返回,進程繼續執行其他操作,所以叫異步 I/O,當數據到來時內核自動復制數據,然後推送給用戶空間,通過在aio_read中指定的信號通知進程,讓其處理數據。異 步I/O跟信號驅動I/O的不同之處在於,它不用調用recv進行數據的復制,如果將後者比做”拉pull“,則前者可以認為是” push推“,push的效率會高點,其實異步I/O跟windows下面的完成端口差不多,但aio_read的實現或多或少存在問題,用得 也比較少。

二、select函數簡介

/* According to POSIX.1-2001 */

      #include <sys/select.h>

      /* According to earlier standards */

      #include <sys/time.h>

      #include <sys/types.h>

      #include <unistd.h>

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

參數1:讀寫異常集合中的文件描述符的最大值加1;

參數2:讀集合,關心可讀 事件;

套接口緩沖區有數據可讀

連接的讀一半關閉,即接收到FIN段,讀操作將返回0

如果是監聽套接口,已完成連接隊列不 為空時。

套接口上發生了一個錯誤待處理,錯誤可以通過getsockopt指定SO_ERROR選項來獲取。

參數3:寫集合,關心可寫事件;

套接口發送緩沖區有空間容納數據。

連接的寫一半關閉。即收到RST段之後,再次調用write操作。

套接口上發生了一個錯誤待處理,錯誤可以通過getsockopt指定SO_ERROR選項來獲取。

參數4:異常集合,關心異常事件;

套接口存在帶外數據(TCP頭部 URG標志,16位緊急指針字段)

參數5:超時時間結構體

對於參數2,3,4來說,如果不關心對應事件則設置為NULL即可。注意5個參數都是輸入 輸出參數,即select返回時可能對其進行了修改,比如集合被修改以便標記哪些套接口發生了事件,時間結構體的傳出參數 是剩余的時間,如果設置為NULL表示永不超時。用select管理多個I/O,select阻塞等待,一旦其中的一個或多個I/O檢測到 我們所感興趣的事件,select函數返回,返回值為檢測到的事件個數,並且返回哪些I/O發送了事件,遍歷這些事件,進而 處理事件。注意當select阻塞返回後,此時調用read/write 是不會阻塞的,因為正是有可讀可寫事件發生才導致select 返 回,也可以認為是select 提前阻塞了。

下面是4個可以對集合進行操作的宏:

void FD_CLR(int fd, fd_set *set); // 清除出集合

int  FD_ISSET(int fd, fd_set *set); // 判斷是否在集合中

void FD_SET(int fd, fd_set *set); // 添加進集合中

void FD_ZERO(fd_set *set); // 將集合清零

select函數的舉例應用看這裡。

Copyright © Linux教程網 All Rights Reserved