http://www.blogbus.com/eastsun-logs/7762285.html

http://www.blogbus.com/eastsun-logs/7762285.html


摘自《UNIX NETWORK PROGRAMMINGchapter 6

p144

 

 

對於常見的input操作,一般分爲兩個步驟:

1.  wait to be ready

2.  copy data from kernel buffer to user buffer

 

 

常見的I/O模型:(參見以上步驟)

1.  阻塞I/O                               用戶進程執行12

2.  非阻塞I/O                           用戶輪巡1,然後執行2

3.  多路複用selectpoll            用戶調用select等待kernel返回,然後執行2

4.  信號驅動I/OSIGIO              用戶設置信號處理函數(sigio)後,正常繼續其他函數,當kernel返回SIGIO後,執行2

5.  異步信號I/O                        kernel執行12後通知用戶進程(不常用)

 

 

# include <sys/select.h>

# include <sys/time.h>

int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);

 

 

作用:

1.  內核掃描maxfdp1個描述符(常規情況下,系統最大值FD_SETSIZE=1024,可修改)

2.  內核查看readset, writeset, exceptset集中的描述符是否準備好

3.  等待超過timeout時間而沒有描述符準備好,select返回

 

 

struct timeval {

       long tv_sec;

       long tv_usec;

}

注:timeval == 0   馬上返回

       timeval == NULL   永久阻塞

 

 

void FD_ZERO(fd_set *fdset);

void FD_SET(int fd, fd_set *fdset);

void FD_CLR(int fd, fd_set *fdset);

void FD_ISSET(int fd, fd_set *fdset);

 

 

注意,如循環調用select,多次檢查同一描述符,必須在調用select之前重新設定初始值。select函數會在每次返回時,將沒有ready的描述符所在的位清0

 

 

任何信號將使select()出錯返回。而且BSD系統的select將不可能自動再啓動

在標準select()中,返回後系統不會改變timeval的值,而linux系統例外。

 

 

exception condition

當前只支持  out-of-bandthe presence of control status information to be read from the master side of a pseudo terminal that has been put into packet mode.(???)

 

 

一般的系統實現中,將fd_set設置爲整數隊列,每個整數元素中的一位表示爲一個描述符

 

 

FD_SETSIZE定義在<sys/select.h>中,如果要更改的話,必須重新編譯內核

 

 

select()的常見錯誤:

1.  maxfdp1必須指定爲最大描述符值+1

2.  每次select返回後,都會將fd_set中的初值清0,除非該描述符已經準備好。因此,如果要重新檢查描述符,必須再次賦初值

 

 

select返回的描述符個數中,如果同一描述符同時爲讀、寫準備好,則記數2

早期的SVR4版本只記錄1次。(bug

 

 

 

 

select準備好的意義:

準備好:

1.  socket接受緩存中的數據 >= SO_RCVLOWAT,默認情況下,SO_RCVLOWAT=1

2.  對端寫關閉,read返回0

3.  listenfd中的complete queue 中,有entry

4.  socket出錯。read返回-1

 

 

準備好:

1.  socket發送緩存中的數據 >= SO_SNDLOWAT,默認情況下,SO_SNDLOWAT=2048

2.  對端讀關閉,kernel返回SIGPIPE

3.  socket出錯,write返回-1

 

 

注意,當socket出錯時,將在readset/writeset分別賦值

 

 

使用select應該注意:

由於同時處理單個描述符的讀寫,可能出現此描述符的寫(或讀)操作已全部完成,而相對的另一個讀(或寫)操作還沒有完成。爲了獲得這些數據,進程必須調用shutdown()以進行半關閉

 

 

# include <sys/socket.h>

int shutdown(int sockfd, int howto);

其中,howto可以置爲SHUT_RD/SHUT_WR/SHUTRDWR

 

 

區別於close()

1.  shutdown不查看描述符計數器。直接進行半關閉

2.  close只能進行全關閉,shutdown可以選擇一端或兩端

 

 

服務器處理客戶機請求的原則:永遠不要將服務器阻塞在一個客戶連接中。(可能被dos攻擊)

 

 

# include <sys/select.h>

# include <signal.h>

# include <time.h>

int pselect(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *except_set, const struct timespec *timeout, const sigset_t sigmask);


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