1. 程式人生 > >Linux epoll詳解

Linux epoll詳解

一、什麼是epoll

epoll是什麼?按照man手冊的說法:是為處理大批量控制代碼而作了改進的poll。當然,這不是2.6核心才有的,它是在2.5.44核心中被引進的(epoll(4) is a new API introduced in Linuxkernel 2.5.44),它幾乎具備了之前所說的一切優點,被公認為Linux2.6下效能最好的多路I/O就緒通知方法。

二、epoll的相關係統呼叫

epoll只有epoll_create,epoll_ctl,epoll_wait 3個系統呼叫。

1. int epoll_create(int size);

建立一個epoll

的控制代碼。自從linux2.6.8之後,size引數是被忽略的。需要注意的是,當建立好epoll控制代碼後,它就是會佔用一個fd值,在linux下如果檢視/proc/程序id/fd/,是能夠看到這個fd的,所以在使用完epoll後,必須呼叫close()關閉,否則可能導致fd被耗盡。

2. int epoll_ctl(int epfd, int op,int fd, struct epoll_event *event);

epoll的事件註冊函式,它不同於select()是在監聽事件時告訴核心要監聽什麼型別的事件,而是在這裡先註冊要監聽的事件型別。

第一個引數是epoll_create()的返回值。

第二個引數表示動作,用三個巨集來表示:

EPOLL_CTL_ADD:註冊新的fd到epfd中;

EPOLL_CTL_MOD:修改已經註冊的fd的監聽事件;

EPOLL_CTL_DEL:從epfd中刪除一個fd;

第三個引數是需要監聽的fd。

第四個引數是告訴核心需要監聽什麼事,struct epoll_event結構如下:

//儲存觸發事件的某個檔案描述符相關的資料(與具體使用方式有關)
typedef union epoll_data {
   void *ptr;
   int fd;
   __uint32_t u32;
   __uint64_t u64;
} epoll_data_t;
// 感興趣的事件和被觸發的事件
struct epoll_event {
    __uint32_t events; /* Epoll events */
    epoll_data_t data; /* User data variable */
};

events可以是以下幾個巨集的集合:

EPOLLIN :表示對應的檔案描述符可以讀(包括對端SOCKET正常關閉);

EPOLLOUT:表示對應的檔案描述符可以寫;

EPOLLPRI:表示對應的檔案描述符有緊急的資料可讀(這裡應該表示有帶外資料到來);

EPOLLERR:表示對應的檔案描述符發生錯誤;

EPOLLHUP:表示對應的檔案描述符被結束通話;

EPOLLET: 將EPOLL設為邊緣觸發(Edge Triggered)模式,這是相對於水平觸發(LevelTriggered)來說的。

EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL佇列裡

3. int epoll_wait(int epfd, structepoll_event * events, int maxevents, int timeout);

收集在epoll監控的事件中已經發生的事件。引數events是分配好的epoll_event結構體陣列,epoll將會把發生的事件賦值到events陣列中(events不可以是空指標,核心只負責把資料複製到這個events陣列中,不會去幫助我們在使用者態中分配記憶體)。maxevents告之核心這個events有多大,這個 maxevents的值不能大於建立epoll_create()時的size,引數timeout是超時時間(毫秒,0會立即返回,-1將不確定,也有說法說是永久阻塞)。如果函式呼叫成功,返回對應I/O上已準備好的檔案描述符數目,如返回0表示已超時。

還有一個與這個類似的函式epoll_pwait:

int epoll_pwait(int epfd, struct epoll_event *events,
int maxevents, int timeout,
const sigset_t *sigmask); 

與epoll_wait的區別是可以通過最後一個引數設定阻塞過程中訊號遮蔽字。

上面的函式原型等價於:

sigset_toriginmask;

sigpromask(SIG_SETMASK,&sigmask,& originmask);

ready = epoll_wait(epfd,&events,maxevents,timeout);

sigpromask(SIG_SETMASK,&  originmask ,NULL);

