epoll使用詳解

来源:https://www.cnblogs.com/ailumiyana/archive/2018/12/27/10183368.html
-Advertisement-
Play Games

[TOC] epoll介紹 epoll的行為與poll(2)相似,監視多個有IO事件的文件描述符。epoll除了提供select/poll那種IO事件的水平觸發(Level Triggered)外,還提供了邊緣觸發(Edge Triggered),這就使得用戶空間程式有可能緩存IO狀態,減少epol ...


目錄


epoll介紹

epoll的行為與poll(2)相似,監視多個有IO事件的文件描述符。epoll除了提供select/poll那種IO事件的水平觸發(Level Triggered)外,還提供了邊緣觸發(Edge Triggered),這就使得用戶空間程式有可能緩存IO狀態,減少epoll_wait/epoll_pwait的調用,提高應用程式效率。

epoll_create(2) 創建一個新的epoll實例,並返回一個引用該實例的文件描述符
epoll_ctl(2) 創建epoll實例後,註冊對感興趣的文件描述符。當前註冊在epoll實例上的文件描述符集被稱為epoll集合。
epoll_wait(2) 等待I/O事件,如果當前沒有事件可用,則阻塞調用線程。

水平觸發邊沿觸發
epoll事件分佈介面既可以表現為邊緣觸發(ET),也可以表現為水平觸發(LT)。這兩種機制的區別
可以這樣描述。假設有這種情況發生:

  1. 表示管道(rfd)的讀側的文件描述符在epoll實例上註冊。
  2. 管道寫入器在管道的寫入端寫入2 kB的數據。
  3. 調用epoll_wait(2)將返回rfd作為就緒文件描述符。
  4. 管道讀取器從rfd讀取1kb的數據。
  5. epoll_wait(2)調用完成。

如果使用邊緣觸發標誌將rfd文件描述符註冊到epoll介面,那麼第五步的epoll_wait(2)的調用可能會掛起,儘管文件輸入緩衝區仍然有1kb數據可讀;同時,遠程對等端可能正在期望基於它已發送的數據的應答。這樣做的原因是,只有在被監視文件描述符上發生更改時,邊緣觸發模式才交付事件。因此,在步驟5中,調用者可能會以等待那些仍在輸入緩衝區中的數據的狀態下結束。
在上面的例子中,將生成rfd上的一個事件,因為在2中完成了寫入,而在3中使用了該事件。由於在4中完成的讀操作不會消耗整個緩衝區數據,所以在步驟5中完成的對epoll_wait(2)的調用可能會無限期阻塞。

使用EPOLLET標誌的應用程式應該使用非阻塞文件描述符,以避免在處理多個文件描述符時出現有阻塞的讀寫饑餓任務。建議使用epoll作為邊沿觸發(EPOLLET)介面的方式如下:
i、 具有非阻塞文件描述符
ii、只有在read(2)或write(2)返回EAGAIN後才等待事件。
相反,當EPOLLET作為水平觸發介面使用時(預設情況下,沒有指定EPOLLET), epoll只是一個更快的poll(2),並且可以在使用後者的任何地方使用,因為它具有相同的語義。

Epoll的優點:

1、支持一個進程打開大數目的socket描述符(FD)

select能打開的文件描述符有一定的限制,FD_SETSIZE設置,預設值是2048,有兩種解決方法,1、修改它的值,然後重新編譯內核。2、使用多進程加入要併發20w個客戶,那麼就要開100進程;epoll則沒有這個限制,它所支持的FD上限是最大可以打開文件的數目,這個數字一般遠大於2048,舉個例子,在1GB記憶體的機器上大約是2萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統記憶體關係很大。

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

select/poll採用輪詢的方式掃描文件描述符,文件描述符數量越多,性能越差;內核 / 用戶空間記憶體拷貝問題,select/poll需要複製大量的句柄數據結構,產生巨大的開銷;select/poll返回的是含有整個句柄的數組,應用程式需要遍歷整個數組才能發現哪些句柄發生了事件,導致效率呈現線性下降。但是epoll不存在這個問題,它只會對"活躍"的socket進行操作---這是因為在內核實現中epoll是根據每個fd上面的callback函數實現的。

3、支持邊緣觸發模式

select/poll的觸發方式是水平觸發,應用程式如果沒有完成對一個已經就緒的文件描述符進行IO操作,那麼之後每次select/poll調用還是會將這些文件描述符通知進程。

4、使用mmap加速內核與用戶空間的消息傳遞。

select/poll和epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的記憶體拷貝很重要,在這點上,select/poll需要複製整個FD數組,產生巨大的開銷;而epoll是通過內核於用戶空間mmap同一塊記憶體實現的。

epoll的系統調用

epoll_create

