本文參考社長的 TinyWebServer 庖丁解牛
epoll 常用API
epoll_create 函式
#include <sys/epoll.h>
int epoll_create(int size);
創建一個指示 epoll 內核事件表的檔案描述符,該描述符將用作其他 epoll 系統呼叫的第一個引數,此處的 size 引數不起作用,
epoll_ctl 函式
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
該函式用于操作內核事件表監控的檔案描述符上的事件:注冊、修改、洗掉:
epfd:為 epoll_create 的句柄;op:表示動作,用 3 個宏來表示:EPOLL_CTL_ADD:注冊新的 fd 到 epfd;EPOLL_CTL_MOD:修改已經注冊的 fd 的監聽事件;EPOLL_CTL_DEL:從 epfd 洗掉一個 fd;
event:告訴內核需要監聽的事件,
其中,event 是 epoll_event 結構體指標型別,表示內核監聽的事件,具體定義如下:
struct epoll_event {
__uint32_t events;
epoll_data_t data;
};
events描述事件型別,其中 epoll 事件型別有以下幾種:EPOLLIN:表示對應的檔案描述符可讀(包括對端SOCKET正常關閉)EPOLLOUT:表示對應的檔案描述符可寫;EPOLLPRI:表示對應的檔案描述符有緊急的資料可讀(這里應該表示有帶外資料到來)EPOLLERR:表示對應的檔案描述符發生錯誤;EPOLLHUP:表示對應的檔案描述符被掛斷;EPOLLLET:將 EPOLL 設定為邊緣觸發(ET)模式;EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之后,如果還需要繼續監聽這個 socket 的話,需要再次把這個 socket 加入到 EPOLL 佇列中,
epoll_wait 函式
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
該函式用于等待所監控的檔案描述符上有事件的產生,回傳就緒的檔案描述符的個數,
events:用來存盤內核得到的事件的集合;maxevents:告知內核這個 events 有多大,這個值不能大于創建 epoll_create() 時的大小;timeout:超時時間:-1:阻塞;0:立即回傳,非阻塞;>0:指定毫秒數;
- 回傳值:成功回傳有多少檔案描述符就緒,時間到時回傳 0,出錯時回傳-1,
select/poll/epoll 的區別
-
呼叫函式
- select 和 poll 都是一個函式,epoll 是一組函式;
-
檔案描述符數量
- select 使用線性表保存檔案描述符的集合,檔案描述符有上限,一般是 1024,但可以修改原始碼,重新編譯內核,不推薦;
- poll 是使用鏈表存盤檔案描述符的集合,突破了檔案描述符的上限;
- epoll 使用紅黑樹存盤檔案描述符的集合,突破了檔案描述符的上限(通過命令 ulimit -n number 修改,僅對當前終端有效);
-
將檔案描述符從用戶傳給內核:
- select 和 poll 將所有檔案描述符拷貝到內核態,每次呼叫都需要拷貝;
- epoll 通過 epoll_create 建立一棵紅黑樹,通過 epoll_ctl 將要監聽的檔案描述符注冊到紅黑樹上,檔案描述符都在內核態;
-
內核判斷就緒的檔案描述符:
- select 和 poll 通過遍歷檔案描述符集合,判斷哪個檔案描述符上有事件發生;
- epoll_create 時,內核除了會建立一個紅黑樹來存盤以后 epoll_ctl 傳來的 fd 外,還會再建立一個 list 鏈表,用于存盤準備就緒的事件,當 epoll_wait 呼叫時,僅僅觀察這個 list 鏈表上有沒有資料即可;
- epoll 是根據每個 fd 上面的回呼函式(中斷函式)判斷,只有發生了時間的 socket 才會主動的去呼叫 callback 函式,其他空閑狀態的 socket 則不會,若是就緒事件,則插入 list;
-
應用程式索引就緒檔案描述符:
- select/epoll 只回傳發生了事件的檔案描述符的個數,若想要知道哪些檔案描述符發生了事件,需要再次遍歷;
- epoll 回傳的是發生了事件的個數和結構體陣列,結構體包含 socket 的資訊,因此直接處理回傳的陣列即可;
-
作業模式:
- select/poll 都只能作業在低效的 LT 模式下;
- epoll 則可以作業在高效的 ET 模式,并且 epoll 還支持 EPOLLONESHOT 事件,可以進一步減少可讀、可寫和例外事件被觸發的次數;
其實 ET 和 LT 哪個高效也是針對不同的任務而言,
-
應用場景:
- 如果所有的 fd 都是活躍連接,epoll 需要建立紅黑樹和鏈表,效率反而不高,不如 select/epoll;
- 如果監測的 fd 數目較小,且各個 fd 都比較活躍,建議使用 select/poll;
- 如果監測的 fd 數目非常大,并且單位時間內只有其中一小部分 fd 處于就緒狀態,這個時候使用 epoll 能夠明顯提升性能,
ET、LT、EPOLLONESHOT
-
LT 水平觸發模式
- epoll_wait 檢測到檔案描述符有事件發生,則將其通知給應用程式,應用程式可以不立即處理該事件;
- 當下一次呼叫 epoll_wait 時,epoll_wait 還會再次向應用程式報告此事件,直至被處理,
Note:
一個事件只要有,就會一直觸發,
socket 上只要有未讀完的資料,就會一直產生 EPOLLIN 事件,所以讀完資料要移除事件,避免一直觸發,
-
ET 邊緣觸發模式
- epoll_wait 檢測到檔案描述符有事件發生,則將其通知給應用程式,應用程式必須立即處理該事件;
- 必須要一次性將資料讀取完,使用非阻塞 I/O,讀取到出現 eagain,
Note:
只有一個事件從無到有,才會觸發,
socket 上每新來一次資料就會觸發一次,如果某一次觸發后,未將 socket 上的資料全部讀完,也不會再次觸發,除非再來一次資料,所以必須要一次性讀完所有資料,如果未讀完,需要再次將事件注冊,
ET 模式必須配合非阻塞 I/O 實作,因為 ET 模式會一次性讀取完所有的資料,如果是阻塞 I/O 的話,會導致執行緒阻塞,影響重新呼叫 epoll_wait 來監聽其他事件,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/469600.html
標籤:其他
上一篇:多執行緒筆記(一)
