主頁 > 軟體設計 > 搞懂這篇文章,關于IO復用的問題就信手拈來了

搞懂這篇文章,關于IO復用的問題就信手拈來了

2020-12-06 12:36:02 軟體設計

以一個生活中的例子來解釋.
假設你在大學中讀書,要等待一個朋友來訪,而這個朋友只知道你在A號樓,但是不知道你具體住在哪里,于是你們約好了在A號樓門口見面.
如果你使用的阻塞IO模型來處理這個問題,那么你就只能一直守候在A號樓門口等待朋友的到來,在這段時間里你不能做別的事情,不難知道,這種方式的效率是低下的.

進一步解釋select和epoll模型的差異.

select版大媽做的是如下的事情:比如同學甲的朋友來了,select版大媽比較笨,她帶著朋友挨個房間進行查詢誰是同學甲,你等的朋友來了,于是在實際的代碼中,select版大媽做的是以下的事情:

int n = select(&readset,NULL,NULL,100); 
for (int i = 0; n > 0; ++i) {
     if (FD_ISSET(fdarray[i], &readset)) {
         do_something(fdarray[i]); --n; 
    } 
}

epoll版大媽就比較先進了,她記下了同學甲的資訊,比如說他的房間號,那么等同學甲的朋友到來時,只需要告訴該朋友同學甲在哪個房間即可,不用自己親自帶著人滿大樓的找人了.于是epoll版大媽做的事情可以用如下的代碼表示:

n = epoll_wait(epfd,events,20,500);
for(i=0;i<n;++i) { do_something(events[n]);
}
在epoll中,關鍵的資料結構epoll_event定義如下:

typedef union epoll_data {
     void *ptr;
     int fd; 
     __uint32_t u32;
     __uint64_t u64; 
}epoll_data_t; 
struct epoll_event { 
    __uint32_t events; /* Epoll events */ 
    epoll_data_t data;/* User data variable */
 }; 

可以看到,epoll_data是一個union結構體,它就是epoll版大媽用于保存同學資訊的結構體,它可以保存很多型別的資訊:
fd,指標,等等.有了這個結構體,epoll大媽可以不用吹灰之力就可以定位到同學甲.

別小看了這些效率的提高,在一個大規模并發的服務器中,輪詢IO是最耗時間的操作之一.再回到那個例子中,如果每到來一個朋友樓管大媽都要全樓的查詢同學,那么處理的效率必然就低下了,過不久樓底就有不少的人了.

對比最早給出的阻塞IO的處理模型, 可以看到采用了多路復用IO之后, 程式可以自由的進行自己除了IO操作之外的作業, 只有到IO狀態發生變化的時候由多路復用IO進行通知, 然后再采取相應的操作, 而不用一直阻塞等待IO狀態發生變化了.

從上面的分析也可以看出,epoll比select的提高實際上是一個用空間換時間思想的具體應用.

【文章福利】小編推薦自己的linuxC/C++語言交流群:832218493,整理了一些個人覺得比較好的學習書籍、視頻資料共享在里面,有需要的可以自行添加哦!~
在這里插入圖片描述
更多優秀文章在公眾號
在這里插入圖片描述

二、深入理解epoll的實作原理:開發高性能網路程式時,windows開發者們言必稱iocp,linux開發者們則言必稱epoll,

大家都明白epoll是一種IO多路復用技術,可以非常高效的處理數以百萬計的socket句柄,比起以前的select和poll效率高大發了,

我們用起epoll來都感覺挺爽,確實快,那么,它到底為什么可以高速處理這么多并發連接呢?

先簡單回顧下如何使用C庫封裝的3個epoll系統呼叫吧,

int epoll_create(int size); 
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

使用起來很清晰,首先要呼叫epoll_create建立一個epoll物件,引數size是內核保證能夠正確處理的最大句柄數,多于這個最大數時內核可不保證效果,
epoll_ctl可以操作上面建立的epoll,例如,將剛建立的socket加入到epoll中讓其監控,或者把 epoll正在監控的某個socket句柄移出epoll,不再監控它等等,