int epoll_create(int size);
int epoll_create1(int flags);
創建一個epoll的句柄。自從linux2.6.8之後,size參數是被忽略的,更推薦使用epoll_crete1(0)來替代,flags可以設置EPOLL_CLOEXEC標誌

epoll_ctl

#include <sys/epoll.h>

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

該系統調用對文件描述符epfd引用的epoll(7)實例執行控制操作。它請求對目標文件描述符fd執行操作op。

epfd : epoll_create創建的文件描述符.

op :參數的有效參數為:
EPOLL_CTL_ADD
在文件描述符epfd引用的epoll實例上註冊目標文件描述符fd。
EPOLL_CTL_MOD
修改已註冊描述符fd關聯的事件。
EPOLL_CTL_DEL
從epfd引用的epoll實例中刪除(取消註冊)目標文件描述符fd。該事件將被忽略,並且可以是NULL

fd :待監聽的fd

epoll_event : 描述鏈接到文件描述符fd的對象,它的定義如下

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:關聯的文件描述符被掛斷;

EPOLLRDHUP:流套接字對等關閉連接,或半關閉寫。(當使用邊緣觸發監視時,此標記對於編寫簡單代碼檢測對等端是否關閉特別有用。2.6.17引入)

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

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

它們在內核頭文件里的定義如下:

 33 
 34 enum EPOLL_EVENTS
 35   {
 36     EPOLLIN = 0x001,
 37 #define EPOLLIN EPOLLIN
 38     EPOLLPRI = 0x002,
 39 #define EPOLLPRI EPOLLPRI
 40     EPOLLOUT = 0x004,
 41 #define EPOLLOUT EPOLLOUT
 42     EPOLLRDNORM = 0x040,
 43 #define EPOLLRDNORM EPOLLRDNORM
 44     EPOLLRDBAND = 0x080,
 45 #define EPOLLRDBAND EPOLLRDBAND
 46     EPOLLWRNORM = 0x100,
 47 #define EPOLLWRNORM EPOLLWRNORM
 48     EPOLLWRBAND = 0x200,
 49 #define EPOLLWRBAND EPOLLWRBAND
 50     EPOLLMSG = 0x400,
 51 #define EPOLLMSG EPOLLMSG
 52     EPOLLERR = 0x008,
 53 #define EPOLLERR EPOLLERR
 54     EPOLLHUP = 0x010,
 55 #define EPOLLHUP EPOLLHUP
 56     EPOLLRDHUP = 0x2000,
 57 #define EPOLLRDHUP EPOLLRDHUP
 58     EPOLLEXCLUSIVE = 1u << 28,
 59 #define EPOLLEXCLUSIVE EPOLLEXCLUSIVE
 60     EPOLLWAKEUP = 1u << 29,
 61 #define EPOLLWAKEUP EPOLLWAKEUP
 62     EPOLLONESHOT = 1u << 30,
 63 #define EPOLLONESHOT EPOLLONESHOT
 64     EPOLLET = 1u << 31
 65 #define EPOLLET EPOLLET
 66   };
 67 
 68 
 69 /* Valid opcodes ( "op" parameter ) to issue to epoll_ctl().  */
 70 #define EPOLL_CTL_ADD 1 /* Add a file descriptor to the interface.  */
 71 #define EPOLL_CTL_DEL 2 /* Remove a file descriptor from the interface.  */
 72 #define EPOLL_CTL_MOD 3 /* Change file descriptor epoll_event structure.  */

epoll_wait

       #include <sys/epoll.h>

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

等待在epoll監控的事件中已經發生的事件。
epfd : epoll_create() 的返回值.
events : 分配好的epoll_event結構體數組,epoll將會把發生的事件賦值到events數組中(events不可以是空指針,內核只負責把數據複製到這個events數組中,不會去幫助我們在用戶態中分配記憶體)
maxevents : maxevents告知內核這個events有多大,這個 maxevents的值大於0(否則Error :Invalid argument)
timeout : 超時時間(毫秒,0會立即返回,-1將不確定,也有說法說是永久阻塞)。如果函數調用成功,返回對應I/O上已準備好的文件描述符數目,如返回0表示已超時,它會阻塞直到

  • 一個文件描述符有事件發生;
  • 信號處理器中斷;
  • 超時;

epoll示常式序

此程式簡單測試一下三個API,註冊標準輸出的描述符到epoll,監視標準輸出的讀事件,觸發後回顯一遍,quit退出程式.

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <sys/epoll.h>
#include <vector>

typedef std::vector<struct epoll_event> PollFdList;

