歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

select/poll/epoll對比分析

select/poll/epoll都是IO多路復用機制,可以同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則立刻通知相應程序進行讀或寫操作。本質上select/poll/epoll都是同步I/O,即讀寫是阻塞的。

一、select

原型:

int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

從select函數監控3類文件描述符:writefds、readfds、exceptfds。調用select函數後會阻塞,直到描述符准備就緒(有數據可讀、可寫、或者出現異常)或者超時,函數便返回。當select函數返回後,可以通過遍歷描述符集合,找到就緒的描述符。

select缺點

  • 單進程能夠監控的文件描述符的數量存在最大限制,在Linux上一般為1024,可以通過修改宏定義增大上限,但同樣存在效率低的弱勢;
  • IO效隨著監視的描述符數量的增長,其效率也會線性下降;

二、poll

原型:

int poll (struct pollfd *fds, unsigned int nfds, int timeout);

其中pollfd表示監視的描述符集合,如下

struct pollfd {
    int fd; //文件描述符
    short events; //監視的請求事件
    short revents; //已發生的事件
};

pollfd結構包含了要監視的event和發生的event,並且pollfd並沒有最大數量限制(但數量過大同樣會導致性下降)。 和select函數一樣,當poll函數返回後,可以通過遍歷描述符集合,找到就緒的描述符。

從上面看,select和poll都需要在返回後,通過遍歷文件描述符來獲取已經就緒的socket。事實上,同時連接的大量客戶端在一時刻可能只有很少的處於就緒狀態,因此隨著監視的描述符數量的增長,其效率也會線性下降。

三、epoll

epoll是在2.6內核中提出的,是select和poll的增強版。相對於select和poll來說,epoll更加靈活,沒有描述符數量限制。epoll使用一個文件描述符管理多個描述符,將用戶空間的文件描述符的事件存放到內核的一個事件表中,這樣在用戶空間和內核空間的copy只需一次。epoll機制是Linux最高效的I/O復用機制,在一處等待多個文件句柄的I/O事件。

select/poll都只有一個方法,而epoll的操作過程有3個方法,分別是epoll_create()epoll_ctl()epoll_wait()

3.1 epoll_create()

int epoll_create(int size);

用於創建一個epoll的句柄,size是指監聽的描述符個數, 現在內核支持動態擴展,該值的意義僅僅是初次分配的fd個數,後面空間不夠時會動態擴容。 當創建完epoll句柄後,占用一個fd值.

ls /proc/<pid>/fd/  //可通過終端執行,看到該fd

使用完epoll後,必須調用close()關閉,否則可能導致fd被耗盡。

3.2 epoll_ctl()

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

用於對需要監聽的文件描述符(fd)執行op操作,比如將fd加入到epoll句柄。

  • epfd:是epoll_create()的返回值;
  • op:表示op操作,用三個宏來表示,分別代表添加、刪除和修改對fd的監聽事件;
    • EPOLL_CTL_ADD(添加)
    • EPOLL_CTL_DEL(刪除)
    • EPOLL_CTL_MOD(修改)
  • fd:需要監聽的文件描述符;
  • epoll_event:需要監聽的事件,struct epoll_event結構如下:

      struct epoll_event {
        __uint32_t events;  /* Epoll事件 */
        epoll_data_t data;  /*用戶可用數據*/
      };
    

    events可取值:(表示對應的文件描述符的操作)

    • EPOLLIN :可讀(包括對端SOCKET正常關閉);
    • EPOLLOUT:可寫;
    • EPOLLERR:錯誤;
    • EPOLLHUP:中斷;
    • EPOLLPRI:高優先級的可讀(這裡應該表示有帶外數據到來);
    • EPOLLET: 將EPOLL設為邊緣觸發模式,這是相對於水平觸發來說的。
    • EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之後就不再監聽該事件

3.3 epoll_wait()

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
  • epfd:等待epfd上的io事件,最多返回maxevents個事件;
  • events:用來從內核得到事件的集合;
  • maxevents:events數量,該maxevents值不能大於創建epoll_create()時的size;
  • timeout:超時時間(毫秒,0會立即返回)。

該函數返回需要處理的事件數目,如返回0表示已超時。

四、對比

在 select/poll中,進程只有在調用一定的方法後,內核才對所有監視的文件描述符進行掃描,而epoll事先通過epoll_ctl()來注冊一個文件描述符,一旦基於某個文件描述符就緒時,內核會采用類似callback的回調機制,迅速激活這個文件描述符,當進程調用epoll_wait() 時便得到通知。(此處去掉了遍歷文件描述符,而是通過監聽回調的的機制。這正是epoll的魅力所在。)

epoll優勢

  1. 監視的描述符數量不受限制,所支持的FD上限是最大可以打開文件的數目,具體數目可以cat /proc/sys/fs/file-max查看,一般來說這個數目和系統內存關系很大,以3G的手機來說這個值為20-30萬。

  2. IO效率不會隨著監視fd的數量增長而下降。epoll不同於select和poll輪詢的方式,而是通過每個fd定義的回調函數來實現的,只有就緒的fd才會執行回調函數。

如果沒有大量的idle-connection或者dead-connection,epoll的效率並不會比select/poll高很多,但是當遇到大量的idle-connection,就會發現epoll的效率大大高於select/poll。

Copyright © Linux教程網 All Rights Reserved