epoll機制詳解
epoll機制詳解
大牛的詳解編程
epoll詳解
- 什麼是epoll?
- epoll是爲處理大批量句柄而做了改進的poll, 是性能最好的多路I/O就緒通知方法;
- 只有三個系統調用: epoll_create, epoll_ctl, epoll_wait;
- epoll_ctl - epoll的事件註冊函數,它不一樣於select()是在監聽事件時告訴內核要監聽什麼類型的事件,而是在這裏先註冊要監聽的事件類型;
- epoll的工做原理
- epoll一樣只告知那些就緒的文件描述符,並且當咱們調用epoll_wait()得到就緒文件描述符時,返回的不是實際的描述符,而是一個表明就緒描述符數量的值;
- 你只須要去epoll指定的一個數組中依次取得相應數量的文件描述符便可,這裏也使用了內存映射(mmap)技術,這樣便完全省掉了這些文件描述符在系統調用時複製的開銷;
- 另外一個本質的改進在於epoll採用基於事件的就緒通知方式;
- 在select/poll中,進程只有在調用必定的方法後,內核纔對全部監視的文件描述符進行掃描;
- 而epoll事先經過epoll_ctl()來註冊一個文件描述符,一旦基於某個文件描述符就緒時,內核會採用相似callback的回調機制,迅速激活這個文件描述符,當進程調用epoll_wait()時便獲得通知;
- epoll的兩種工做方式
- 水平觸發(LT)
- 至關於速度比較快的poll;
- LT(level triggered)是epoll缺省的工做方式,而且同時支持block和no-block socket.在這種作法中,內核告訴你一個文件描述符是否就緒了,而後你能夠對這個就緒的fd進行IO操做;
- 若是你不做任何操做,內核仍是會繼續通知你的,因此,這種模式編程出錯誤可能性要小一點;
- 傳統的select/poll都是這種模型的表明.
- 邊緣觸發(ET)
- 用了EPOLLET標誌;
- 至關於非阻塞的讀;
- ET (edge-triggered)是高速工做方式,只支持no-block socket,它效率要比LT更高;
- ET與LT的區別在於,當一個新的事件到來時,ET模式下固然能夠從epoll_wait調用中獲取到這個事件,但是若是此次沒有把這個事件對應的套接字緩衝區處理完,在這個套接字中沒有新的事件再次到來時,在ET模式下是沒法再次從epoll_wait調用中獲取這個事件的;
- 而LT模式正好相反,只要一個事件對應的套接字緩衝區還有數據,就總能從epoll_wait中獲取這個事件;
- LT模式下開發基於epoll的應用要簡單些,不太容易出錯。而在ET模式下事件發生時,若是沒有完全地將緩衝區數據處理完,則會致使緩衝區中的用戶請求得不到響應;
epoll的優勢
- 支持一個進程打開大數目的socket描述符(FD);
- select 最不能忍受的是一個進程所打開的FD是有必定限制的,由FD_SETSIZE設置,默認值是2048;
- 從新編譯內核解決這個問題, 或者利用多進程解決這個問題(Apache方案);
- epoll則沒有這個限制,它所支持的FD上限是最大能夠打開文件的數目, 這個數字通常遠大於2048, 1G的內存上是10W左右;
- IO效率不隨FD數目增長而線性降低;
- 傳統的select/poll另外一個致命弱點就是當你擁有一個很大的socket集合,性能會線性降低;
- 不過因爲網絡延時,任一時間只有部分的socket是"活躍"的,可是select/poll每次調用都會線性掃描所有的集合,致使效率呈現線性降低;
- epoll不存在這個問題,它只會對"活躍"的socket進行操做(內核實現的緣由);
- 這是由於在內核實現中epoll是根據每一個fd上面的callback函數實現的;
- 只有"活躍"的socket纔會主動的去調用 callback函數,其餘idle狀態socket則不會;
- epoll實現了一個"僞AIO,由於這時候推進力在os內核;
- 在一些 benchmark中,若是全部的socket基本上都是活躍的;
- epoll並不比select/poll有什麼效率,相反,若是過多使用epoll_ctl,效率相比還有稍微的降低;
- 使用mmap加速內核與用戶空間的消息傳遞;
- 這點實際上涉及到epoll的具體實現了;
- 不管是select,poll仍是epoll都須要內核把FD消息通知給用戶空間,如何避免沒必要要的內存拷貝就很重要,在這點上,epoll是經過內核於用戶空間mmap同一塊內存實現的;
- epoll是一種IO多路複用技術,能夠很是高效的處理數以百萬計的socket句柄;
- 由於select/poll每次調用時都要傳遞你所要監控的全部socket給select/poll系統調用,這意味着須要將用戶態的socket列表copy到內核態,若是以萬計的句柄會致使每次都要copy幾十幾百KB的內存到內核態,很是低效;
- epoll_wait卻不用傳遞socket句柄給內核,由於內核已經在epoll_ctl中拿到了要監控的句柄列表;
- epoll還維護了一個雙鏈表,用戶存儲發生的事件;
- 當epoll_wait調用時,僅僅觀察這個list鏈表裏有沒有數據即eptime項便可;
- 有數據就返回,沒有數據就sleep,等到timeout時間到後即便鏈表沒數據也返回;
- 這個準備就緒list鏈表是怎麼維護的呢?
- 當咱們執行epoll_ctl時,除了把socket放到epoll文件系統裏file對象對應的紅黑樹上以外;
- 還會給內核中斷處理程序註冊一個回調函數,告訴內核,若是這個句柄的中斷到了,就把它放到準備就緒list鏈表裏;
- 一顆紅黑樹,一張準備就緒句柄鏈表,少許的內核cache,就幫咱們解決了大併發下的socket處理問題;
- 執行epoll_create時,建立了紅黑樹和就緒鏈表;
- 執行epoll_ctl時,若是增長socket句柄,則檢查在紅黑樹中是否存在,存在當即返回,不存在則添加到樹幹上,而後向內核註冊回調函數,用於當中斷事件來臨時向準備就緒鏈表中插入數據;
- 執行epoll_wait時馬上返回準備就緒鏈表裏的數據便可;
歡迎關注本站公眾號,獲取更多信息