三、epoll工作原理

    epoll同樣只告知那些就緒的檔案描述符,而且當我們呼叫epoll_wait()獲得就緒檔案描述符時,返回的不是實際的描述符,而是一個代表就緒描述符數量的值,你只需要去epoll指定的一個數組中依次取得相應數量的檔案描述符即可,這裡也使用了記憶體對映(mmap)技術,這樣便徹底省掉了這些檔案描述符在系統呼叫時複製的開銷。

    另一個本質的改進在於epoll採用基於事件的就緒通知方式。在select/poll中,程序只有在呼叫一定的方法後,核心才對所有監視的檔案描述符進行掃描,而epoll事先通過epoll_ctl()來註冊一個檔案描述符,一旦基於某個檔案描述符就緒時,核心會採用類似callback的回撥機制,迅速啟用這個檔案描述符,當程序呼叫epoll_wait()時便得到通知。

Epoll2種工作方式-水平觸發(LT)和邊緣觸發(ET

假如有這樣一個例子:

1. 我們已經把一個用來從管道中讀取資料的檔案控制代碼(RFD)新增到epoll描述符

2. 這個時候從管道的另一端被寫入了2KB的資料

3. 呼叫epoll_wait(2),並且它會返回RFD,說明它已經準備好讀取操作

4. 然後我們讀取了1KB的資料

5. 呼叫epoll_wait(2)......

EdgeTriggered工作模式:

    如果我們在第1步將RFD新增到epoll描述符的時候使用了EPOLLET標誌,那麼在第5步呼叫epoll_wait(2)之後將有可能會掛起,因為剩餘的資料還存在於檔案的輸入緩衝區內,而且資料發出端還在等待一個針對已經發出資料的反饋資訊。只有在監視的檔案控制代碼上發生了某個事件的時候 ET 工作模式才會彙報事件。因此在第5步的時候,呼叫者可能會放棄等待仍在存在於檔案輸入緩衝區內的剩餘資料。在上面的例子中,會有一個事件產生在RFD控制代碼上,因為在第2步執行了一個寫操作,然後,事件將會在第3步被銷燬。因為第4步的讀取操作沒有讀空檔案輸入緩衝區內的資料,因此我們在第5步呼叫epoll_wait(2)完成後,是否掛起是不確定的。epoll工作在ET模式的時候,必須使用非阻塞套介面,以避免由於一個檔案控制代碼的阻塞讀/阻塞寫操作把處理多個檔案描述符的任務餓死。最好以下面的方式呼叫ET模式的epoll介面,在後面會介紹避免可能的缺陷。

i基於非阻塞檔案控制代碼

ii只有當read(2)或者write(2)返回EAGAIN時才需要掛起,等待。但這並不是說每次read()時都需要迴圈讀,直到讀到產生一個EAGAIN才認為此次事件處理完成,當read()返回的讀到的資料長度小於請求的資料長度時,就可以確定此時緩衝中已沒有資料了,也就可以認為此事讀事件已處理完成。

LevelTriggered工作模式

相反的,以LT方式呼叫epoll介面的時候,它就相當於一個速度比較快的poll(2),並且無論後面的資料是否被使用,因此他們具有同樣的職能。因為即使使用ET模式的epoll,在收到多個chunk的資料的時候仍然會產生多個事件。呼叫者可以設定EPOLLONESHOT標誌,在 epoll_wait(2)收到事件後epoll會與事件關聯的檔案控制代碼從epoll描述符中禁止掉。因此當EPOLLONESHOT設定後,使用帶有EPOLL_CTL_MOD標誌的epoll_ctl(2)處理檔案控制代碼就成為呼叫者必須作的事情。

LT(level triggered)epoll預設的工作方式,並且同時支援blockno-block socket.在這種做法中,核心告訴你一個檔案描述符是否就緒了,然後你可以對這個就緒的fd進行IO操作。如果你不作任何操作,核心還是會繼續通知你的,所以這種模式程式設計出錯誤可能性要小一點。傳統的select/poll都是這種模型的代表.

ET (edge-triggered)是高速工作方式,只支援no-block socket,它效率要比LT更高。ETLT的區別在於,當一個新的事件到來時,ET模式下當然可以從epoll_wait呼叫中獲取到這個事件,可是如果這次沒有把這個事件對應的套接字緩衝區處理完,在這個套接字中沒有新的事件再次到來時,在ET模式下是無法再次從epoll_wait呼叫中獲取這個事件的。而LT模式正好相反,只要一個事件對應的套接字緩衝區還有資料,就總能從epoll_wait中獲取這個事件。

因此,LT模式下開發基於epoll的應用要簡單些,不太容易出錯。而在ET模式下事件發生時,如果沒有徹底地將緩衝區資料處理完,則會導致緩衝區中的使用者請求得不到響應。

圖示說明:


注:Nginx預設採用ET模式來使用epoll

epoll的優點:

1.支援一個程序開啟大數目的socket描述符(FD)

select 最不能忍受的是一個程序所開啟的FD是有一定限制的,由FD_SETSIZE設定,預設值是2048。對於那些需要支援的上萬連線數目的IM伺服器來說顯然太少了。這時候你一是可以選擇修改這個巨集然後重新編譯核心,不過資料也同時指出這樣會帶來網路效率的下降,二是可以選擇多程序的解決方案(傳統的 Apache方案),不過雖然linux上面建立程序的代價比較小,但仍舊是不可忽視的,加上程序間資料同步遠比不上執行緒間同步的高效,所以也不是一種完美的方案。不過 epoll則沒有這個限制,它所支援的FD上限是最大可以開啟檔案的數目,這個數字一般遠大於2048,舉個例子,1GB記憶體的機器上大約是10萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統記憶體關係很大。

2.IO效率不隨FD數目增加而線性下降

    傳統的select/poll另一個致命弱點就是當你擁有一個很大的socket集合,不過由於網路延時,任一時間只有部分的socket"活躍"的,但是select/poll每次呼叫都會線性掃描全部的集合,導致效率呈現線性下降。但是epoll不存在這個問題,它只會對"活躍"socket進行操作---這是因為在核心實現中epoll是根據每個fd上面的callback函式實現的。那麼,只有"活躍"socket才會主動的去呼叫 callback函式,其他idle狀態socket則不會,在這點上,epoll實現了一個""AIO因為這時候推動力在os核心。在一些 benchmark中,如果所有的socket基本上都是活躍的---比如一個高速LAN環境,epoll並不比select/poll有什麼效率,相反,如果過多使用epoll_ctl,效率相比還有稍微的下降。但是一旦使用idle connections模擬WAN環境,epoll的效率就遠在select/poll之上了。

3.使用mmap加速核心與使用者空間的訊息傳遞

這點實際上涉及到epoll的具體實現了。無論是select,poll還是epoll都需要核心把FD訊息通知給使用者空間,如何避免不必要的記憶體拷貝就很重要,在這點上,epoll是通過核心於使用者空間mmap同一塊記憶體實現的。而如果你想我一樣從2.5核心就關注epoll的話,一定不會忘記手工 mmap這一步的。

4.核心微調

這一點其實不算epoll的優點了,而是整個linux平臺的優點。也許你可以懷疑linux平臺,但是你無法迴避linux平臺賦予你微調核心的能力。比如,核心TCP/IP協議棧使用記憶體池管理sk_buff結構,那麼可以在執行時期動態調整這個記憶體pool(skb_head_pool)的大小---通過echoXXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函式的第2個引數(TCP完成3次握手的資料包佇列長度),也可以根據你平臺記憶體大小動態調整。更甚至在一個數據包面數目巨大但同時每個資料包本身大小卻很小的特殊系統上嘗試最新的NAPI網絡卡驅動架構。

linuxepoll如何實現高效處理百萬控制代碼的

開發高效能網路程式時,windows開發者們言必稱iocplinux開發者們則言必稱epoll。大家都明白epoll是一種IO多路複用技術,可以非常高效的處理數以百萬計的socket控制代碼,比起以前的selectpoll效率高大發了。我們用起epoll來都感覺挺爽,確實快,那麼,它到底為什麼可以高速處理這麼多併發連線呢?

使用起來很清晰,首先要呼叫epoll_create建立一個epoll物件。引數size是核心保證能夠正確處理的最大控制代碼數,多於這個最大數時核心可不保證效果。

epoll_ctl可以操作上面建立的epoll,例如,將剛建立的socket加入到epoll中讓其監控,或者把 epoll正在監控的某個socket控制代碼移出epoll,不再監控它等等。

epoll_wait在呼叫時,在給定的timeout時間內,當在監控的所有控制代碼中有事件發生時,就返回使用者態的程序。

從上面的呼叫方式就可以看到epollselect/poll的優越之處:因為後者每次呼叫時都要傳遞你所要監控的所有socketselect/poll系統呼叫,這意味著需要將使用者態的socket列表copy到核心態,如果以萬計的控制代碼會導致每次都要copy幾十幾百KB的記憶體到核心態,非常低效。而我們呼叫epoll_wait時就相當於以往呼叫select/poll,但是這時卻不用傳遞socket控制代碼給核心,因為核心已經在epoll_ctl中拿到了要監控的控制代碼列表。

所以,實際上在你呼叫epoll_create後,核心就已經在核心態開始準備幫你儲存要監控的控制代碼了,每次呼叫epoll_ctl只是在往核心的資料結構裡塞入新的socket控制代碼。

當一個程序呼叫epoll_create方法時,Linux核心會建立一個eventpoll結構體,這個結構體中有兩個成員與epoll的使用方式密切相關:

/*

*This structure is stored inside the "private_data" member of the file

*structure and represents the main data structure for the eventpoll

*interface.

*/

structeventpoll {

/* Protect the access to thisstructure */

spinlock_t lock;

/*

* This mutex is used to ensurethat files are not removed

* while epoll is using them. Thisis held during the event

* collection loop, the filecleanup path, the epoll file   * exit code and the ctl operations.

*/

struct mutex mtx;

/* Wait queue used bysys_epoll_wait() */

wait_queue_head_t wq;

/* Wait queue used byfile->poll() */

wait_queue_head_t poll_wait;

/* List of ready file descriptors*/

struct list_head rdllist;

/* RB tree root used to storemonitored fd structs */

  /*紅黑樹根節點,這棵樹儲存著所有新增到epoll中的事件,也就是這個epoll監控的事件 */

struct rb_root rbr;

/*

* This is a single linked listthat chains all the "struct epitem" that

* happened while transferringready events to userspace w/out

* holding ->lock.

*/

struct epitem*ovflist;

/* wakeup_source used whenep_scan_ready_list is running */

struct wakeup_source*ws;

/* The user that created theeventpoll descriptor */

struct user_struct*user;

struct file*file;

/* used to optimize loopdetection check */

int visited;

/*雙向連結串列中儲存著將要通過epoll_wait返回給使用者的、滿足條件的事件 */

struct list_head visited_list_link;

};

每一個epoll物件都有一個獨立的eventpoll結構體,這個結構體會在核心空間中創造獨立的記憶體,用於儲存使用epoll_ctl方法向epoll物件中新增進來的事件。這樣,重複的事件就可以通過紅黑樹而高效的識別出來。

epoll中,對於每一個事件都會建立一個epitem結構體:


/*

  * Each file descriptor added to the eventpoll interfacewill

  * have an entry of this type linked to the"rbr" RB tree.

  * Avoid increasing the size of this struct, there can bemany thousands

  * of these on a server and we do not want this to takeanother cache line.

  */

 struct epitem {

         /* RB tree node used to link this structure to theeventpoll RB tree */

         struct rb_node rbn;

         /* List header used to link this structure to the eventpollready list */

         struct list_head rdllink;

         /*

          * Workstogether "struct eventpoll"->ovflist in keeping the

          * singlelinked chain of items.

          */

         struct epitem*next;

         /* The file descriptor information this item refers to */

         struct epoll_filefd ffd;

         /* Number of active wait queue attached to poll operations*/

         int nwait;

         /* List containing poll wait queues */

         struct list_head pwqlist;

         /* The "container" of this item */

         struct eventpoll*ep;

         /* List header used to link this item to the "structfile" items list */

         struct list_head fllink;

         /* wakeup_source used when EPOLLWAKEUP is set */

         struct wakeup_source __rcu*ws;

         /* The structure that describe the interested events andthe source fd */

         struct epoll_event event;

 };


    此外,epoll還維護了一個雙鏈表,使用者儲存發生的事件。epoll_wait呼叫時,僅僅觀察這個list連結串列裡有沒有資料即eptime項即可。有資料就返回,沒有資料就sleep,等到timeout時間到後即使連結串列沒資料也返回。所以,epoll_wait非常高效。

    而且,通常情況下即使我們要監控百萬計的控制代碼,大多一次也只返回很少量的準備就緒控制代碼而已,所以,epoll_wait僅需要從核心態copy少量的控制代碼到使用者態而已,如何能不高效?!

    那麼,這個準備就緒list連結串列是怎麼維護的呢?

相關推薦

linux epoll及使用方法概述

一、什麼是epoll 於Linux 2.5.44首度登場的epoll是Linux核心的可擴充套件I/O事件通知機制。它設計目的只在取代既有POSIX select(2)與poll(2)系統函式,讓需要

Linux epoll

一、什麼是epoll epoll是什麼?按照man手冊的說法:是為處理大批量控制代碼而作了改進的poll。當然,這不是2.6核心才有的,它是在2.5.44核心中被引進的(epoll(4) is a new API introduced in Linuxkernel 2.5.44),它幾乎具備了之前所

Linux IO模式及select、poll、epoll

注:本文來自博友文章,出處https://segmentfault.com/a/1190000003063859。總結不錯,略作收藏,感謝分享。 同步IO和非同步IO,阻塞IO和非阻塞IO分別是什麼,到底有什麼區別?不同的人在不同的上下文下給出的答案是不同的。所以先限定一下本文的上

linuxepoll

什麼是epoll epoll是什麼?按照man手冊的說法:是為處理大批量控制代碼而作了改進的poll。當然,這不是2.6核心才有的,它是在2.5.44核心中被引進的(epoll(4) is a new API introduced in Linux kernel 2.5.44),它幾乎具備了之前所說的一切優

Linux IO模式(BIO、NIO、IO多路複用、非同步IO)及 select、poll、epoll

同步IO和非同步IO,阻塞IO和非阻塞IO分別是什麼,到底有什麼區別?不同的人在不同的上下文下給出的答案是不同的。所以先限定一下本文的上下文。 本文討論的背景是Linux環境下的network IO。 一 概念說明 在進行解釋之前,首先要說明幾個概念: -

18.Linux下的I/O複用與epoll

為什麼引出epoll? 1.select的缺點 1.select所用到的FD_SET是有限的 /linux/posix_types.h: #define __FD_SETSIZE 102

linux 高併發網路程式設計之epoll

前言       I/O多路複用有很多種實現。在linux上,2.4核心前主要是select和poll,自Linux 2.6核心正式引入epoll以來,epoll已經成為了目前實現高效能網路伺服器的必備技術。儘管他們的使用方法不盡相同,但是本質上卻沒有什麼區別。本文將重點探

Linux IO模式及 select、poll、epoll

一 概念說明 在進行解釋之前,首先要說明幾個概念: - 使用者空間和核心空間 - 程序切換 - 程序的阻塞 - 檔案描述符 - 快取 I/O 使用者空間與核心空間 現在作業系統都是採用虛擬儲存器,那麼對32位作業系統而言,它的定址空間(虛擬儲存空間)為4G(2的32次方)。作業系統的核心是核心,獨立

Linux特性

linux特性詳解 history 命令替換 命令別名 文件名統配 bash及其特性:shell: 外殼GUI:Gnome, KDE, XfceCLI: sh, csh, ksh, bash, tcsh, zsh Linux允許同一個用戶登錄多次root, student程序:進程 進程:

Linux LVM及創建

lvm 1. LVM基本創建及管理 2. LVM快照 3. LVM與RAID的結合使用:基於RAID的LVMLVM創建: 描述: LVM是邏輯盤卷管理(LogicalVolumeManager)的簡稱,它是Linux環境下對磁盤分區進行管理的一種機制,LVM是建立在硬盤和分區之上的一個邏輯層,來提高磁盤分

linux rsyslog

syslog priority facility rsyslog 概念和特性歷史日誌、歷史事件:時間、事件本身、日誌級別(根據時間的關鍵性程度)系統日誌服務:syslog有兩個進程syslogd(system負責用戶進程)、 klogd(kernel負責內核進程)centos7:rsyslog

IO多路復用--epoll

模式 輪詢 同步 對象 讀數 csdn 我們 涵蓋 多進程 epoll 或者 kqueue 的原理是什麽? 【轉自知乎】 Epoll 引入簡介 首先我們來定義流的概念,一個流可以是文件,socket,pipe等等可以進行I/O操作的內核對象。 不管是文件,還是套接字,

Linux 命令(十)Shell腳本的數組

cti err art case lin start shell pre round 1、數組定義 [[email protected] ~]# a=(1 2 3 4 5 6 7 8) [[email protected]-IDC ~]# echo $

(轉)Linux命令-file

版本信息 ref 獲取文件 linux命令 過程 嘗試 file img 文件類型 Linux命令詳解-file 原文:https://www.cnblogs.com/Dodge/p/4278306.html file命令用來識別文件類型,也可用來辨別一些文件的編碼格

高並發網絡編程之epoll

分享圖片 file 解決 雙鏈表 開始 數據結構實現 list 函數 實現機制 select、poll和epoll的區別 在linux沒有實現epoll事件驅動機制之前,我們一般選擇用select或者poll等IO多路復用的方法來實現並發服務程序。在大數據、高並發、集群等一

linux目錄

style 啟動過程 usr 位置 cpu信息 pos 過程 scripts strong 網卡的配置文件目錄 /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0

Docker 基礎技術之 Linux namespace

基本 mar $$ 裏的 sta 進程資源 進程間通信 開始 消息隊列 Docker 是“新瓶裝舊酒”的產物,依賴於 Linux 內核技術 chroot 、namespace 和 cgroup。本篇先來看 namespace 技術。 Docker 和虛擬機技術一樣,從操作系

linux管道

linux原文鏈接:http://blog.csdn.net/qq_38646470/article/details/79564392 #符號表示| 和管道特別形象。#作用:    管道是Linux中很重要的一種通信方式,是把一個程序的輸出直接連接到另一個程序的輸入

Linux命令(部分昨今兩天)

Linux命令詳解基本命令1.Linux的基本原則:1、由目的單一的小程序組成;組合小程序完成復雜任務;2、一切皆文件;3、盡量避免捕獲用戶接口;(盡量不和用戶進行交互,就是一個程序一但開始運行,就不需要用戶進行任何操作,如ls命令,ifconfig命令)4、配置文件保存為純文本格式;2.命令形式命令格式:命

Linux命令

Linux命令詳解路徑:絕對路徑:凡是以“/”開頭的輸入路徑的方式都是絕對路徑相對路徑:凡是以“.”或者“..”開頭的都是相對路徑查看服務器基本信息:cat /proc/cpuinfo 查看cpu信息cat /proc/meminfo 查看內存信息free 查看內存使用情況uptime 監控CPU情況unam