linux select()詳解(一)-- 使用及注意事項
通過本文你會了解到:
1. select()原型及引數說明
2. select()應用情景
3. select()注意事項
4. select()作定時器
原型
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *utimeout);
引數說明
readfds
, writefds
, exceptfds
為所要監聽的三個描述符集:
——readfds
監聽檔案描述符是否可讀,不監聽可以傳入 NULL
——
writefds
監聽檔案描述符是否可寫 ,不監聽可以傳入 NULL
——
exceptfds
監聽檔案描述符是否有異常,不監聽可以傳入 NULL
nfds
是 select()
監聽的三個描述符集中描述符的最大值+1 timeout
設定超時時間 更詳細資訊請參考譯文linux-select()
應用情景
select()
函式的重點在於它可以同時監控多個描述符(一般最大為1024),並且在描述符集中沒有可操作的描述符時會進入睡眠狀態。 實際應用中,若需要同時處理多個描述符的讀寫時,如果只是建立了一系列的read()
和write()
就會導致在有些描述符沒有準備好讀寫時而被阻塞,這樣當然不是我們期望的,因此這時就需要應用select()
注意事項
這段是select()
使用必須要了解和掌握的知識點,建議認真閱讀,同時可以結合後續的一些例項做分析,相信你一定能掌握select()
使用方法。
nfds
必須被正確設定,一般取描述符集中描述符的最大值並加1。在非必須的情況下,儘量使用不超時的
select()
,即將utimeout
引數設定為NULL
。/*引數 timeout 置為 NULL*/ select(nfds, &readfds, &writefds, &exceptfds, NULL);
timeout
的值必須在每次select()
之前重新賦值,因為作業系統會修改此值。while
由於
select()
會修改字符集,因此如果select()
呼叫是在一個迴圈中,則描述符集必須被重新賦值。/*以read操作為例*/ while(1) { FD_ZERO(&readfds); FD_SET(fd, &readfds); select(nfds, &readfds, NULL, NULL, NULL); }
函式
read()
,write()
,recv()
,send()
以及select()
可能會返回-1並且errno置位為EINTR,或這errno被賦值為EAGAIN(EWOULDBLOCK),這種情況需要被正確處理。如果程式中不接收任何訊號,則不會得到EINTR。如果程式設為阻塞I/O,則不會收到EAGAIN。/*一般只需對EINTR進行處理就可以了,例子如下*/ while(1) { ret = select(nfds, &readfds, NULL, NULL, NULL); if(ret == -1 && errno == EINTR) continue; }
- 當
read()
,write()
,recv()
和send()
返回0時建議關閉描述符並在字符集中移除此描述符(不關閉描述符並移除的話可能會導致未知錯誤,還是對此情況處理的好)。
定時器
在沒有usleep函式的系統中,可以應用select來實現,下例中實現了0.2秒的延時:
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 200000; /* 0.2 秒*/
select(0, NULL, NULL, NULL, &tv);