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

1. epoll概念

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

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

I/O事件

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

  • 事件

    • 可讀事件, 當文件描述符關聯的內核讀緩衝區可讀,則觸發可讀事件
      什麼是可讀呢? 就是內核緩衝區非空,有數據可以讀取
    • 可寫事件, 當文件描述符關聯的內核寫緩衝區可寫,則觸發可寫事件
      什麼是可寫呢?就是內核緩衝區不滿,有空閒空間可以寫入

通知機制

  • 通知機制,就是當事件發生的時候,去通知他
  • 通知機制的反面,就是輪詢機制

以上兩點結合起來理解

epoll是一種當文件描述符的內核緩衝區非空的時候,發出可讀信號進行通知,當寫緩衝區不滿的時候,發出可寫信號通知的機制

2. 水平觸發與邊緣觸發

水平觸發(level-trggered)

  • 只要文件描述符關聯的讀內核緩衝區非空,有數據可以讀取,就一直髮出可讀信號進行通知,
  • 當文件描述符關聯的內核寫緩衝區不滿,有空間可以寫入,就一直髮出可寫信號進行通知

邊緣觸發(edge-triggered)

  • 當文件描述符關聯的讀內核緩衝區由空轉化爲非空的時候,則發出可讀信號進行通知,
  • 當文件描述符關聯的內核寫緩衝區由滿轉化爲不滿的時候,則發出可寫信號進行通知

兩者的區別在哪裏呢?水平觸發是隻要讀緩衝區有數據,就會一直觸發可讀信號,而邊緣觸發僅僅在空變爲非空的時候通知一次,舉個例子:

  1. 讀緩衝區剛開始是空的
  2. 讀緩衝區寫入2KB數據
  3. 水平觸發和邊緣觸發模式此時都會發出可讀信號
  4. 收到信號通知後,讀取了1kb的數據,讀緩衝區還剩餘1KB數據
  5. 水平觸發會再次進行通知,而邊緣觸發不會再進行通知

所以邊緣觸發需要一次性的把緩衝區的數據讀完爲止,也就是一直讀,直到讀到EGAIN爲止,EGAIN說明緩衝區已經空了,因爲這一點,邊緣觸發需要設置文件句柄爲非阻塞

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

3. epoll接口介紹

  • epoll_create

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

      int epoll_create(int size);
      Man文檔中說明了在老的內核版本中,入參size用來指出創建的內部數據結構的大小,目前已經可以動態調整,但是爲了兼容老的版本,所以仍然保留,這個size其實意義已經不大

  • epoll_ctl

    • 添加,修改,或者刪除 註冊到epoll實例中的文件描述符上的監控事件

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

      • epfd: 通過epoll_create創建的文件描述符
      • op:操作類型
        EPOLL_CTL_ADD, 爲相應fd添加事件
        EPOLL_CTL_MOD, 修改fd的事件
        EPOLL_CTL_DEL,刪除fd上的某些事件
      • fd: 操作的目標文件描述符
      • 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: 邊緣觸發,默認是水平觸發
        
  • epoll_wait

    int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
    等待註冊的事件發生,返回事件的數目,並將觸發的事件寫入events數組中

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



作者:ld9183
鏈接:https://www.jianshu.com/p/41dc33b97419
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章