int main(int argc ,char **argv)
{

  int fd;
  char buf[1024];
  int i,res,real_read, maxfd;

  if((fd=open("/dev/stdin",O_RDONLY|O_NONBLOCK)) < 0)
  {
    fprintf(stderr,"open data1 error:%s",strerror(errno));
    return 1;
  }

  PollFdList m_pollfds;
  int epfd = epoll_create1(EPOLL_CLOEXEC);

  struct epoll_event ev;
  ev.events = EPOLLIN | EPOLLPRI;
  ev.data.fd = fd;

  epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
  
  m_pollfds.resize(1024);

  while(1)
  {
    int ret = epoll_wait(epfd, m_pollfds.data(), m_pollfds.size(), 5000);
    if (ret < 0)
    {
      printf("ePoll error : %s\n",strerror(errno));
      return 1;
    }

    if(ret == 0){
      printf("ePoll timeout\n");
      continue;
    }

    for (i = 0; i< 1; i++)
    {
      if (m_pollfds[i].events & EPOLLIN)
      {
        memset(buf, 0, 1024);
        real_read = read(m_pollfds[i].data.fd, buf, 1024);
        if (real_read < 0)
        {
          if (errno != EAGAIN)
          {
            printf("read eror : %s\n",strerror(errno));
            continue;
          }
        }
        else if (!real_read)
        {
          close(m_pollfds[i].data.fd);
          m_pollfds[i].events = 0;
        }
        else
        {
          if (i == 0)
          {
            buf[real_read] = '\0';
            printf("%s", buf);
            if ((buf[0] == 'q') || (buf[0] == 'Q'))
            {
              printf("quit\n");
              return 1;
            }
          }
          else
          {
            buf[real_read] = '\0';
            printf("%s", buf);
          }
        }
      }
    }
  }

  exit(0);
}
./test
hello
hello
hello epoll
hello epoll
ePoll timeout
quit
quit
quit

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 為什麼要有進程優先順序?這似乎不用過多的解釋,畢竟自從多任務操作系統誕生以來,進程執行占用cpu的能力就是一個必須要可以人為控制的事情。因為有的進程相對重要,而有的進程則沒那麼重要。 本文作者:鄒立巍 Linux系統技術專家。目前在騰訊SNG社交網路運營部 計算資源平臺組,負責內部私有雲平臺的建設和架 ...
  • 最初的2小時,你會愛上Docker,對原理和使用流程有個最基本的理解,避免滿世界無頭蒼蠅式找資料。本人反對暴風驟雨式多管齊下狂轟濫炸的學習方式,提倡迭代學習法,就是先知道怎麼玩,有個感性認識,再深入學習高級用法,深層原理,一輪輪迭代。堅決反對一上來就搞幾百頁厚的東西把人腦子弄亂。 Docker是什麼 ...
  • 一.Logrotate工具介紹 Logrotate是一個日誌文件管理工具,它是Linux預設自帶的一個日誌切割工具。用來把舊文件輪轉、壓縮、刪除,並且創建新的日誌文件。我們可以根據日誌文件的大小、天數等來轉儲,便於對日誌文件管理,一般都是通過cron計劃任務來完成的,讓日誌切割實現按小時分割,按天分 ...
  • 該按鍵庫使用 C 語言編寫,驅動與應用程式解耦,便於靈活應用,比如用戶可以方便地在應用層增加按鍵中斷、處理按鍵功耗、定義按鍵事件處理方式,而無需修改 FlexibleButton 庫中的代碼。另外,使用 C 語言標準庫 API 編寫,也使得該按鍵庫可以無縫相容任意的處理器平臺,並且支持任意 OS 和... ...
  • 1 前景回顧 1.1 內核映射區 儘管vmalloc函數族可用於從高端記憶體域向內核映射頁幀(這些在內核空間中通常是無法直接看到的), 但這並不是這些函數的實際用途. 重要的是強調以下事實 : 內核提供了其他函數用於將 頁幀顯式映射到內核空間, 這些函數與vmalloc機制無關. 因此, 這就造成了混 ...
  • 最後更新時間:2018年12月27日Docker使用代理上網去 pull 各類 images,需要做如下配置:創建目錄: /etc/systemd/system/docker.service.d寫入配置:http-proxy.conf [root@localhost docker.service.d... ...
  • 1. centos 6.x 重置root密碼 開機按esc 到下圖 按 e 鍵進入編輯模式,通常選擇第二項後按 e 鍵編輯此項 進入該編輯模式後,在quiet後面輸入 simple 或者 1 然後回車 按b鍵進入單用戶模式 passwd root 輸入2次新密碼 最後 reboot 重啟 2. ce ...
  • LAMP指的是 Linux + Apatch + MySQL / MariaDB + PHP/Perl/Python 一套搭建網站伺服器的開源軟體組合。工作原理圖如下: 下麵開始環境搭建 1 Linux的安裝 Linux 用的是 CentOS 7版本,進入安裝界面後,如下圖 選擇最小安裝,如下圖 對 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...