當前位置:編程學習大全網 - 編程語言 - select和epoll的區別 知乎

select和epoll的區別 知乎

先說下本文框架,先是問題引出,然後概括兩個機制的區別和聯系,最後介紹每個接口的用法

壹、問題引出 聯系區別

問題的引出,當需要讀兩個以上的I/O的時候,如果使用阻塞式的I/O,那麽可能長時間的阻塞在壹個描述符上面,另外的描述符雖然有數據但是不能讀出來,這樣實時性不能滿足要求,大概的解決方案有以下幾種:

1.使用多進程或者多線程,但是這種方法會造成程序的復雜,而且對與進程與線程的創建維護也需要很多的開銷。(Apache服務器是用的子進程的方式,優點可以隔離用戶)

2.用壹個進程,但是使用非阻塞的I/O讀取數據,當壹個I/O不可讀的時候立刻返回,檢查下壹個是否可讀,這種形式的循環為輪詢(polling),這種方法比較浪費CPU時間,因為大多數時間是不可讀,但是仍花費時間不斷反復執行read系統調用。

3.異步I/O(asynchronous I/O),當壹個描述符準備好的時候用壹個信號告訴進程,但是由於信號個數有限,多個描述符時不適用。

4.壹種較好的方式為I/O多路轉接(I/O multiplexing)(貌似也翻譯多路復用),先構造壹張有關描述符的列表(epoll中為隊列),然後調用壹個函數,直到這些描述符中的壹個準備好時才返回,返回時告訴進程哪些I/O就緒。select和epoll這兩個機制都是多路I/O機制的解決方案,select為POSIX標準中的,而epoll為Linux所特有的。

區別(epoll相對select優點)主要有三:

1.select的句柄數目受限,在linux/posix_types.h頭文件有這樣的聲明:#define __FD_SETSIZE 1024 表示select最多同時監聽1024個fd。而epoll沒有,它的限制是最大的打開文件句柄數目。

2.epoll的最大好處是不會隨著FD的數目增長而降低效率,在selec中采用輪詢處理,其中的數據結構類似壹個數組的數據結構,而epoll是維護壹個隊列,直接看隊列是不是空就可以了。epoll只會對"活躍"的socket進行操作---這是因為在內核實現中epoll是根據每個fd上面的callback函數實現的。那麽,只有"活躍"的socket才會主動的去調用 callback函數(把這個句柄加入隊列),其他idle狀態句柄則不會,在這點上,epoll實現了壹個"偽"AIO。但是如果絕大部分的I/O都是“活躍的”,每個I/O端口使用率很高的話,epoll效率不壹定比select高(可能是要維護隊列復雜)。

3.使用mmap加速內核與用戶空間的消息傳遞。無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝就很重要,在這點上,epoll是通過內核於用戶空間mmap同壹塊內存實現的。

二、接口

1)select

1. int select(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict tvptr);

struct timeval{

long tv_sec;

long tv_usec;

}

有三種情況:tvptr == NULL 永遠等待;tvptr->tv_sec == 0 && tvptr->tv_usec == 0 完全不等待;不等於0的時候為等待的時間。select的三個指針都可以為空,這時候select提供了壹種比sleep更精確的定時器。註意select的第壹個參數maxfdp1並不是描述符的個數,而是最大的描述符加1,壹是起限制作用,防止出錯,二來可以給內核輪詢的時候提供壹個上屆,提高效率。select返回-1表示出錯,0表示超時,返回正值是所有的已經準備好的描述符個數(同壹個描述符如果讀和寫都準備好,對結果影響是+2)。

2.int FD_ISSET(int fd, fd_set *fdset); fd在描述符集合中非0,否則返回0

3.int FD_CLR(int fd, fd_set *fd_set); int FD_SET(int fd, fd_set *fdset) ;int FD_ZERO(fd_set *fdset);

用壹段linux 中man裏的話“FD_ZERO() clears a set.FD_SET() and FD_CLR() respectively add and remove a given file descriptor from a set. FD_ISSET() tests to see if a file descriptor is part of the set; this is useful after select() returns.”這幾個函數與描述符的0和1沒關系,只是添加刪除檢測描述符是否在set中。

2)epoll

1.int epoll_create(int size);

創建壹個epoll的句柄,size用來告訴內核這個監聽的數目壹***有多大。這個參數不同於select()中的第壹個參數,給出最大監聽的fd+1的值。需要註意的是,當創建好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結構如下:

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)模式,這是相對於水平觸發(Level Triggered)來說的。

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

關於epoll工作模式ET,LT

LT(level triggered)是缺省的工作方式,並且同時支持block和no-block socket.在這種做法中,內核告訴妳壹個文件描述符是否就緒了,然後妳可以對這個就緒的fd進行IO操作。如果妳不作任何操作,內核還是會繼續通知妳的,所以,這種模式編程出錯誤可能性要小壹點。傳統的select/poll都是這種模型的代表.

ET (edge-triggered)是高速工作方式,只支持no-block socket。在這種模式下,當描述符從未就緒變為就緒時,內核通過epoll告訴妳。然後它會假設妳知道文件描述符已經就緒,並且不會再為那個文件描述符發送更多的就緒通知,直到妳做了某些操作導致那個文件描述符不再為就緒狀態了,但是請註意,如果壹直不對這個fd作IO操作(從而導致它再次變成未就緒),內核不會發送更多的通知(only once)

3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)

等待事件的產生,類似於select()調用。參數events用來從內核得到事件的集合,maxevents告之內核這個events有多大,這個maxevents的值不能大於創建epoll_create()時的size,參數timeout是超時時間(毫秒,0會立即返回,-1永久阻塞)。該函數返回需要處理的事件數目,如返回0表示已超時。

  • 上一篇:長汀縣職業中等專業學校的教學管理
  • 下一篇:女生適合學軟件工程麽
  • copyright 2024編程學習大全網