日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

前言

這篇文章讀不懂的沒關系,可以先收藏一下。筆者準備介紹完epoll和NIO等知識點,然后寫一篇JAVA網絡IO模型的介紹,這樣可以使Java網絡IO的知識體系更加地完整和嚴謹。初學者也可以等看完IO模型介紹的博客之后,再回頭看這些博客,會更加有收獲。

如果你順利啃下這篇博客,恭喜你,Nginx、redis和NIO等核心思想已經被你掌握了,可以順勢去拓展自己的理解。否則,只是孤立的看epoll,時間一長會很快忘記的。

當然,這些核心思想,筆者也會在之后的博客慢慢做詳細講解,歡迎關注

概念初探

epoll是一種I/O事件通知機制,是linux 內核實現(xiàn)IO多路復用的一個實現(xiàn)。

IO多路復用是指,在一個操作里同時監(jiān)聽多個輸入輸出源,在其中一個或多個輸入輸出源可用的時候返回,然后對其的進行讀寫操作。

IO多路復用,以后的博客會有詳細講解。

I/O

輸入輸出(input/output)的對象可以是文件(file), 網絡(socket),進程之間的管道(pipe)。在linux系統(tǒng)中,都用文件描述符(fd)來表示。

事件

  • 可讀事件,當文件描述符關聯(lián)的內核讀緩沖區(qū)可讀,則觸發(fā)可讀事件。
  • (可讀:內核緩沖區(qū)非空,有數(shù)據可以讀取)
  • 可寫事件,當文件描述符關聯(lián)的內核寫緩沖區(qū)可寫,則觸發(fā)可寫事件。
  • (可寫:內核緩沖區(qū)不滿,有空閑空間可以寫入)

通知機制

通知機制,就是當事件發(fā)生的時候,則主動通知。通知機制的反面,就是輪詢機制。

epoll的通俗解釋

結合以上三條,epoll的通俗解釋是一種當文件描述符的內核緩沖區(qū)非空的時候,發(fā)出可讀信號進行通知,當寫緩沖區(qū)不滿的時候,發(fā)出可寫信號通知的機制

epoll的API詳解

epoll的核心是3個API,核心數(shù)據結構是:1個紅黑樹和1個鏈表

徹底搞懂epoll高效運行的原理

 

epoll

1. int epoll_create(int size)

功能:

  • 內核會產生一個epoll 實例數(shù)據結構并返回一個文件描述符,這個特殊的描述符就是epoll實例的句柄,后面的兩個接口都以它為中心(即epfd形參)。

size參數(shù)表示所要監(jiān)視文件描述符的最大值,不過在后來的Linux版本中已經被棄用(同時,size不要傳0,會報invalid argument錯誤)

2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

功能:

  • 將被監(jiān)聽的描述符添加到紅黑樹或從紅黑樹中刪除或者對監(jiān)聽事件進行修改
typedef union epoll_data {
 void *ptr; /* 指向用戶自定義數(shù)據 */
 int fd; /* 注冊的文件描述符 */
 uint32_t u32; /* 32-bit integer */
 uint64_t u64; /* 64-bit integer */
} epoll_data_t;
struct epoll_event {
 uint32_t events; /* 描述epoll事件 */
 epoll_data_t data; /* 見上面的結構體 */
};

對于需要監(jiān)視的文件描述符集合,epoll_ctl對紅黑樹進行管理,紅黑樹中每個成員由描述符值和所要監(jiān)控的文件描述符指向的文件表項的引用等組成。

op參數(shù)說明操作類型:

  • EPOLL_CTL_ADD:向interest list添加一個需要監(jiān)視的描述符
  • EPOLL_CTL_DEL:從interest list中刪除一個描述符
  • EPOLL_CTL_MOD:修改interest list中一個描述符

struct epoll_event結構描述一個文件描述符的epoll行為。在使用epoll_wait函數(shù)返回處于ready狀態(tài)的描述符列表時,

  • data域是唯一能給出描述符信息的字段,所以在調用epoll_ctl加入一個需要監(jiān)測的描述符時,一定要在此域寫入描述符相關信息
  • events域是bit mask,描述一組epoll事件,在epoll_ctl調用中解釋為:描述符所期望的epoll事件,可多選。

常用的epoll事件描述如下:

  • EPOLLIN:描述符處于可讀狀態(tài)
  • EPOLLOUT:描述符處于可寫狀態(tài)
  • EPOLLET:將epoll event通知模式設置成edge triggered
  • EPOLLONESHOT:第一次進行通知,之后不再監(jiān)測
  • EPOLLHUP:本端描述符產生一個掛斷事件,默認監(jiān)測事件
  • EPOLLRDHUP:對端描述符產生一個掛斷事件
  • EPOLLPRI:由帶外數(shù)據觸發(fā)
  • EPOLLERR:描述符產生錯誤時觸發(fā),默認檢測事件

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

