select\poll\epoll\kqueue\IOCP的區別及用法
先來看看linux manual對三者的介紹
1、select
select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation (e.g., input possible). A file descriptor is considered ready if it is possible to perform a corresponding I/O operation (e.g., read(2) without blocking, or a sufficiently small write(2)).
select() can monitor only file descriptors numbers that are less than FD_SETSIZE; poll(2) does not have this limitation
翻譯一下:
select()和pselect()允許監聽多個檔案描述符,等待一個或多個某些IO操作檔案描述符標為“就緒”狀態(比如可輸入)。如果一個檔案描述符可以完成一個相應的IO操作(例如不阻塞的read()或者小資料量的write())。
select()只能監聽少於FD_SETSIZE數量的檔案描述符,poll()沒有此限制,FD_SETSIZE一般為1024
select()的特點:
(1)使用fd_set這一結構體儲存檔案描述符,最大可監聽的fd數量不能超過FD_SETSIZE
(2)每次呼叫select()都要將fd_set從使用者空間拷貝到核心空間
(3)每次都要遍歷整個fd_set,時間複雜度O(n),當fd數量很多時,耗費時間
(4)只能電平觸發,如果在下一次呼叫select()時,上一次的事件處理函式還沒處理完,即使沒有新的事件,select()仍然會返回“就緒”
2、poll
poll() performs a similar task to select(2): it waits for one of a set of file descriptors to become ready to perform I/O.
The set of file descriptors to be monitored is specified in the fds argument, which is an array of structures of the following form:
poll與select相似,唯一的改進就是把fd_set改成了pollfd,
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
函式原型:
int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *tmo_p, const sigset_t *sigmask);
從上面的函式宣告可以看出,呼叫poll()傳遞的引數是pollfd指標和fd數量,因此poll不再受FD_SETSIZE的限制,但是其他的問題依然存在(網上的文章都說poll用了連結串列來解決FD_SETSIZE限制,這個說法不對)
3、epoll()
The epoll API performs a similar task to poll(2): monitoring multiple file descriptors to see if I/O is possible on any of them. The epoll API can be used either as an edge-triggered or a level-triggered interface and scales well to large numbers of watched file descriptors.
翻譯一下:
epoll API的功能類似於poll():監聽多個檔案描述符是否可操作(讀寫)epoll可使用邊沿觸發或者電平觸發並且可以良好的伸縮以監聽大量的檔案描述符。
epoll使用了三個關鍵的技術解決select()和poll()的不足:mmap、紅黑樹、回撥方式檢測就緒事件
(1)使用了mmap將要監聽的檔案描述符從使用者空間對映到核心空間,避免了拷貝,其實2.5核心的epoll是需要手動mmap的,2.6之後才自動mmap
(2)使用紅黑樹來儲存大量的fd集合,可以快速的新增、刪除、修改
(3)將要監聽的事件與裝置驅動建立回撥關係,這樣就不用每次都遍歷所有fd,epoll_wait()得以快速返回,另外還能實現邊沿觸發
因為以上原因,epoll可以監聽大量的fd,比如幾百萬,但仍可以快速返回
不過,epoll並不是完美的,epoll的返回結果儲存到一個雙向連結串列中,需要從核心空間拷貝到使用者空間才能取出監聽結果,如果活躍的fd數量很大,效率會下降,特別是監聽的fd數量不是很多,但非常活躍的情況,效能可能與select、poll差不多,甚至不如,這點在liveMedia555裡面有體現。
epoll支援邊沿觸發,所以可以將epoll與執行緒池結合使用,於是誕生了另一種非同步IO模型IOCP!
4、IOCP
IOCP是Input/Output Completion Port的縮寫,是Windows、AIX、Solaris 系統中的一種高效非同步IO模型,epoll只是告知呼叫者有哪些事件就緒,需要呼叫者自己去進行資料讀寫等操作,而IOCP除此之外還維護了一個執行緒池用於處理就緒的IO請求,因此IOCP可以認為是唯一一個真正意義上的非同步IO
IOCP的程式設計複雜度要比epoll高些,更多IOCP介紹請參考MSDN文件和《WIndows核心程式設計》第10章
https://docs.microsoft.com/zh-cn/windows/desktop/FileIO/i-o-completion-ports
5、Kqueue
kqueue是FreeBSD系統下的一種事件通過模型,細節請參考我的另外兩篇文章:
1、Kqueue用法
https://blog.csdn.net/Namcodream521/article/details/83032615
2、Kqueu的簡單例項
https://blog.csdn.net/Namcodream521/article/details/83032670