epoll機制詳解

epoll機制詳解

大牛的詳解編程

epoll詳解

  1. 什麼是epoll?
    • epoll是爲處理大批量句柄而做了改進的poll, 是性能最好的多路I/O就緒通知方法;
    • 只有三個系統調用: epoll_create, epoll_ctl, epoll_wait;
    • epoll_ctl - epoll的事件註冊函數,它不一樣於select()是在監聽事件時告訴內核要監聽什麼類型的事件,而是在這裏先註冊要監聽的事件類型;
  2. epoll的工做原理
    • epoll一樣只告知那些就緒的文件描述符,並且當咱們調用epoll_wait()得到就緒文件描述符時,返回的不是實際的描述符,而是一個表明就緒描述符數量的值;
    • 你只須要去epoll指定的一個數組中依次取得相應數量的文件描述符便可,這裏也使用了內存映射(mmap)技術,這樣便完全省掉了這些文件描述符在系統調用時複製的開銷;
    • 另外一個本質的改進在於epoll採用基於事件的就緒通知方式;
      • 在select/poll中,進程只有在調用必定的方法後,內核纔對全部監視的文件描述符進行掃描;
      • 而epoll事先經過epoll_ctl()來註冊一個文件描述符,一旦基於某個文件描述符就緒時,內核會採用相似callback的回調機制,迅速激活這個文件描述符,當進程調用epoll_wait()時便獲得通知;
  3. 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的優勢

  1. 支持一個進程打開大數目的socket描述符(FD);
    • select 最不能忍受的是一個進程所打開的FD是有必定限制的,由FD_SETSIZE設置,默認值是2048;
      • 從新編譯內核解決這個問題, 或者利用多進程解決這個問題(Apache方案);
    • epoll則沒有這個限制,它所支持的FD上限是最大能夠打開文件的數目, 這個數字通常遠大於2048, 1G的內存上是10W左右;
  2. 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中拿到了要監控的句柄列表;
  3. epoll還維護了一個雙鏈表,用戶存儲發生的事件;
    • 當epoll_wait調用時,僅僅觀察這個list鏈表裏有沒有數據即eptime項便可;
    • 有數據就返回,沒有數據就sleep,等到timeout時間到後即便鏈表沒數據也返回;
    • 這個準備就緒list鏈表是怎麼維護的呢?
      • 當咱們執行epoll_ctl時,除了把socket放到epoll文件系統裏file對象對應的紅黑樹上以外;
      • 還會給內核中斷處理程序註冊一個回調函數,告訴內核,若是這個句柄的中斷到了,就把它放到準備就緒list鏈表裏;
    • 一顆紅黑樹,一張準備就緒句柄鏈表,少許的內核cache,就幫咱們解決了大併發下的socket處理問題;
      • 執行epoll_create時,建立了紅黑樹和就緒鏈表;
      • 執行epoll_ctl時,若是增長socket句柄,則檢查在紅黑樹中是否存在,存在當即返回,不存在則添加到樹幹上,而後向內核註冊回調函數,用於當中斷事件來臨時向準備就緒鏈表中插入數據;
      • 執行epoll_wait時馬上返回準備就緒鏈表裏的數據便可;
相關文章
相關標籤/搜索