當前位置: 妍妍網 > 碼農

騰訊二面:epoll效能那麽高,為什麽?

2024-03-05碼農

推薦關註

掃碼關註 後端架構師 」,選擇 星標 公眾號

重磅幹貨,第一時間送達!

責編:架構君 | 來源:cloud.tencent.com/developer/article/2384713

上一篇好文:

正文

大家好,我是後端架構師。

最近有小夥伴拿到了一線互聯網企業如 美團、拼多多、極兔、有贊、希音的面試資格,遇到一幾個很重要的面試題:

  • 說說epoll的數據結構

  • 說說epoll的實作原理

  • 協定棧如何與epoll通訊?

  • epoll執行緒安全如何加鎖?

  • 說說ET與LT的實作

  • ……

  • 這裏給大家做一下系統化、體系化的梳理,使得大家可以充分展示一下大家雄厚的 「技術肌肉」, 讓面試官愛到 「不能自已、口水直流」

    epoll的數據結構


    多種數據結構進行決策


    epoll至少需要兩個集合

  • 所有fd的總集

  • 就緒fd的集合

  • 那麽這個總集選用什麽數據結構儲存呢?

    我們知道,一個fd,其底層對應一個TCB。那麽也就是說key=fd,val=TCB,是一個典型的kv型數據結構,對於kv型數據結構我們可以使用以下三種進行儲存。

    1. hash

    2. 紅黑樹

    3. b/b+tree

    如果使用hash進行儲存,其優點是查詢速度很快,O(1)。

    但是在我們呼叫epoll_create()的時候,hash底層的陣列建立多大合適呢?

    如果我們有百萬的fd,那麽這個陣列越大越好,如果我們僅僅十幾個fd需要管理,在建立陣列的時候,太大的空間就很浪費。而這個fd我們又不能預先知道有多少,所以hash是不合適的。

    b/b+tree是多叉樹,一個結點可以存多個key,主要是用於降低層高,用於磁盤索引的,所以在我們這個記憶體場景下也是不適合的。

    在記憶體索引的場景下我們一般使用紅黑樹來作為首選的數據結構,首先紅黑樹的尋找速度很快,O(log(N))。其次在呼叫epoll_create()的時候,只需要建立一個紅黑樹樹根即可,無需浪費額外的空間。

    那麽就緒集合用什麽數據結構呢,首先就緒集合不是以尋找為主的,就緒集合的作用是將裏面的元素拷貝給使用者進行處理,所以集合裏的元素沒有優先級,那麽就可以采用線性的數據結構,使用佇列來儲存,先進先出,先就緒的先處理。

    所有fd的總集 -----> 紅黑樹

    就緒fd的集合 -----> 佇列

    紅黑樹和就緒佇列的關系

    紅黑樹的結點和就緒佇列的結點的同一個節點,所謂的加入就緒佇列,就是將結點的前後指標聯系到一起。所以就緒了不是將紅黑樹結點delete掉然後加入佇列。他們是同一個結點,不需要delete。

    structepitem{
    RB_ENTRY(epitem) rbn;
    LIST_ENTRY(epitem) rdlink;
    int rdy; //exist in list

    int sockfd;
    structepoll_eventevent;
    };
    structeventpoll {
    ep_rb_tree rbr;
    int rbcnt;
    LIST_HEAD( ,epitem) rdlist;
    int rdnum;
    int waiting;
    pthread_mutex_t mtx; //rbtree update
    pthread_spinlock_t lock; //rdlist update
    pthread_cond_t cond; //block for event
    pthread_mutex_t cdmtx; //mutex for cond
    };


    協定棧如何與epoll模組通訊

    epoll的工作環境


    應用程式只能透過三個 api介面 來操作epoll。 當一個io準備就緒的時候,epoll是怎麽知道io準備就緒了呢?是由協定棧將數據解析出來觸發回呼通知epoll的。也就是說可以把epoll的工作環境看出三部份,左邊應用程式的api,中間的epoll,右邊是協定棧的回呼(協定棧當然不能直接操作epoll,中間的vfs在此不是重點,就直接省略vfs這一層了)。



    協定棧觸發回呼通知epoll的時機


    socket有兩類,一類是監聽listenfd,一類是客戶端clientfd。 對於sockfd而言,我們一般比較關註EPOLLIN和EPOLLOUT這兩個事件,所以如果是listenfd,我們通常的做法就是accept。對於clientfd來說,如果可讀我們就recv,如果可寫我們就send。

    協定棧將數據解析出來觸發回呼通知epoll。epoll是怎麽知道哪個io就緒了呢?我們從ip頭可以解析出源ip,目的ip和協定,從tcp頭可以解析出源埠和目的埠,此時 五元組 就湊齊了。socket fd --- < 源IP地址 , 源埠 , 目的IP地址 , 目的埠 , 協定 > 一個fd就是一個五元組,知道了fd,我們就能從紅黑樹中找到對應的結點。

    那麽這個回呼函式做什麽事情呢?我們傳入fd和具體事件這兩個參數,然後做下面兩個操作

    1. 透過fd找到對應的結點

    2. 把結點加入到就緒佇列

    1、協定棧中,在三次握手完成之後,會往全連線佇列中添加一個TCB結點,然後觸發一個回呼函式,通知到epoll裏面有個 EPOLL IN事件

    2、客戶端發送一個封包,協定棧接收後回復ACK,之後觸發一個回呼函式,通知到epoll裏面有個EPOLLIN事件

    3、每個連線的TCB裏面都有一個sendbuf,在對端接收到數據並返回ACK以後,sendbuf就可以將這部份確認接收的數據清空,此時sendbuf裏面就有剩余空間,此時觸發一個回呼函式,通知到epoll裏面有個EPOLLOUT事件

    4、當對端發送close,在接收到fin後回復ACK,此時會呼叫回呼函式,通知到epoll有個EPOLLIN事件

    5、當接收到rst標誌位的時候,回復ack之後也會觸發回呼函式,通知epoll有一個EPOLLERR事件


    通知的時機總結


    個有5個通知的地方

    1. 三次握手完成之後

    2. 接收數據回復ACK之後

    3. 發送數據收到ACK之後

    4. 接收FIN回復ACK之後

    5. 接收RST回復ACK之後


    從回呼機制看epoll 與 select/poll的區別


    由於select和poll沒有本質的區別,所以下面統一稱為poll。

    // poll跟select類似, 其實poll就是把select三個檔描述符集合變成一個集合了。
    intselect(int nfds, fd_set * readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    intpoll(struct pollfd *fds, nfds_t nfds, int timeout);

    我們看到每次呼叫poll,都需要把總集fds拷貝到內核態,檢測完之後,再有內核態拷貝的使用者態,這就是poll。而epoll不是這樣,epoll只要有新的io就呼叫epoll_ctl()加入到紅黑樹裏面,一旦有觸發就用epoll_wait()將有事件的結點帶出來,可以看到他們的第一個區別:poll總是拷貝總集,如果有100w個fd,只有兩三個就緒呢?這會造成大量資源浪費;而epoll總是將需要拷貝的東西進行拷貝,沒有浪費。

    第二個區別:我們從上面知道了epoll的事件都是由協定棧進行回呼然後加入到就緒佇列的,而poll呢?內核如何檢測poll的io是否就緒?只能透過遍歷的方法判斷,所以poll檢測io透過遍歷的方法也是比較慢的。

    所以兩者的區別:

    select/poll需要把總集copy到內核,而epoll不用

    實作原理上面,select/poll 需要迴圈遍歷總集是否有就緒,而epoll是那個結點就緒了就加入就緒佇列裏面。

    註意:poll不一定就比epoll慢,在io量小的情況下,poll是比epoll快的,而在大io量下,epoll絕對是有主導地位的。至於有多少個io才算多,其實也很難說,一般認為500或者1024為分界點。

    epoll執行緒安全如何加鎖

    3個api做什麽事情

    epoll_create() ===】建立紅黑樹的根節點

    epoll_ctl() ===】add,del,mod 增加、刪除、修改結點

    epoll_wait() ===】把就緒佇列的結點copy到使用者態放到events裏面,跟recv函式很像


    分析加鎖


    如果有3個執行緒同時操作epoll,有哪些地方需要加鎖? 我們使用者層面一共就只有3個api可以使用:

    如果同時呼叫 epoll_create() ,那就是建立三顆紅黑樹,沒有涉及到資源競爭,沒有關系。

    如果同時呼叫 epoll_ctl() ,對同一顆紅黑樹進行,增刪改,這就涉及到資源競爭需要加鎖了,此時我們對整棵樹進行加鎖。

    如果同時呼叫epoll_wait() ,其操作的是就緒佇列,所以需要對就緒佇列進行加鎖。

    我們要扣住epoll的工作環境,在應用程式呼叫 epoll_ctl() ,協定棧會不會有回呼操作紅黑樹結點?呼叫epoll_wait() copy出來的時候,協定棧會不會操作操作紅黑樹結點加入就緒佇列?綜上所述:

    epoll_ctl() 對紅黑樹加鎖
    epoll_wait()對就緒佇列加鎖
    回呼函式() 對紅黑樹加鎖,對就緒佇列加鎖

    那麽紅黑樹加什麽鎖,就緒佇列加什麽鎖呢?

    對於紅黑樹這種節點比較多的時候,采用互斥鎖來加鎖。

    就緒佇列就跟生產者消費者一樣,結點是從協定棧回呼函式來生產的,消費是epoll_wait()來消費。那麽對於佇列而言,用自旋鎖(對於佇列而言,插入刪除比較簡單,cpu自旋等待比讓出的成本更低,所以用自旋鎖)。

    ET與LT如何實作

    ET邊沿觸發,只觸發一次

    LT水平觸發,如果沒有讀完就一直觸發

    程式碼如何實作ET和LT的效果呢?水平觸發和邊沿觸發不是故意設計出來的,這是自然而然,水到渠成的功能。水平觸發和邊沿觸發程式碼只需要改一點點就能實作。從協定棧檢測到接收數據,就呼叫一次回呼,這就是ET,接收到數據,呼叫一次回呼。而LT水平觸發,檢測到recvbuf裏面有數據就呼叫回呼。所以ET和LT就是在使用回呼的次數上面的差異。

    那麽具體如何實作呢?協定棧流程裏面觸發回呼,是天然的符合ET只觸發一次的。那麽如果是LT,在recv之後,如果緩沖區還有數據那麽加入到就緒佇列。那麽如果是LT,在send之後,如果緩沖區還有空間那麽加入到就緒佇列。那麽這樣就能實作LT了。

    (真實實作程式碼參看:linux-2.6.24/fs/eventpoll.c檔中的 ep_send_events 函式)

    說在最後


    Linux相關面試題,是非常常見的面試題。

    以上的內容,如果大家能對答如流,如數家珍,基本上 面試官會被你 震驚到、吸引到。

    最終, 讓面試官愛到 「不能自已、口水直流」 。offer, 也就來了。

    你還有什麽想要補充的嗎?

    最後,再次推薦下我們的AI星

    為了跟上AI時代我幹了一件事兒,我建立了一個知識星球社群:ChartGPT與副業。想帶著大家一起探索 ChatGPT和新的AI時代

    有很多小夥伴搞不定ChatGPT帳號,於是我們決定,凡是這三天之內加入ChatPGT的小夥伴,我們直接送一個正常可用的永久ChatGPT獨立帳戶。

    不光是增長速度最快,我們的星球品質也絕對經得起考驗,短短一個月時間,我們的課程團隊釋出了 8個專欄、18個副業計畫

    簡單說下這個星球能給大家提供什麽:

    1、不斷分享如何使用ChatGPT來完成各種任務,讓你更高效地使用ChatGPT,以及副業思考、變現思路、創業案例、落地案例分享。

    2、分享ChatGPT的使用方法、最新資訊、商業價值。

    3、探討未來關於ChatGPT的機遇,共同成長。

    4、幫助大家解決ChatGPT遇到的問題。

    5、 提供一整年的售後服務,一起搞副業

    星球福利:

    1、加入星球4天後,就送ChatGPT獨立帳號。

    2、邀請你加入ChatGPT會員交流群。

    3、贈送一份完整的ChatGPT手冊和66個ChatGPT副業賺錢手冊。

    其它福利還在籌劃中... 不過,我給你大家保證,加入星球後,收獲的價值會遠遠大於今天加入的門票費用 !

    本星球第一期原價 399 ,目前屬於試營運,早鳥價 139 ,每超過50人漲價10元,星球馬上要來一波大的漲價,如果你還在猶豫,可能最後就要以 更高價格加入了 。。

    早就是優勢。 建議大家盡早以便宜的價格加入!

    歡迎有需要的同學試試,如果本文對您有幫助,也請幫忙點個 贊 + 在看 啦!❤️

    在 還有更多優質計畫系統學習資源,歡迎分享給其他同學吧!

    PS:如果覺得我的分享不錯,歡迎大家隨手點贊、轉發、在看。

    最後給讀者整理了一份BAT大廠面試真題,需要的可掃碼加微信備註:「面試」獲取。

    版權申明:內容來源網路,版權歸原創者所有。除非無法確認,我們都會標明作者及出處,如有侵權煩請告知,我們會立即刪除並表示歉意。謝謝!

    END

    最近面試BAT,整理一份面試資料【Java面試BAT通關手冊】,覆蓋了Java核心技術、JVM、Java並行、SSM、微服務、資料庫、數據結構等等。在這裏,我為大家準備了一份2021年最新最全BAT等大廠Java面試經驗總結。

    別找了,想獲取史上最全的Java大廠面試題學習資料

    掃下方二維碼回復面試就好了

    歷史好文:

    掃碼關註後端架構師」,選擇星標公眾號

    重磅幹貨,第一時間送達

    ,你在看嗎?