古詩詞大全網 - 成語查詢 - libevent

libevent

epoll跟select都能提供多路I/O復用的解決方案。在現在的Linux內核裏有都能夠支持,其中epoll是Linux所特有,而select則應該是POSIX所規定,壹般操作系統均有實現。

網上現在關於這兩者不同的介紹已經到處都是了。我這裏也不能多說出什麽東西,只是記錄下我看了實現代碼之後的壹些總結。

兩者的使用場景壹般是通過壹個入口能夠同時監控多路I/O。壹般使用的接口,

epool就是

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

select為:

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

通過上述兩個函數能夠將調用線程阻塞,線程變為可執行條件有兩種情況:

無任何事件發生,超時時間已過

在所控制的I/O有事件到來

epoll_wait函數中看不到相關的監控信息,因為是通過epoll_ctl已經加入,而select之間在函數調用中由(fd_set *readfds, fd_set *writefds, fd_set *exceptfds)傳入。epoll_wait飯互結果通過events返回,而select的傳入參數也是傳出參數。兩者傳出參數均表示發生事件的對應I/O標識。

兩種方式的區別主要體現在以下幾個方面:

select所能控制的I/O數有限,這主要是因為fd_set數據結構是壹個有大小的,相當與壹個定長所數組。

select每次都需要重新設置所要監控的fd_set(因為調用之後會改變其內容),這增加了程序開銷。

select的性能要比epoll差,具體原因會在後續內容中詳細說明。

嗯,說道這個為什麽select要差,那就要從這個select API說起了。這個傳進去壹個數組,內部實現也不知道那個有哪個沒有,所以要遍歷壹遍。假設說我只監控壹個文件描述符,但是他是1000。那麽select需要遍歷前999個之後再來poll這個1000的文件描述符,而epoll則不需要,因為在之前epoll_ctl的調用過程中,已經維護了壹個隊列,所以直接等待事件到來就可以了。

Linux中select此段相關代碼為:

/* 遍歷所有傳入的fd_set */

for (i = 0; i < n; ++rinp, ++routp, ++rexp) {

unsigned long in, out, ex, all_bits, bit = 1, mask, j;

unsigned long res_in = 0, res_out = 0, res_ex = 0;

const struct file_operations *f_op = NULL;

struct file *file = NULL;

in = *inp++; out = *outp++; ex = *exp++;

all_bits = in | out | ex;

/* 此處跳無需監控的fd, 白白的浪費時間啊…… */

if (all_bits == 0) {

i += __NFDBITS;

continue;

}

/* 後續進行壹些相關操作 */

}

而epoll則無需進行此類操作,直接檢測內部維護的壹個就緒隊列,如果隊列有內容,說明有I/O就緒,那麽直接賦值返回內容,成功返回,如果沒有成功,那麽睡眠,等待就緒隊列非空。

通過這個兩者的比較,其實兩者的差距啊,大部分是因為這個API設計所決定的,select就設計成這樣壹個API,內部再怎麽優化也只能是這麽個爛樣子,而epoll這樣維護與等待分離,靈活多變,最後也就帶來了相對的高性能,以及可擴展性。