天天看點

網絡程式設計中I/O複用模型

再回首一些概念

  • 阻塞IO
    • 資料沒有準備好, 讀操作就會阻塞
    • 資料不能立即被收時, 寫操作就會阻塞
    • 打開檔案時阻塞, 直到某些條件發生
  • 非阻塞IO
    • 立即傳回, 并用錯誤值來表示目前的狀态
  • 指定非阻塞方式
    • 打開時指定O_NONBLOCK 标志
    • 使用fcntl 打開或關閉非阻塞方式
  • 網絡程式設計時, 可以使用非阻塞, 用輪詢方式發送
  • 使用多線程可以避免使用非阻塞IO, 但是同步開銷較大

多路IO

  • 當程式需要同時從多個檔案描述符讀資料時
    • 使用多程序/多線程, 同步複雜, 程序線程開銷
    • 使用非阻塞IO, 交替輪詢
    • 通過信号使用異步IO, 無法判斷哪個IO完成
  • 多路IO: 把關心的IO放入一個清單/檔案描述符集合, 調用多路函數
  • 多路IO函數阻塞, 直到有一個IO資料準備好後傳回
  • 傳回後告訴調用者哪個描述符準備好了

select實作說明

  • 調用select時通過參數告訴核心使用者感興趣的IO描述符
  • 關心的IO狀态: 輸入,輸出或錯誤三種狀态
  • 調用者等待指定的時間
  • 傳回之後核心告訴調用者哪些個描述符準備好了
    • 哪些描述符發生了變化
  • 調用傳回後對準備好的描述符調用讀寫操作
  • 不關心的描述符集合傳NULL

函數詳解

  • man手冊
select() 
/* According to POSIX 1003.1-2001 */
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>    
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,  struct timeval *timeout);
           
  • 傳回值:如果成功,傳回所有sets中描述符的個數;如果逾時,傳回0;如果出錯,傳回-1。
  • 監視readfds來檢視是否read的時候會被堵塞
    注意,即便到了end-of-file,fd也是可讀的。
  • 監視writefds看寫的時候會不會被堵塞。
  • 監視exceptfd是否出現了異常。
    主要用來讀取OOB資料,異常并不是指出錯。
  • 注意當一個套接口出錯時,它會變得既可讀又可寫。
  • 如果有了狀态改變,會将其他fd清零,隻有那些發生改變了的fd保持置位,以用來訓示set中的哪一個改變了狀态。
  • 參數n是所有set裡所有fd裡,具有最大值的那個fd的值加1

與fd_set有關的函數

fd_set是一個位向量, 每位表示一個描述符

網絡程式設計中I/O複用模型
int FD_ISSET(int fd, fd_set *fdset);
//測試某個描述符是否在集合内用來訓示一個fd是不是一個set的一部分。他很有用,用來看select後哪一個fd可用了。
void FD_CLR(int fd, fd_set *fdset);
//從集合内把一個描述符移除
void FD_SET(int fd, fd_set *fdset);
//把一個描述符加入集合
void FD_ZERO(fd_set *fdset);
//清空描述符集合
           
網絡程式設計中I/O複用模型
網絡程式設計中I/O複用模型

關于time_out

  • timeout是從調用開始到select傳回前,會經曆的最大等待時間。
  • 兩種特殊情況:如果為值為0,會立刻傳回。如果timeout是NULL,會阻塞式等待。
struct timeval {
      long    tv_sec;     /* seconds */
      long    tv_usec;    /* microseconds */
  };
           
  • 一些調用使用3個空的set, n為zero, 一個非空的timeout來達到較為精确的sleep.
  • 為了較好的可移植性,timeout在循環中需要被重新賦初值。
  • timeout== NULL
    • 無限等待
    • 被信号打斷時傳回-1, errno 設定成 EINTR
  • timeout->tv_sec == 0 && timeout ->tv_usec == 0
    • 不等待立即傳回
  • timeout->tv_sec != 0 || timeout ->tv_usec != 0
    • 等待特定時間長度, 逾時傳回0

注意事項

  • 可以把同一個描述符同時放入讀和寫集合
  • 當讀和寫準備好時, 傳回值的計數分别加1次
  • 普通檔案的三種狀态總是傳回準備好的狀态
  • 是否阻塞式IO不會影響select的結果
  • 如果一個描述符到了檔案結尾,select傳回的狀态是準備好
  • 對一個準備好的描述符, 讀出長度是0表示到達結尾