1. 阿里面試問的select、poll、epoll模型的區(qū)別

        共 2838字,需瀏覽 6分鐘

         ·

        2022-06-08 10:27


        這一篇要說的select、poll、epoll這三個(gè)的區(qū)別,大家對于IO多路復(fù)用都了解吧,這個(gè)問題也是面試官最最愛問的問題之一了


        ? ? ? ? ? ?


        操作系統(tǒng)在處理IO的時(shí)候,主要客源分為兩個(gè)階段:

        ?

        等待數(shù)據(jù)傳遞到IO設(shè)備

        ?

        IO設(shè)備將數(shù)據(jù)復(fù)制到用戶空間user?space

        ?

        也就可以將上述過程簡化理解為:

        ?

        等待數(shù)據(jù)到kernel內(nèi)核區(qū)域

        ?

        kernel內(nèi)核區(qū)域?qū)?shù)據(jù)復(fù)制到用戶區(qū)域user?space,用戶區(qū)域可以理解為JVM區(qū)域,即進(jìn)程或者線程的緩沖區(qū)

        ?

        BIO

        ?

        這是屬于最簡單的同步阻塞IO模型

        ?

        當(dāng)應(yīng)用層有數(shù)據(jù)過來的時(shí)候,會(huì)調(diào)用recvfrom方法,但是這個(gè)時(shí)候應(yīng)用層的數(shù)據(jù)還沒有復(fù)制到kernel內(nèi)核區(qū),也就是上面說的第一個(gè)過程,這個(gè)過程需要時(shí)間,所以recvfrom會(huì)阻塞

        ?

        當(dāng)內(nèi)核kernel中的數(shù)據(jù)準(zhǔn)備好了之后,recvfrom方法并不會(huì)返回,而是會(huì)發(fā)起一個(gè)系統(tǒng)調(diào)用將kernel中的數(shù)據(jù)復(fù)制到JVM的進(jìn)程中的緩沖區(qū)中,也就是用戶空間user?space


        當(dāng)這個(gè)復(fù)制也完成之后,也就是上面說的兩個(gè)階段全部完成之后,recvfrom才會(huì)返回并且解除程序的阻塞


        默認(rèn)情況下,所有的文件操作都是阻塞的,在進(jìn)程調(diào)用期間的recvfrom直到數(shù)據(jù)包達(dá)到并且被復(fù)制到JVM進(jìn)程緩沖區(qū)或者發(fā)生錯(cuò)誤的時(shí)候才會(huì)返回,在此期間一直阻塞

        ?

        在阻塞期間,CPU不能執(zhí)行IO操作,但是CPU還是可以去做別的事情的,阻塞了,但沒有完全阻塞

        ?

        我們再看操作系統(tǒng)處理IO的兩個(gè)步驟

        ?

        等待數(shù)據(jù)到kernel內(nèi)核區(qū)域

        ?

        kernel內(nèi)核區(qū)域?qū)?shù)據(jù)復(fù)制到用戶區(qū)域user?space

        ?

        阻塞IO模型,其實(shí)就是把這兩個(gè)階段合并在一起,一起阻塞

        ?

        NIO

        ?

        非阻塞其實(shí)就是把第一個(gè)過程的阻塞變成非阻塞,也就是recvfrom函數(shù)會(huì)不斷的去詢問kernel內(nèi)核區(qū)域中的數(shù)據(jù)是否準(zhǔn)備好

        ?

        如果數(shù)據(jù)沒有準(zhǔn)備好,就返回EWOULDBLOCK錯(cuò)誤,不斷的去進(jìn)行輪詢檢查,知道發(fā)現(xiàn)kernel內(nèi)核中的數(shù)據(jù)準(zhǔn)備好了,就會(huì)返回

        ?

        第二個(gè)階段屬于系統(tǒng)調(diào)用,是必須阻塞的,就是將數(shù)據(jù)從kernel內(nèi)核區(qū)域拷貝到用戶區(qū)域,也就是進(jìn)程緩沖區(qū)中

        ?

        Linux系統(tǒng)將所有設(shè)備都當(dāng)作文件來處理,而Linux用文件描述符fd來標(biāo)識(shí)每個(gè)文件對象。

        ?

        問題

        ?

        阻塞模型在沒有收到數(shù)據(jù)的時(shí)候就會(huì)阻塞卡主,如果一個(gè)線程需要接受到多個(gè)客戶端的socket的fd連接,這樣會(huì)導(dǎo)致在處理這種情況效率比較低

        ?

        必須處理完前面的所有fd,才可以處理后面的fd,即使后面的fd可能比前面的fd提前準(zhǔn)備好了,但是也得等,這樣會(huì)導(dǎo)致客戶端的嚴(yán)重延遲

        ?

        于是為了處理多個(gè)請求,有的小伙伴可能會(huì)想到用多個(gè)線程來改善,可以引入線程池改善這個(gè)情況,并且通過線程池的最大數(shù)量來控制這個(gè)限度,但是這并沒有從根本上解決問題

        ?

        應(yīng)用:適用于針對大量的io請求的情況,對于服務(wù)器必須在同時(shí)處理來自客戶端的大量的io操作的時(shí)候,就非常適合

        ?

        ?

        Select工作流程

        ?

        單個(gè)進(jìn)程可以同時(shí)處理多個(gè)網(wǎng)絡(luò)連接的IO請求,就是調(diào)用select之后,整個(gè)程序就阻塞了

        ?

        此時(shí)需要把所有的fd集合都從JVM用戶空間拷貝到kernel內(nèi)核空間,這個(gè)開銷很大

        ?

        然后去輪詢檢查所有的select負(fù)責(zé)的fd,當(dāng)找到一個(gè)client中的數(shù)據(jù)準(zhǔn)備好了之后,select就會(huì)返回,此時(shí)將數(shù)據(jù)從kernel復(fù)制到進(jìn)程的緩沖區(qū)中

        ?

        缺點(diǎn):

        ?

        1、每次都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài),消耗資源

        ?

        2、每次調(diào)用需要進(jìn)行輪詢所有傳遞進(jìn)來的fd,也比較消耗資源

        ?

        你想啊,要是有十萬個(gè)連接,而活躍的只有幾個(gè),那每次都要遍歷這十萬個(gè),豈不是很糟糕

        ?

        3、支持的fd數(shù)量有限,根據(jù)fd_size的定義,它的大小為32個(gè)整數(shù)大?。?/span>32位機(jī)器為32*32,所有共有1024bits可以記錄fd),每個(gè)fd一個(gè)bit,所以最大只能同時(shí)處理1024個(gè)fd

        ?

        每一次呼叫select都需要先從 user spaceFD_SET復(fù)制到 kernel(約線性時(shí)間成本)


        為什么 select 不能像epoll一樣,只做一次復(fù)制就好呢?

        ?

        每一次呼叫 select()前,FD_SET都可能更動(dòng),而 epoll 提供了共享記憶存儲(chǔ)結(jié)構(gòu),所以不需要這里的kernel內(nèi)核區(qū)域和用戶區(qū)域之間的全量數(shù)據(jù)復(fù)制

        ?

        Poll

        ?

        poll的原理與select非常相似,但是呢,有兩點(diǎn)的不同之處

        ?

        一個(gè)是存儲(chǔ)fd的集合不同,select是有限的,而poll的方式存儲(chǔ)是鏈?zhǔn)降模瑳]有最大連接數(shù)的限制

        ?

        另一點(diǎn)就是水平觸發(fā),也就是通知程序fd就緒后,這次沒有被處理,那么下次poll的時(shí)候會(huì)再次通知同個(gè)fd已經(jīng)就緒

        ?

        Epoll

        ?

        epoll既然是對selectpoll的改進(jìn),就應(yīng)該能避免上述的三個(gè)缺點(diǎn)。那epoll都是怎么解決的呢?

        ?

        在此之前,我們先看一下epoll和selectpoll的調(diào)用接口上的不同,selectpoll都只提供了一個(gè)函數(shù)——select或者poll函數(shù)。而epoll提供了三個(gè)函數(shù),epoll_create,epoll_ctlepoll_wait

        ?

        epoll_create是創(chuàng)建一個(gè)epoll句柄;epoll_ctl是注冊要監(jiān)聽的事件類型;epoll_wait則是等待事件的產(chǎn)生。

        ?

        對于每次都需要把數(shù)據(jù)從用戶區(qū)全量復(fù)制到kernel內(nèi)核區(qū)這個(gè)缺點(diǎn),epoll的解決方案在epoll_ctl函數(shù)中。

        ?

        每次注冊新的事件到epoll句柄中時(shí)(在epoll_ctl中指定EPOLL_CTL_ADD),會(huì)把所有的fd拷貝進(jìn)內(nèi)核,而不是在epoll_wait的時(shí)候重復(fù)拷貝。epoll保證了每個(gè)fd在整個(gè)過程中只會(huì)拷貝一次。

        ?

        對于第二個(gè)每次都需要輪詢所有fd這個(gè)缺點(diǎn)

        ?

        epoll的解決方案不像selectpoll一樣每次都把current輪流加入fd對應(yīng)的設(shè)備等待隊(duì)列中,而只在epoll_ctl時(shí)把current掛一遍(這一遍必不可少)并為每個(gè)fd指定一個(gè)回調(diào)函數(shù),當(dāng)設(shè)備就緒,喚醒等待隊(duì)列上的等待者時(shí),就會(huì)調(diào)用這個(gè)回調(diào)函數(shù),而這個(gè)回調(diào)函數(shù)會(huì)把就緒的fd加入一個(gè)就緒鏈表)。

        ?

        epoll_wait的工作實(shí)際上就是在這個(gè)就緒鏈表中查看有沒有就緒的fd(利用schedule_timeout()實(shí)現(xiàn)睡一會(huì),判斷一會(huì)的效果,和select實(shí)現(xiàn)中的第7步是類似的)。

        ?

        把主動(dòng)權(quán)交給了每一個(gè)連接,當(dāng)設(shè)備就緒的時(shí)候,調(diào)用回調(diào)函數(shù)才會(huì)加入到這個(gè)就緒的集合

        ?

        對于第三個(gè)數(shù)量的缺點(diǎn)

        ?

        epoll沒有這個(gè)限制,它所支持的FD上限是最大可以打開文件的數(shù)目,這個(gè)數(shù)字一般遠(yuǎn)大于1024,舉個(gè)例子,1GB內(nèi)存的機(jī)器上大約是10萬左右,具體數(shù)目可以cat /proc/sys/fs/file-max察看,一般來說這個(gè)數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大。

        ?


        結(jié)束語


        贈(zèng)人玫瑰,手有余香。


        哦對了,后續(xù)所有的文章都會(huì)更新到這里


        https://github.com/DayuMM2021/Java








        瀏覽 59
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 美女被操喷水 | 五月婷亚洲 | 亚洲色情网站 | www.99热 | 国产日韩精品一区二区三区 |