epoll_wait在呼叫時,在給定的timeout時間內,當在監控的所有句柄中有事件發生時,就回傳用戶態的行程,
從上面的呼叫方式就可以看到epoll比select/poll的優越之處:

因為后者每次呼叫時都要傳遞你所要監控的所有socket給select/poll系統呼叫,這意味著需要將用戶態的socket串列copy到內核態,如果以萬計的句柄會導致每次都要copy幾十幾百KB的記憶體到內核態,非常低效,

而我們呼叫epoll_wait時就相當于以往呼叫select/poll,但是這時卻不用傳遞socket句柄給內核,因為內核已經在epoll_ctl中拿到了要監控的句柄串列,

所以,實際上在你呼叫epoll_create后,內核就已經在內核態開始準備幫你存盤要監控的句柄了,每次呼叫epoll_ctl只是在往內核的資料結構里塞入新的socket句柄,

在內核里,一切皆檔案,所以,epoll向內核注冊了一個檔案系統,用于存盤上述的被監控socket,

當你呼叫epoll_create時,就會在這個虛擬的epoll檔案系統里創建一個file結點,當然這個file不是普通檔案,它只服務于epoll,epoll在被內核初始化時(作業系統啟動),同時會開辟出epoll自己的內核高速cache區,用于安置每一個我們想監控的socket,這些socket會以紅黑樹的形式保存在內核cache里,以支持快速的查找、插入、洗掉,

這個內核高速cache區,就是建立連續的物理記憶體頁,然后在之上建立slab層,簡單的說,就是物理上分配好你想要的size的記憶體物件,每次使用時都是使用空閑的已分配好的物件,

