I/O的多路轉接: select

1. 概念:

     I/O多路轉接技術, 是先構造一張有關描述符的列表, 然後調用一個函數, 直到這些描述符中的一個已準備好進行I/O時, 該函數才返回. 在返回時, 它告訴進程哪些描述符已經準備好可以進行I/O.

    在Linux下poll, pselect, select這三個函數都可以執行I/O多路轉接操作, 今天我們主要說select函數.

 

2. select函數:

#include <sys/select.h>

int select(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds,
               
struct timeval *restrict tvptr);

正常則返回準備就緒的描述符數, 超時則返回0, 出錯則返回
-1.

下面對參數進行說明:

  • maxfdp1: MAX File Description Plus 1. 最大描述符+1.
  • tvptr: 等待時間, 用一個timeval結構的值表示, 有3種情況:
    • tvptr == NULL: 永遠等待. 可被信號中斷.
    • tvptr->tv_sec == 0 && tvptr->tv_usec == 0: 完全不等待, 測試所有指定的描述符並立即返回. 非阻塞輪詢.
    • tvptr->tv_sec != 0 &7 tvptr->tv_usec != 0: 等待指定的時間數. 時間超過立即返回0. 可被信號中斷.
  • readfds, writefds, exceptfds: 指向描述符集的指針, 分別是可讀, 可寫, 異常的描述符. 每個描述符集存放在一個fd_set數據類型中.

 

fd_set可進行以下3種處理:

  • 分配一個這種類型的變量.
  • 將這種類型的一個變量賦值給同類型的另一個變量.
  • 其他處理.

關於其他處理, 由以下幾個宏來實現:

#include <sys/select.h>

/* 判斷fd是否在描述符集fdset中 */
int FD_ISSET(int fd, fd_set *fdset);

在則返回非0, 不在則返回0.

 

#include <sys/select.h>

/* 將一指定位清楚 */
void FD_CLR(int fd, fd_set *fdset);

/* 設置fd_set變量的一個指定位爲fd */
void FD_SET(int fd, fd_set *fdset);

/* 將fdset的所有位設置爲0 */
void FD_ZERO(fd_set *fdset);

 

3. 說明:

    前面我們所說的"準備好", 具體地說是指:

  • 若對讀集(readfds)中的一個描述符的read操作將不會阻塞, 則此描述符是準備好的.
  • 若對寫集(writefds)中的一個描述符的write操作將不會阻塞, 則此描述符是準備好的.
  • 若異常狀態集(exceptfds)中的一個描述符有一個未決異常狀態, 則此描述符是準備好的. 現在, 異常狀態包括:
    • 在網絡連接上到達的帶外數據.
    • 在處於數據包模式的僞終端上發生了某些狀態.

    描述符的阻塞是否並不影響select的阻塞. 也就是說, 這是兩種阻塞層關係, 比如: 讀一個非阻塞描述符, 並且以超時值5s調用select, 則select最多阻塞5s. 又比如: 指定一個tvptr = NULL 來調用select, 則該描述符數據準備好或捕捉到一個信號之前, select是一直阻塞的.

 

4. pselect:

    POSIX.1 定義了一個select的變體, 即: pselect, 對於這個函數不多介紹, 只說一下和select的兩點區別:

  • 超時結構, select用timeval, pselect用const timespec. 這使得時間更精確, 並且不會被修改.
  • pselect可使用一可選擇的信號屏蔽字sigmask. 在調用pselect時, 以原子操作的方式安裝該信號屏蔽字, 在返回時恢復以前的信號屏蔽字.

 

5. 實例:

    編寫中, 等待添加...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章