為什麼 redis 中要使用 i/o 多路復用這種技術呢?
首先,redis 是跑在單執行緒中的,所有的操作都是按照順序線性執行的,但是由於讀寫操作等待使用者輸入或輸出都是阻塞的,所以 i/o 操作在一般情況下往往不能直接返回,
這會導致某一檔案的 i/o 阻塞導致整個程序無法對其它客戶提供服務,而 i/o 多路復用就是為了解決這個問題而出現的。
先來看一下傳統的阻塞 i/o 模型到底是如何工作的:
當使用 read 或者 write 對某乙個檔案描述符(file descriptor 以下簡稱 fd)進行讀寫時,如果當前 fd 不可讀或不可寫,整個 redis 服務就不會對其它的操作作出響應,導致整個服務不可用。
這也就是傳統意義上的,也就是我們在程式設計中使用最多的阻塞模型:
阻塞模型雖然開發中非常常見也非常易於理解,但是由於它會影響其他 fd 對應的服務,所以在需要處理多個客戶端任務的時候,往往都不會使用阻塞模型。
阻塞式的 i/o 模型並不能滿足這裡的需求,我們需要一種效率更高的 i/o 模型來支撐 redis 的多個客戶(redis-cli),這裡涉及的就是 i/o 多路復用模型了:
在 i/o 多路復用模型中,最重要的函式呼叫就是 select,
該方法的能夠同時監控多個檔案描述符的可讀可寫情況,當其中的某些檔案描述符可讀或者可寫時,select 方法就會返回可讀以及可寫的檔案描述符個數。
與此同時也有其它的 i/o 多路復用函式 epoll/kqueue/evport,它們相比 select 效能更優秀,同時也能支撐更多的服務。當如下任一情況發生時,會產生套接字的可讀事件:
當如下任一情況發生時,會產生套接字的可寫事件:
此外,在unix系統上,一切皆檔案套接字也不例外,每乙個套接字都有對應的fd(即檔案描述符)。
我們簡單看看這幾個系統呼叫的原型。
select(int nfds, fd_set *r, fd_set *w,fd_set *e, struct timeval *timeout)對於select(),我們需要傳3個集合,r(讀),w(寫)和e。其中,r表示我們對哪些fd的可讀事件感興趣,w表示我們對哪些fd的可寫事件感興趣,每個集合其實是乙個bitmap,通過0/1表示我們感興趣的fd例如,
如:我們對於fd為6的可讀事件感興趣,那麼r集合的第6個bit需要被設定為1這個系統呼叫會阻塞,直到我們感興趣的事件(至少乙個)發生呼叫返回時,
核心同樣使用這3個集合來存放fd實際發生的事件資訊。也就是說,呼叫前這3個集合表示我們感興趣的事件,呼叫後這3個集合表示實際發生的事件
select為最早期的unix系統呼叫,它存在4個問題:
1)這3個bitmap有大小限制(fd_setsize,通常為1024);
2)由於這3個集合在返回時會被核心修改,因此我們每次呼叫時都需要重新設定;
3)我們在呼叫完成後需要掃瞄這3個集合才能知道哪些fd的讀/寫事件發生了,一般情況下全量集合比較大而實際發生讀/寫事件的fd比較少,效率比較低下;
4)核心在每次呼叫都需要掃瞄這3個fd集合,然後檢視哪些fd的事件實際發生,在讀/寫比較稀疏的情況下同樣存在效率問題
由於存在這些問題,於是人們對select進行了改進,從而有了poll
I O多路復用
一 五種i o模型 1 阻塞i o模型 最流行的i o模型是阻塞i o模型,預設情形下,所有套介面都是阻塞的。我們以資料報套介面為例來講解此模型 我們使用udp而不是tcp作為例子的原因在於就udp而言,資料準備好讀取的概念比較簡單 要麼整個資料報已經收到,要麼還沒有。然而對於tcp來說,諸如套介面...
i o多路復用
最常見的i o多路復用就是 select poll epoll了,下面說說他們的一些特點和區別吧。select 可讀 可寫 異常三種檔案描述符集的申明和初始化。fd set readfds,writefds,exceptionfds fd zero readfds fd zero writefds ...
I O多路復用
我們都知道unix like 世界裡,一切皆檔案,而檔案是什麼呢?檔案就是一串二進位製流而已,不管socket,還是fifo 管道 終端,對我們來說,一切都是檔案,一切都是流。在資訊 交換的過程中,我們都是對這些流進行資料的收發操作,簡稱為i o操作 input and output 往流中讀出資料...