static int __init eventpoll_init(void) {
… …
/* Allocates slab cache used to allocate “struct epitem” items /
epi_cache = kmem_cache_create(“eventpoll_epi”, sizeof(struct epitem),0,SLAB_HWCACHE_ALIGN| EPI_SLAB_DEBUG|SLAB_PANIC, NULL, NULL);
/
Allocates slab cache used to allocate “struct eppoll_entry” */
pwq_cache = kmem_cache_create(“eventpoll_pwq”, sizeof(struct eppoll_entry), 0, EPI_SLAB_DEBUG|SLAB_PANIC, NULL, NULL);
… …
epoll的高效就在于,當我們呼叫epoll_ctl往里塞入百萬個句柄時,epoll_wait仍然可以飛快的回傳,并有效的將發生事件的句柄給我們用戶,

這是由于我們在呼叫epoll_create時,內核除了幫我們在epoll檔案系統里建了個file結點,在內核cache里建了個紅黑樹用于存盤以后epoll_ctl傳來的socket外,還會再建立一個list鏈表,用于存盤準備就緒的事件,當epoll_wait呼叫時,僅僅觀察這個list鏈表里有沒有資料即可,有資料就回傳,沒有資料就sleep,等到timeout時間到后即使鏈表沒資料也回傳,所以,epoll_wait非常高效,

那么,這個準備就緒list鏈表是怎么維護的呢?當我們執行epoll_ctl時,除了把socket放到epoll檔案系統里file物件對應的紅黑樹上之外,還會給內核中斷處理程式注冊一個回呼函式,告訴內核,如果這個句柄的中斷到了,就把它放到準備就緒list鏈表里,

所以,當一個socket上有資料到了,內核在把網卡上的資料copy到內核中后就來把socket插入到準備就緒鏈表里了,
如此,一顆紅黑樹,一張準備就緒句柄鏈表,少量的內核cache,就幫我們解決了大并發下的socket處理問題,

執行epoll_create時,創建了紅黑樹和就緒鏈表,執行epoll_ctl時,如果增加socket句柄,則檢查在紅黑樹中是否存在,存在立即回傳,不存在則添加到樹干上,然后向內核注冊回呼函式,用于當中斷事件來臨時向準備就緒鏈表中插入資料,執行epoll_wait時立刻回傳準備就緒鏈表里的資料即可,

最后看看epoll獨有的兩種模式LT和ET,無論是LT和ET模式,都適用于以上所說的流程,

區別是,LT模式下,只要一個句柄上的事件一次沒有處理完,會在以后呼叫epoll_wait時次次回傳這個句柄,而ET模式僅在第一次回傳,

這件事怎么做到的呢?當一個socket句柄上有事件時,內核會把該句柄插入上面所說的準備就緒list鏈表,這時我們呼叫epoll_wait,會把準備就緒的socket拷貝到用戶態記憶體,然后清空準備就緒list鏈表,最后,epoll_wait干了件事,就是檢查這些socket,如果不是ET模式(就是LT模式的句柄了),并且這些socket上確實有未處理的事件時,又把該句柄放回到剛剛清空的準備就緒鏈表了,

所以,非ET的句柄,只要它上面還有事件,epoll_wait每次都會回傳,而ET模式的句柄,除非有新中斷到,即使socket上的事件沒有處理完,也是不會次次從epoll_wait回傳的,

三、擴展閱讀(epoll與之前其他相關技術的比較):

Linux提供了select、poll、epoll介面來實作IO復用,三者的原型如下所示,本文從引數、實作、性能等方面對三者進行對比,

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
select、poll、epoll_wait引數及實作對比

1、select的第一個引數nfds為fdset集合中最大描述符值加1,fdset是一個位陣列,其大小限制為__FD_SETSIZE(1024),位陣列的每一位代表其對應的描述符是否需要被檢查,
select的第二三四個引數表示需要關注讀、寫、錯誤事件的檔案描述符位陣列,這些引數既是輸入引數也是輸出引數,可能會被內核修改用于標示哪些描述符上發生了關注的事件,
所以每次呼叫select前都需要重新初始化fdset,
timeout引數為超時時間,該結構會被內核修改,其值為超時剩余的時間,
select對應于內核中的sys_select呼叫,sys_select首先將第二三四個引數指向的fd_set拷貝到內核,然后對每個被SET的描述符呼叫進行poll,并記錄在臨時結果中(fdset),如果有事件發生,select會將臨時結果寫到用戶空間并回傳;當輪詢一遍后沒有任何事件發生時,如果指定了超時時間,則select會睡眠到超時,睡眠結束后再進行一次輪詢,并將臨時結果寫到用戶空間,然后回傳, select回傳后,需要逐一檢查關注的描述符是否被SET(事件是否發生),

2、poll與select不同,通過一個pollfd陣列向內核傳遞需要關注的事件,故沒有描述符個數的限制,pollfd中的events欄位和revents分別用于標示關注的事件和發生的事件,故pollfd陣列只需要被初始化一次,
poll的實作機制與select類似,其對應內核中的sys_poll,只不過poll向內核傳遞pollfd陣列,然后對pollfd中的每個描述符進行poll,相比處理fdset來說,poll效率更高, poll回傳后,需要對pollfd中的每個元素檢查其revents值,來得指事件是否發生,

3、epoll通過epoll_create創建一個用于epoll輪詢的描述符,通過epoll_ctl添加/修改/洗掉事件,通過epoll_wait檢查事件,epoll_wait的第二個引數用于存放結果, epoll與select、poll不同,首先,其不用每次呼叫都向內核拷貝事件描述資訊,在第一次呼叫后,事件資訊就會與對應的epoll描述符關聯起來,另外epoll不是通過輪詢,而是通過在等待的描述符上注冊回呼函式,當事件發生時,回呼函式負責把發生的事件存盤在就緒事件鏈表中,最后寫到用戶空間,

作者:金發萌音
鏈接:https://www.jianshu.com/p/b5bc204da984
來源:簡書
著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處,

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/230766.html

標籤:其他

上一篇:Java環境變數的配置(圖文詳解)

下一篇:還不懂java六邊形架構的看過來!

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more