功能:

  • 阻塞等待注冊的事件發(fā)生,返回事件的數(shù)目,并將觸發(fā)的事件寫入events數(shù)組中。
  • events: 用來記錄被觸發(fā)的events,其大小應該和maxevents一致
  • maxevents: 返回的events的最大個數(shù)

處于ready狀態(tài)的那些文件描述符會被復制進ready list中,epoll_wait用于向用戶進程返回ready list。events和maxevents兩個參數(shù)描述一個由用戶分配的struct epoll event數(shù)組,調用返回時,內核將ready list復制到這個數(shù)組中,并將實際復制的個數(shù)作為返回值。注意,如果ready list比maxevents長,則只能復制前maxevents個成員;反之,則能夠完全復制ready list。

另外,struct epoll event結構中的events域在這里的解釋是:在被監(jiān)測的文件描述符上實際發(fā)生的事件。

參數(shù)timeout描述在函數(shù)調用中阻塞時間上限,單位是ms:

  • timeout = -1表示調用將一直阻塞,直到有文件描述符進入ready狀態(tài)或者捕獲到信號才返回;
  • timeout = 0用于非阻塞檢測是否有描述符處于ready狀態(tài),不管結果怎么樣,調用都立即返回;
  • timeout > 0表示調用將最多持續(xù)timeout時間,如果期間有檢測對象變?yōu)閞eady狀態(tài)或者捕獲到信號則返回,否則直到超時。

epoll的兩種觸發(fā)方式

epoll監(jiān)控多個文件描述符的I/O事件。epoll支持邊緣觸發(fā)(edge trigger,ET)或水平觸發(fā)(level trigger,LT),通過epoll_wait等待I/O事件,如果當前沒有可用的事件則阻塞調用線程。

select和poll只支持LT工作模式,epoll的默認的工作模式是LT模式。

1.水平觸發(fā)的時機

  1. 對于讀操作,只要緩沖內容不為空,LT模式返回讀就緒。
  2. 對于寫操作,只要緩沖區(qū)還不滿,LT模式會返回寫就緒。

當被監(jiān)控的文件描述符上有可讀寫事件發(fā)生時,epoll_wait()會通知處理程序去讀寫。如果這次沒有把數(shù)據一次性全部讀寫完(如讀寫緩沖區(qū)太小),那么下次調用 epoll_wait()時,它還會通知你在上沒讀寫完的文件描述符上繼續(xù)讀寫,當然如果你一直不去讀寫,它會一直通知你。如果系統(tǒng)中有大量你不需要讀寫的就緒文件描述符,而它們每次都會返回,這樣會大大降低處理程序檢索自己關心的就緒文件描述符的效率。

2.邊緣觸發(fā)的時機

  • 對于讀操作
  1. 當緩沖區(qū)由不可讀變?yōu)榭勺x的時候,即緩沖區(qū)由空變?yōu)椴豢盏臅r候。
  2. 當有新數(shù)據到達時,即緩沖區(qū)中的待讀數(shù)據變多的時候。
  3. 當緩沖區(qū)有數(shù)據可讀,且應用進程對相應的描述符進行EPOLL_CTL_MOD 修改EPOLLIN事件時。
  • 對于寫操作
  1. 當緩沖區(qū)由不可寫變?yōu)榭蓪憰r。
  2. 當有舊數(shù)據被發(fā)送走,即緩沖區(qū)中的內容變少的時候。
  3. 當緩沖區(qū)有空間可寫,且應用進程對相應的描述符進行EPOLL_CTL_MOD 修改EPOLLOUT事件時。

當被監(jiān)控的文件描述符上有可讀寫事件發(fā)生時,epoll_wait()會通知處理程序去讀寫。如果這次沒有把數(shù)據全部讀寫完(如讀寫緩沖區(qū)太小),那么下次調用epoll_wait()時,它不會通知你,也就是它只會通知你一次,直到該文件描述符上出現(xiàn)第二次可讀寫事件才會通知你。這種模式比水平觸發(fā)效率高,系統(tǒng)不會充斥大量你不關心的就緒文件描述符。

在ET模式下, 緩沖區(qū)從不可讀變成可讀,會喚醒應用進程,緩沖區(qū)數(shù)據變少的情況,則不會再喚醒應用進程。

舉例1:

  1. 讀緩沖區(qū)剛開始是空的
  2. 讀緩沖區(qū)寫入2KB數(shù)據
  3. 水平觸發(fā)和邊緣觸發(fā)模式此時都會發(fā)出可讀信號
  4. 收到信號通知后,讀取了1KB的數(shù)據,讀緩沖區(qū)還剩余1KB數(shù)據
  5. 水平觸發(fā)會再次進行通知,而邊緣觸發(fā)不會再進行通知

舉例2:(以脈沖的高低電平為例)

  • 水平觸發(fā):0為無數(shù)據,1為有數(shù)據。緩沖區(qū)有數(shù)據則一直為1,則一直觸發(fā)。
  • 邊緣觸發(fā)發(fā):0為無數(shù)據,1為有數(shù)據,只要在0變到1的上升沿才觸發(fā)。

