一、epoll多路複用
這裡重點要說的就是redis的IO程式設計模型,首先了解下
為什麼要有多路複用呢 ?
案例
引用知乎上一個高讚的回答來解釋什麼是I/O多路複用。假設你是一個老師,讓30個學生解答一道題目,然後檢查學生做的是否正確,你有下面幾個選擇:
第一種選擇:按順序逐個檢查,先檢查A,然後是B,之後是C、D....這中間如果有一個學生卡住,全班都會被耽誤。這種模式就好比,你用迴圈挨個處理socket,根本不具有併發能力。
第二種選擇:你建立30個分身,每個分身檢查一個學生的答案是否正確。 這種類似於為每一個使用者建立一個程序或者 - 執行緒處理連線。
第三種選擇,你站在講臺上等,誰解答完誰舉手。這時C、D舉手,表示他們解答問題完畢,你下去依次檢查C、D的答案,然後繼續回到講臺上等。此時E、A又舉手,然後去處理 E 和 A。
如果沒有多路複用,一個執行緒只能監聽一個埠的一個連線,這樣這個效率比較低。當然我們有幾種辦法可以破除這個,一個是使用多執行緒模型,我們還是監聽一個埠,
但是一個請求進來,我們為其建立一個執行緒。但是這種消耗是比較大的。所以我們一直想辦法,有沒有辦法一個執行緒監聽多個埠,或者多個一個埠的多個連線(fd)。
這裡再說說fd, 檔案描述符(file descriptor)是核心為了高效管理已被開啟的檔案所建立的索引,其是一個非負整數(通常是小整數),用於指代被開啟的檔案,
所有執行I/O操作(包括網路socket操作)的系統呼叫都透過檔案描述符。每個連線請求上來,都會建立一個連線套接字,一個連線使用一個連線套接字。
對於監聽埠,我們會有一個監聽套接字,對應監聽fd。我們所有的監聽業務都是從監聽這個套接字開始的。
那麼如果我一個程式能同時監聽多個連線套接字,是不是就很讚了。是的,這就是linux的io多路複用邏輯。但是這麼多連線套接字,傳遞資料等是斷斷續續的,
A連線接收一個包,B連線再接收一個包,A連線再接收一個包,B連線再接收一個包....如果我等著A連線把包都接收完再處理B,那效率是非常慢的。所以,
這裡我們就需要有一個通知機制,讓有收到包的時候通知下處理執行緒。
linux的IO多路複用邏輯主要有三種:select、 poll、 epoll
select
select模型監聽的三個事件:讀資料事件,寫資料事件,異常事件。
使用select模型的步驟如下:
-
我們確定要監聽的監聽fd列表
-
呼叫select監聽所有監聽fd,阻塞執行緒。
-
select只有當有事件出現並且有事件的fd已經等待完畢
-
如果是建立一個連線事件:
-
建立一個連線套接字,連線fd
-
將連線fd和監聽fd集合放在一起
-
如果是一個讀寫事件:
-
遍歷所有fd,判斷是否是準備好的fd
-
如果是準備好的fd,進行業務讀寫邏輯
poll模型:
poll傳遞給核心的是:
-
監聽的fd集合
-
需要監聽的事件型別
-
實際發生的事件型別
poll的模型邏輯是:
-
我們確定要監聽的監聽fd列表
-
呼叫poll監聽所有監聽fd,阻塞執行緒。
-
poll只有當有事件出現才解除阻塞
-
如果是建立一個連線事件:
-
建立一個連線套接字,連線fd
-
將連線fd和監聽fd集合放在一起
-
如果是一個讀寫事件:
-
遍歷所有fd,判斷是否是有讀寫事件的fd
-
如果fd有讀寫事件,進行業務讀寫邏輯