select的原理以下:linux
用戶程序發起讀操做後,將阻塞查詢讀數據是否可用,直到內核準備好數據後,用戶程序纔會真正的讀取數據。windows
poll與select的原理類似,用戶程序都要阻塞查詢事件是否就緒,但poll沒有最大文件描述符的限制。
epoll是select和poll的改進,原理圖以下:
epoll使用「事件」的方式通知用戶程序數據就緒,而且使用內存拷貝的方式使用戶程序直接讀取內核準備好的數據,不用再讀取數據
使用Netty構建服務器時,須要指定parent線程池和child線程池,parent線程負責監聽端口,一旦有鏈接接入,則註冊到child線程池中的一個線程上,該鏈接的IO操做/任務都由該線程完成。
換句話說,一個線程會負責多個鏈接的IO操做,也就是多路複用。Netty底層是使用系統提供的select或者epoll來實現多路複用的。數組
先來科普下select/poll/epoll。服務器
服務端創建每一個鏈接,至關於打開文件,會得到對應的文件描述符(fd),相同的源IP/源端口/目標IP/目標端口對應同一個fd。app
select和poll是類似的,不同的地方是,select是使用數組,有鏈接數限制,而poll使用鏈表,無鏈接數限制。ide
監聽鏈接時,從用戶層的角度看,
(1)會構建3個fd數組,分別關注讀/寫/異常事件,設置超時時間,調用系統提供的select方法。
(2)調用select方法時,須要將fd數組傳到內核態,等待部分fd就緒後,把fd數組(包含就緒狀態)返回到用戶態
(3)用戶程序對fd數組進行遍歷,處理就緒的fd
(4)從新調用select方法。oop
能夠看出很差的地方是(1)每次都要傳入fd數組,返回整個fd數組,致使了大量在用戶空間和內核空間的相互拷貝。
(2)用戶程序仍須要遍歷fd數組才能找出就緒的fdspa
從系統層的角度看,調用select方法時
(1)遍歷fd數組,對於每一個fd,調用其對應的poll方法(由設備對應的驅動程序實現),將fd所在線程加入等待隊列,而且檢查就緒狀態,記錄感興趣的就緒狀態。
(2)若是存在感興趣的就緒狀態,直接返回
(3)若是不存在感興趣的就緒狀態,進入休眠,等待fd就緒後,會喚醒等待隊列中的線程
(4)被喚醒後,重複1-4的操做。.net
能夠看出很差的地方是每次都須要檢查全部fd。線程
epoll
epoll相對select改善了不少。
(1)在使用epoll時,首先會構建epoll對象。
(2)有鏈接接入時,會插入到epoll對象中,epoll對象裏實際是一個紅黑樹+雙向鏈表,fd插入到紅黑樹中,經過紅黑樹查找到是否重複
(3)一旦fd就緒,會觸發回調把fd的插入到就緒鏈表中,並喚醒等待隊列中的線程。
(4)調用epoll_wait方法時只須要檢查就緒鏈表,若有則返回給用戶程序,如沒有進入等待隊列。
因爲epoll把fd管理起來,不須要每次都重複傳入,並且只返回就緒的fd,所以減小了用戶空間和內核空間的相互拷貝,在fd數量龐大的時候更加高效。
Netty能夠選擇使用不一樣的多路複用技術。
NioEventLoop
NioEventLoop底層會根據系統選擇select或者epoll。若是是windows系統,則底層使用WindowsSelectorProvider(select)實現多路複用;若是是linux,則使用epoll
當爲select模式,在NioEventLoop對應的Selector中會維護着newKeys,updateKeys,cancelledKeys,分別是新增的fd,更新fd的感興趣狀態,取消fd監聽。每當鏈接接入,或者斷連,都會調用NioEventLoop的註冊/解除註冊方法,更新這幾個集合。(這裏是JDK11的實現,在JDK8中則直接更新PollArrayWrapper)
NioEventLoop在運行的時候,會不斷的監聽註冊的鏈接,核心邏輯在doSelect方法中,主要的幾個操做是
(1)processUpdateQueue,將newKeys,updateKeys,cancelledKeys中的key更新到PollArrayWrapper中
(2)subSelector.poll(),這裏實際是調用native方法,會將PollArrayWrapper的fd拷貝至readFds/writeFds/exceptFds,並監聽這些fd。
private native int poll0(long pollAddress, int numfds,
int[] readFds, int[] writeFds, int[] exceptFds, long timeout);
1
2
(3)updateSelectedKeys,遍歷freadFds/writeFds/exceptFds,將就緒的fd存儲到selectedKey中
當存在fd就緒後,doSelect方法返回,應用程序能夠遍歷selectedKey進行處理。
EpollEventLoop
EpollEventLoop底層使用epoll實現多路複用。
EpollEventLoop中會初始化epollFd、eventFd、timerFd。
epollFd是調用系統方法生成的epoll對象,後續會使用其管理全部須要監聽的fd
eventFd是用於線程通信,程序會把eventFd添加到epollFd中,監聽eventFd,一旦eventFd有操做,則會喚醒調用epoll_wait的線程。
timerFd是用於計時,一樣的監聽timerFd,一旦時間到達,則會喚醒調用epoll_wait的線程。
每當鏈接接入或者斷連,都會調用epoll_ctl_add/epoll_ctl_del方法來操做epoll對象。
在EpollEventLoop的run方法中,會調用epoll_wait來監聽全部fd,一旦有fd就緒,會拷貝至EpollEventArray中,應用程序遍歷EpollEventArray處理全部就緒事件。
————————————————
版權聲明:本文爲CSDN博主「lbl2018」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/lblblbl...