Linux面試必知: 一句話講透epoll

1. epoll概念

在Linux的Man文檔中,我們可以看到如下定義

Epoll - I/O event notification facility
epoll是一種I/O事件通知機(jī)制

I/O事件

  • I/O
    輸入輸出(input/output),輸入輸出的對象可以是 文件(file), 網(wǎng)絡(luò)(socket), 進(jìn)程之間的管道(pipe), 在linux系統(tǒng)中,都用文件描述符(fd)來表示

  • 事件

    • 可讀事件, 當(dāng)文件描述符關(guān)聯(lián)的內(nèi)核讀緩沖區(qū)可讀,則觸發(fā)可讀事件
      什么是可讀呢? 就是內(nèi)核緩沖區(qū)非空,有數(shù)據(jù)可以讀取
    • 可寫事件, 當(dāng)文件描述符關(guān)聯(lián)的內(nèi)核寫緩沖區(qū)可寫,則觸發(fā)可寫事件
      什么是可寫呢?就是內(nèi)核緩沖區(qū)不滿,有空閑空間可以寫入

通知機(jī)制

  • 通知機(jī)制,就是當(dāng)事件發(fā)生的時候,去通知他
  • 通知機(jī)制的反面,就是輪詢機(jī)制

以上兩點結(jié)合起來理解

epoll是一種當(dāng)文件描述符的內(nèi)核緩沖區(qū)非空的時候,發(fā)出可讀信號進(jìn)行通知,當(dāng)寫緩沖區(qū)不滿的時候,發(fā)出可寫信號通知的機(jī)制

2. 水平觸發(fā)與邊緣觸發(fā)

水平觸發(fā)(level-trggered)

  • 只要文件描述符關(guān)聯(lián)的讀內(nèi)核緩沖區(qū)非空,有數(shù)據(jù)可以讀取,就一直發(fā)出可讀信號進(jìn)行通知,
  • 當(dāng)文件描述符關(guān)聯(lián)的內(nèi)核寫緩沖區(qū)不滿,有空間可以寫入,就一直發(fā)出可寫信號進(jìn)行通知

邊緣觸發(fā)(edge-triggered)

  • 當(dāng)文件描述符關(guān)聯(lián)的讀內(nèi)核緩沖區(qū)由空轉(zhuǎn)化為非空的時候,則發(fā)出可讀信號進(jìn)行通知,
  • 當(dāng)文件描述符關(guān)聯(lián)的內(nèi)核寫緩沖區(qū)由滿轉(zhuǎn)化為不滿的時候,則發(fā)出可寫信號進(jìn)行通知

兩者的區(qū)別在哪里呢?水平觸發(fā)是只要讀緩沖區(qū)有數(shù)據(jù),就會一直觸發(fā)可讀信號,而邊緣觸發(fā)僅僅在空變?yōu)榉强盏臅r候通知一次,舉個例子:

  1. 讀緩沖區(qū)剛開始是空的
  2. 讀緩沖區(qū)寫入2KB數(shù)據(jù)
  3. 水平觸發(fā)和邊緣觸發(fā)模式此時都會發(fā)出可讀信號
  4. 收到信號通知后,讀取了1kb的數(shù)據(jù),讀緩沖區(qū)還剩余1KB數(shù)據(jù)
  5. 水平觸發(fā)會再次進(jìn)行通知,而邊緣觸發(fā)不會再進(jìn)行通知

所以邊緣觸發(fā)需要一次性的把緩沖區(qū)的數(shù)據(jù)讀完為止,也就是一直讀,直到讀到EGAIN為止,EGAIN說明緩沖區(qū)已經(jīng)空了,因為這一點,邊緣觸發(fā)需要設(shè)置文件句柄為非阻塞

//水平觸發(fā)
    ret = read(fd, buf, sizeof(buf));
    
    //邊緣觸發(fā)
    while(true) {
        ret = read(fd, buf, sizeof(buf);
        if (ret == EAGAIN) break;
    }

3. epoll接口介紹

  • epoll_create

    • 創(chuàng)建epoll實例,會創(chuàng)建所需要的紅黑樹,以及就緒鏈表,以及代表epoll實例的文件句柄

      int epoll_create(int size);
      Man文檔中說明了在老的內(nèi)核版本中,入?yún)ize用來指出創(chuàng)建的內(nèi)部數(shù)據(jù)結(jié)構(gòu)的大小,目前已經(jīng)可以動態(tài)調(diào)整,但是為了兼容老的版本,所以仍然保留,這個size其實意義已經(jīng)不大

  • epoll_ctl

    • 添加,修改,或者刪除 注冊到epoll實例中的文件描述符上的監(jiān)控事件

      int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
      對于添加到epfd的文件描述符fd, 添加或者刪除或者修改, 對應(yīng)的event

      • epfd: 通過epoll_create創(chuàng)建的文件描述符
      • op:操作類型
        EPOLL_CTL_ADD, 為相應(yīng)fd添加事件
        EPOLL_CTL_MOD, 修改fd的事件
        EPOLL_CTL_DEL,刪除fd上的某些事件
      • fd: 操作的目標(biāo)文件描述符
      • 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可以是一組bit的組合
              EPOLLIN:可讀
              EPOLLOUT: 可寫
              EPOLLET: 邊緣觸發(fā),默認(rèn)是水平觸發(fā)
        
  • epoll_wait

    int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
    等待注冊的事件發(fā)生,返回事件的數(shù)目,并將觸發(fā)的事件寫入events數(shù)組中

    • epfd: epoll實例文件描述符
    • events: 數(shù)組出參,用來記錄被觸發(fā)的events,其大小應(yīng)該和maxevents一致
    • maxevents: 返回的events的最大個數(shù),如果最大個數(shù)大于實際觸發(fā)的個數(shù),則下次epoll_wait的時候仍然可以返回
    • timeout: 等待事件,毫秒為單位 -1:無限等待 0:立即返回

了解以上這些,面試是足夠了,但是對于工程中的實際使用,仍然不足,后續(xù)會以epoll在redis中的使用為例,為大家進(jìn)行剖析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容