對select函數的理解

1. 處理多個socket連接的方法

阻塞模式下服務端要解決多個客戶連接的問題的3個思路:linux

  1. 每一個客戶端的socket對應一個內核線程,在這個線程內部進行阻塞的read
  2. 單線程,本身記錄一個socket列表,循環去內核中查詢socket是否ready
  3. 單線程,系統提供一個ready狀態的socket列表,主線程從這個列表中處理socket

思路1,若是連接不少(C10k)線程就會不少,消耗系統資源,並增長調度成本(Java BIO)。數組

思路2,每次都要遍歷一邊全部socket,連接不少時效率低,可能大部分連接都沒數據(select)。socket

思路3,比較理想(epoll)。函數

2. select函數

2.1. 使用方法

函數原型:性能

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

select() 檢查 readfds, writefds 中的 io描符是否可讀、可寫了,若是有ready狀態的,函數就返回。
nfds是總共fd的個數,而不是最大的fd。線程

使用select函數步驟:指針

  1. 初始化 fd_set,把要監控的fd仍進去
  2. 調用select,阻塞
  3. 阻塞結束後,遍歷查看fd_set中的ready的socket

fd_set 是什麼?
一個結構體:rest

typedef    struct fd_set {
    __int32_t    fds_bits[__DARWIN_howmany(__DARWIN_FD_SETSIZE, __DARWIN_NFDBITS)];
} fd_set;

結構體中有一個數組,默認是是1024,這就是linux中select的函數限制最大連接數的緣由。
從新編譯內核才能提升這個數字。
FD_ISSET 就是取對應位置狀態值(0,1),而且在用戶空間,
用戶須要遍歷編一遍這個數組來檢查是哪一個socket有數據。code

select的內部實現:隊列

  1. readfds 從用戶空間傳遞到內核空間
  2. 將當前進程加入到 readfds 中的每一個socket的等待隊列
  3. 當socket來數據了就把 線程喚醒(移出等待隊列)
  4. 把有數據的fds 從內核空間搞到用戶空間
  5. 用戶空間看一遍fds,知道哪一個socket有數據了,而後read、accept

select的問題:

  1. 每次調用select就要把readfds 傳到內核,wake的時候再拿回來須要傳遞1024 * 4 bytes
  2. 每次須要把當前線程加入到全部socket的等待隊列
  3. 每次須要遍歷一遍readfds來查看那個socket有數據

每次調用select都會有以上兩次傳遞和兩次遍歷,當連接個數多時,性能降低比較快:

select可能的改進:

  1. readfds一直都在內核中維護,不要每次都送進來
  2. 能夠動態單個變動內核中的readfds
  3. 就緒列表,傳給內核一個指針,內核把這個指針指向就緒的sockets (避免來回傳遞全部的socket)
相關文章
相關標籤/搜索