IO 多路復(fù)用 select vs pool vs epool

IO 多路復(fù)用 select vs pool vs epool

前言

  • 在類 unix linux 系統(tǒng)中每一個(gè)進(jìn)程都存在一個(gè)文件描述符表,表指向具體的文件,比如 socket, 設(shè)備(devices), 和其他操作系統(tǒng)對(duì)象。

IO多路復(fù)用(IO Multiplexing )出現(xiàn)的背景

  • 多個(gè)io資源協(xié)同工作的系統(tǒng),具有典型的兩個(gè)階段,初始化階段,然后進(jìn)入等待模式,等待客戶端發(fā)起請(qǐng)求并對(duì)其進(jìn)行相應(yīng)。
  • 簡(jiǎn)單的實(shí)現(xiàn)是對(duì)每一個(gè)客戶端socket 請(qǐng)求創(chuàng)建一個(gè)thread 線程,在read 的時(shí)候block 阻塞,直至一個(gè)請(qǐng)求被發(fā)送并且接受到一個(gè)寫入(write)的響應(yīng)。
  • 這種工作模式在客戶端client 比較少的時(shí)候是ok 的, 但是如果在大規(guī)模的客戶端請(qǐng)求調(diào)度中,為每個(gè)client 都創(chuàng)建一個(gè)線程是非常不好的。

io 多路復(fù)用應(yīng)運(yùn)而生。

IO Multiplexing 模式的支持

實(shí)現(xiàn)思想是: 采用內(nèi)核機(jī)制來(lái)輪詢一組文件描述符,在linux 中有以下三種支持。

它們的實(shí)現(xiàn)思路一樣,都是創(chuàng)建一組標(biāo)志讀(read) 寫(write)的文件描述符,并告知內(nèi)核,并用一個(gè)線程阻塞一個(gè)函數(shù)調(diào)用,直到一個(gè)文件描述符的操作(read/write)可用。

  • select()
  • pool()
  • epool()

select() 系統(tǒng)調(diào)用

select () 指令

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

對(duì)一個(gè)select 指令的調(diào)用會(huì)被阻塞,直到給定的文件描述符執(zhí)行IO 操作,或則在指定的過(guò)期時(shí)間內(nèi)失效,被監(jiān)視的文件描述符分為三組。

  • readfds: 監(jiān)視 readfds集中列出的文件描述符,以查看是否有數(shù)據(jù)可讀取。
  • writefds: 監(jiān)視 writefds 集中列出的文件描述符,已查看是否寫入操作完成且沒有發(fā)生阻塞。
  • exceptfds : 監(jiān)視是否出現(xiàn)異常,或則帶外數(shù)據(jù)(OOB)可用。

select 沒有監(jiān)察到上述事件就會(huì)返回null。成功返回則修改每個(gè)集合。使其僅包含已經(jīng)準(zhǔn)備就緒的文件描述符。

因?yàn)樾枰嬷?select() 最大的文件描述符編號(hào),這是fd_sets 的內(nèi)部實(shí)現(xiàn)。

【在fd_set 中 一個(gè)fd(file description) 文件描述占用 1bit, fd_set 是length 為32 的整數(shù)數(shù)組,(32 x 4byte x 8bit = 1024bit) ,假設(shè)存在 8個(gè)文件描述符,最大的文件描述符值為1000, 那么將會(huì)在0 ~ 1000 中找到監(jiān)視的文件描述符】

這也是一個(gè)弊端,因?yàn)樾枰诿看屋喸兊鷷r(shí)重新構(gòu)建文件描述符集。

select () 總結(jié)

  • 在每次調(diào)用前需要重新構(gòu)建每個(gè)fd_set 集合。
  • 函數(shù)調(diào)用監(jiān)視文件描述符最大值N 范圍內(nèi)的任何位,時(shí)間復(fù)雜度為 O(N)。
  • 需要輪詢遍歷文件描述符集,檢查是否存在于從 select() 返回的集合中。
  • select 最大的優(yōu)勢(shì)是輕便,可移植,任何unix 系統(tǒng)都支持。

pool() 系統(tǒng)調(diào)用

int poll (struct pollfd *fds, unsigned int nfds, int timeout);

與select () 采用3個(gè) 基于標(biāo)記位的文件描述符集不同,pool 采用一個(gè)pollfd 結(jié)構(gòu),原型更簡(jiǎn)單。

struct pollfd {
      int fd;
      short events; 
      short revents;
};

為每一個(gè)文件描述符構(gòu)建一個(gè) pollfd 類型的兌對(duì)象,并填充請(qǐng)求(request)事件,然后輪詢返回響應(yīng),檢查返回事件(revents)字段。

與selelct() 類似,需要檢測(cè)每一個(gè)pollfd 對(duì)象去看它的文件描述符是否已準(zhǔn)備好,,但是不需要在每次輪詢時(shí)重新構(gòu)建文件描述符集。

epool() 系統(tǒng)調(diào)用

pool() ,select() 工作時(shí),我們需要在用戶空間管理任何事情,在每次調(diào)用發(fā)送 文件描述符集時(shí)陷入阻塞等待。添加另外的socket , 我們需要把它加入到 集合中并再一次調(diào)用 pool() select()。

epool() 幫助我們?cè)趦?nèi)核中創(chuàng)建和管理上下文,可以分為以下三步:

  • 調(diào)用 epool_create 在內(nèi)核中創(chuàng)建上下文。
  • 調(diào)用epool_ctl 在上線文中添加或刪除文件描述符。
  • 采用epool_wait 等待上下文中的事件。

pool() vs select()

  • pool() 不需要用戶去計(jì)算文件描述符最大數(shù)+1。
  • pool() 對(duì)于很大數(shù)值的文件描述符更有效率。
  • select() 的文件描述符集是固定的大小。
  • 采用select() 系統(tǒng)調(diào)用,文件描述符集在返回后被重新構(gòu)造,因此接下來(lái)的調(diào)用必須重新實(shí)例化,pool() 將輸入 (event 字段)和 輸出 (output )分開,從而允許文件描述符集可以重用而無(wú)需改動(dòng)。
  • select() 更輕便, 一些unix 系統(tǒng)不支持 pool()。

epool vs pool vs select

  • 在等待io 響應(yīng)的時(shí)候可以添加或刪除文件描述符。
  • epool_wait 僅返回具有已經(jīng)準(zhǔn)備的文件描述符的對(duì)象。
  • epool 擁有更高的性能,時(shí)間復(fù)雜度為 O(1)。
  • epool 可以表現(xiàn)為水平觸發(fā),和邊緣觸發(fā)。
  • epool 是linux 特有的,不具有很好的可移植性。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容