JDK并沒有實現(xiàn)邊緣觸發(fā),Netty重新實現(xiàn)了epoll機制,采用邊緣觸發(fā)方式;另外像Nginx也采用邊緣觸發(fā)。

JDK在Linux已經默認使用epoll方式,但是JDK的epoll采用的是水平觸發(fā),而Netty重新實現(xiàn)了epoll機制,采用邊緣觸發(fā)方式,netty epoll transport 暴露了更多的nio沒有的配置參數(shù),如 TCP_CORK, SO_REUSEADDR等等;另外像Nginx也采用邊緣觸發(fā)。

epoll與select、poll的對比

1. 用戶態(tài)將文件描述符傳入內核的方式

  • select:創(chuàng)建3個文件描述符集并拷貝到內核中,分別監(jiān)聽讀、寫、異常動作。這里受到單個進程可以打開的fd數(shù)量限制,默認是1024。
  • poll:將傳入的struct pollfd結構體數(shù)組拷貝到內核中進行監(jiān)聽。
  • epoll:執(zhí)行epoll_create會在內核的高速cache區(qū)中建立一顆紅黑樹以及就緒鏈表(該鏈表存儲已經就緒的文件描述符)。接著用戶執(zhí)行的epoll_ctl函數(shù)添加文件描述符會在紅黑樹上增加相應的結點。

2. 內核態(tài)檢測文件描述符讀寫狀態(tài)的方式

  • select:采用輪詢方式,遍歷所有fd,最后返回一個描述符讀寫操作是否就緒的mask掩碼,根據這個掩碼給fd_set賦值。
  • poll:同樣采用輪詢方式,查詢每個fd的狀態(tài),如果就緒則在等待隊列中加入一項并繼續(xù)遍歷。
  • epoll:采用回調機制。在執(zhí)行epoll_ctl的add操作時,不僅將文件描述符放到紅黑樹上,而且也注冊了回調函數(shù),內核在檢測到某文件描述符可讀/可寫時會調用回調函數(shù),該回調函數(shù)將文件描述符放在就緒鏈表中。

3. 找到就緒的文件描述符并傳遞給用戶態(tài)的方式

  • select:將之前傳入的fd_set拷貝傳出到用戶態(tài)并返回就緒的文件描述符總數(shù)。用戶態(tài)并不知道是哪些文件描述符處于就緒態(tài),需要遍歷來判斷。
  • poll:將之前傳入的fd數(shù)組拷貝傳出用戶態(tài)并返回就緒的文件描述符總數(shù)。用戶態(tài)并不知道是哪些文件描述符處于就緒態(tài),需要遍歷來判斷。
  • epoll:epoll_wait只用觀察就緒鏈表中有無數(shù)據即可,最后將鏈表的數(shù)據返回給數(shù)組并返回就緒的數(shù)量。內核將就緒的文件描述符放在傳入的數(shù)組中,所以只用遍歷依次處理即可。這里返回的文件描述符是通過mmap讓內核和用戶空間共享同一塊內存實現(xiàn)傳遞的,減少了不必要的拷貝。

4. 重復監(jiān)聽的處理方式

  • select:將新的監(jiān)聽文件描述符集合拷貝傳入內核中,繼續(xù)以上步驟。
  • poll:將新的struct pollfd結構體數(shù)組拷貝傳入內核中,繼續(xù)以上步驟。
  • epoll:無需重新構建紅黑樹,直接沿用已存在的即可。

epoll更高效的原因

  1. select和poll的動作基本一致,只是poll采用鏈表來進行文件描述符的存儲,而select采用fd標注位來存放,所以select會受到最大連接數(shù)的限制,而poll不會。
  2. select、poll、epoll雖然都會返回就緒的文件描述符數(shù)量。但是select和poll并不會明確指出是哪些文件描述符就緒,而epoll會。造成的區(qū)別就是,系統(tǒng)調用返回后,調用select和poll的程序需要遍歷監(jiān)聽的整個文件描述符找到是誰處于就緒,而epoll則直接處理即可。
  3. select、poll都需要將有關文件描述符的數(shù)據結構拷貝進內核,最后再拷貝出來。而epoll創(chuàng)建的有關文件描述符的數(shù)據結構本身就存于內核態(tài)中,系統(tǒng)調用返回時利用mmap()文件映射內存加速與內核空間的消息傳遞:即epoll使用mmap減少復制開銷。
  4. select、poll采用輪詢的方式來檢查文件描述符是否處于就緒態(tài),而epoll采用回調機制。造成的結果就是,隨著fd的增加,select和poll的效率會線性降低,而epoll不會受到太大影響,除非活躍的socket很多。
  5. epoll的邊緣觸發(fā)模式效率高,系統(tǒng)不會充斥大量不關心的就緒文件描述符

雖然epoll的性能最好,但是在連接數(shù)少并且連接都十分活躍的情況下,select和poll的性能可能比epoll好,畢竟epoll的通知機制需要很多函數(shù)回調。

更多內容,歡迎關注微信公眾號:全菜工程師小輝~

分享到:
標簽:epoll
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定