select設計模式
監聽一組句柄fd_set,第一次調用的時候循環全部句柄對應的驅動函數xx_poll,socket的話就是sock_poll。
循環遍歷完畢以後會若是發現有可用的(活躍狀態的)fd,則返回,返回的時候會返回活躍的fd個數,同時會
把不活躍的fd在fd_set移除。若是循環fd_set一遍之後發現沒有活躍的fd。假設此時socket在非阻塞模式下,
那麼select會重複遍歷這些fd_set直到超過了咱們設置的時間限制,但是在阻塞模式下呢,怎麼處理?
阻塞模式下咱們沒有必要設置超時限制,由於若是第一遍變量fd_set發現沒有活躍的fd那麼,這條進程會被
掛起的。進程怎麼被激活呢,實際上進程是被掛在了n個fd的等待隊列裏,只要一個fd準備就緒那麼進程就
會被喚醒,網卡接收了數據包發送一箇中斷給內核,內核處理這個中斷,喚醒進程,這裏是涉及到軟中斷的
,有興趣能夠看看。select被喚醒以後,並不知道是哪個fd把本身喚醒的,因此還要來一個遍歷,過程跟
上面說的是同樣的。select阻塞到返回經歷的內核和用戶空間的拷貝,先把fd_set從用戶空間拷貝給內核空
間,內核空間處理完畢以後把活動的fd再copy回來。其實後者還好,可是前者當fd_set百萬級別的時候,就
費勁了。箇中細節我並不是徹底瞭解,但是單看這個設計模式就覺着有效率問題了,感受真的很傻,copy不說,
這個遍歷全部描述符實在是太費勁了,並且每次在調用開始時,要把當前進程放入各個文件描述符的等待隊列
在調用結束後,又把進程從各個等待隊列中刪除。
epoll
epoll的實現說白了,就是新發明了一個迷你文件系統:eventpollfs。其實,這須要sock_poll驅動的輔助
才能完成這樣的設計,話說若是關注到epoll和select了,還不瞭解一下驅動的話,是看不動的。epoll的一
組函數:epoll_create,epoll_ctl,epoll_wait,就這三個函數完成百萬併發醉了麼?簡單的能夠這麼理解,
epoll_create : 建立紅黑樹和就緒列表,實際他建立了一個專屬於epoll文件系統的一個文件
epoll_ctl :向紅黑樹添加socket句柄,向內核註冊回調函數,用於當中斷事件來臨時,向準備就緒列表插
入數據
epoll_wait :返回準備就緒列表的數據
準備就緒列表怎麼維護的呢?當咱們在作epoll_ctl時會給內核中斷處理程序註冊一個函數,告訴內核,若是這個句柄的中斷到了,就把他放在準備就緒list鏈表裏。
另外還有就是epoll的水平觸發和邊緣觸發
水平觸發level-triggered lT 只要知足條件就觸發一個事件,只要數據沒有被獲取,內核會不斷通知你
邊緣出發edge-triggered ET 每當狀態變化,觸發一個事件。
具體例子吧,假設一個socket原本無數據可讀,如今來了100個字節,這時候狀態發生了變化,那麼水平觸發
和邊緣出發都會被激活,但是呢,咱們此次只讀了20個字節,對於水平觸發來講,還有80字節沒讀呢,再去監
聽端口仍是應該被觸發可讀調用,可是對於邊緣觸發的設計模式來講,由於剛纔狀態是從不可讀變爲可讀,而如今是可讀維持爲可讀,狀態沒變化,因此如今不必再觸發可讀調用了。
這只是種設計思想而已,好比epoll,在就緒列表作點名堂就能夠了,好比一個socket,假設沒有被讀完,提供是否再次放回到就緒列表中這兩個選項,就成了兩種觸發了。