咱們經過比較select、poll和epoll處理I/O的過程來剖析其中的緣由:
1. 用戶態將文件描述符傳入內核的方式:
select:建立3個文件描述符集並拷貝到內核中,分別監聽讀、寫、異常動做。這裏受到單個進程能夠打開的fd數量限制,默認是1024。
poll:將傳入的struct pollfd結構體數組拷貝到內核中進行監聽。
epoll:執行epoll_create會在內核的高速cache區中創建一顆紅黑樹以及就緒鏈表(該鏈表存儲已經就緒的文件描述符)。接着用戶執行的epoll_ctl函數添加文件描述符會在紅黑樹上增長相應的結點。數組
2. 內核態檢測文件描述符是否可讀可寫的方式:
select:採用輪詢方式,遍歷全部fd,最後返回一個描述符讀寫操做是否就緒的mask掩碼,根據這個掩碼給fd_set賦值。
poll:一樣採用輪詢方式,查詢每一個fd的狀態,若是就緒則在等待隊列中加入一項並繼續遍歷。
epoll:採用回調機制。在執行epoll_ctl的add操做時,不只將文件描述符放到紅黑樹上,並且也註冊了回調函數,內核在檢測到某文件描述符可讀/可寫時會調用回調函數,該回調函數將文件描述符放在就緒鏈表中。數據結構
3. 如何找到就緒的文件描述符並傳遞給用戶態:
select:將以前傳入的fd_set拷貝傳出到用戶態並返回就緒的文件描述符總數。用戶態並不知道是哪些文件描述符處於就緒態,須要遍從來判斷。
poll:將以前傳入的fd數組拷貝傳出用戶態並返回就緒的文件描述符總數。用戶態並不知道是哪些文件描述符處於就緒態,須要遍從來判斷。
epoll:epoll_wait只用觀察就緒鏈表中有無數據便可,最後將鏈表的數據返回給數組並返回就緒的數量。內核將就緒的文件描述符放在傳入的數組中,因此只用遍歷依次處理便可。這裏返回的文件描述符是經過mmap讓內核和用戶空間共享同一塊內存實現傳遞的,減小了沒必要要的拷貝。socket
4. 繼續從新監聽時如何重複以上步驟:
select:將新的監聽文件描述符集合拷貝傳入內核中,繼續以上步驟。
poll:將新的struct pollfd結構體數組拷貝傳入內核中,繼續以上步驟。
epoll:無需從新構建紅黑樹,直接沿用已存在的便可。函數
經過以上步驟咱們能夠發現如下幾點:
select和poll的動做基本一致,只是poll採用鏈表來進行文件描述符的存儲,而select採用fd標註位來存放,因此select會受到最大鏈接數的限制,而poll不會。
select、poll、epoll雖然都會返回就緒的文件描述符數量。可是select和poll並不會明確指出是哪些文件描述符就緒,而epoll會。形成的區別就是,系統調用返回後,調用select和poll的程序須要遍歷監聽的整個文件描述符找到是誰處於就緒,而epoll則直接處理就好了。
select、poll都須要將有關文件描述符的數據結構拷貝進內核,最後再拷貝出來。而epoll建立的有關文件描述符的數據結構自己就存於內核態中,系統調用返回時也採用mmap共享存儲區,須要拷貝的次數大大減小。
select、poll採用輪詢的方式來檢查文件描述符是否處於就緒態,而epoll採用回調機制。形成的結果就是,隨着fd的增長,select和poll的效率會線性下降,而epoll不會受到太大影響,除非活躍的socket不少。
最後總結一下,epoll比select和poll高效的緣由主要有兩點:
1. 減小了用戶態和內核態之間的文件描述符拷貝
2. 減小了對就緒文件描述符的遍歷
---------------------
做者:Move_now
來源:CSDN
原文:https://blog.csdn.net/Move_now/article/details/71773965
版權聲明:本文爲博主原創文章,轉載請附上博文連接!.net