成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

面試題:BIO,NIO,AIO 的區(qū)別是什么?說說 select 和 epoll 工作機(jī)制與差異?為何 epoll 如此高效

開發(fā) 通信技術(shù)
多路復(fù)用是一種能夠同時(shí)監(jiān)控多個(gè)文件描述符(如網(wǎng)絡(luò)連接、文件、管道等),一旦某個(gè)描述符就緒,便能夠通知程序進(jìn)行相應(yīng)的讀寫操作的技術(shù)。

面試官:說說看你知道的IO模型有哪些?

先說說看什么是IO吧。IO表示輸入輸出,訪問外部設(shè)備讀取數(shù)據(jù)或者向外部設(shè)備寫數(shù)據(jù)。常見的文件讀寫操作、網(wǎng)絡(luò)通信操作,其實(shí)都是IO的過程。

這里以阻塞式讀取為例,分析IO操作所進(jìn)行的工作:

  • 當(dāng)read/recv時(shí),如果緩沖區(qū)中沒有數(shù)據(jù),那就阻塞式等待數(shù)據(jù)就緒 -- 等待。
  • 當(dāng)read/recv時(shí),如果緩沖區(qū)中有數(shù)據(jù),那么就將數(shù)據(jù)從緩沖區(qū)拷貝到應(yīng)用層 -- 拷貝。

因此,我們可以認(rèn)為,IO = 等待 + 拷貝。只要執(zhí)行流(進(jìn)程/線程)參與了等待和拷貝其中之一,那么就認(rèn)為這個(gè)執(zhí)行流進(jìn)行了IO操作。

下圖展示了在進(jìn)行寫文件操作時(shí)系統(tǒng)所進(jìn)行的工作,可分為三步:

再來說IO模型,經(jīng)典的IO模型主要包括以下幾種:

一、阻塞IO(BIO)

定義:在BIO模型中,當(dāng)程序進(jìn)行IO操作時(shí),它會(huì)持續(xù)等待直到操作完成。在此期間,線程被阻塞,不能執(zhí)行其他任務(wù)。

特點(diǎn):

  • 傳統(tǒng)的IO模型,適用于連接請(qǐng)求較少且請(qǐng)求處理簡(jiǎn)單的場(chǎng)景。
  • 在這種模型中,服務(wù)器為每個(gè)客戶端連接創(chuàng)建一個(gè)線程,導(dǎo)致線程數(shù)量隨著客戶端的增加而急劇增加,消耗大量系統(tǒng)資源。

二、非阻塞IO(NIO)

定義:為了解決BIO模型的線程阻塞問題,NIO模型引入了非阻塞的概念。在NIO中,當(dāng)一個(gè)線程進(jìn)行IO操作時(shí),它不會(huì)等待操作完成,而是繼續(xù)執(zhí)行其他任務(wù)。當(dāng)IO操作完成時(shí),線程會(huì)收到通知。非阻塞式IO一般采用輪詢檢查的方法進(jìn)行IO操作,即:通過循環(huán),不斷檢查IO資源是否已經(jīng)就緒,就緒就讀取,不就緒就執(zhí)行其他的工作。

特點(diǎn):

  • 提高了線程的利用率,適用于連接請(qǐng)求多且單個(gè)請(qǐng)求處理耗時(shí)較長的場(chǎng)景。
  • 需要不斷輪詢或者使用事件通知來檢查操作是否完成,可能會(huì)占用一定的CPU時(shí)間。
  • Java的NIO庫提供了基于Reactor設(shè)計(jì)模式的非阻塞IO操作,也稱為IO多路復(fù)用。

三、異步IO(AIO)

定義:異步IO就是發(fā)起IO的執(zhí)行流本身不參與IO工作,而是有另外一個(gè)執(zhí)行流來進(jìn)行IO操作,發(fā)起IO的線程只需要等待進(jìn)行IO的執(zhí)行流反饋回結(jié)果。這樣發(fā)起IO的執(zhí)行流就不需要等到,可以處理其他的工作。當(dāng)操作完成時(shí),系統(tǒng)會(huì)通知該線程。

特點(diǎn):

  • 能夠顯著提高并發(fā)性能,因?yàn)槎鄠€(gè)操作可以同時(shí)進(jìn)行。
  • 適用于高并發(fā)、高吞吐量的場(chǎng)景,如網(wǎng)絡(luò)服務(wù)器、大規(guī)模并行計(jì)算等。
  • Java的NIO2(即AIO)基于Proactor設(shè)計(jì)模式的異步非阻塞模型。

四、IO多路復(fù)用

定義:使用操作系統(tǒng)提供的select、poll或epoll等多路復(fù)用機(jī)制,允許應(yīng)用程序同時(shí)監(jiān)視多個(gè)IO事件。

特點(diǎn):

  • 應(yīng)用程序可以將多個(gè)IO請(qǐng)求注冊(cè)到一個(gè)多路復(fù)用器上,然后通過輪詢或者阻塞等待多路復(fù)用器通知事件的發(fā)生。
  • 適用于需要同時(shí)處理多個(gè)連接的場(chǎng)景,提高了系統(tǒng)的并發(fā)性能。
  • 有效地管理多個(gè)IO操作,減少系統(tǒng)資源的消耗。

五、信號(hào)驅(qū)動(dòng)IO

定義:通過自定義對(duì)SIGIO信號(hào)的處理函數(shù)來實(shí)現(xiàn)信號(hào)驅(qū)動(dòng)式IO,當(dāng)進(jìn)程收到SIGIO信號(hào)的時(shí)候,就調(diào)用對(duì)應(yīng)的處理函數(shù)來進(jìn)行IO操作,這樣保證在調(diào)用IO接口的時(shí)候數(shù)據(jù)一定是就緒的,在沒有收到信號(hào)時(shí)不影響進(jìn)程進(jìn)行其他的工作,信號(hào)驅(qū)動(dòng)式IO避免了阻塞等待資源就緒,提高了IO效率。

特點(diǎn):

  • 相比阻塞IO和非阻塞IO更為靈活。
  • 適用于需要處理多個(gè)IO事件的場(chǎng)景。

面試官:阻塞IO(BIO)和非阻塞IO(NIO)的區(qū)別是什么?NIO和異步IO(AIO)的區(qū)別是什么?

一、以下是 BIO 和 NIO 的主要差異:

(1) 阻塞IO(Blocking IO)

  • 等待操作完成:在阻塞IO模型中,當(dāng)一個(gè)線程發(fā)起一個(gè)I/O請(qǐng)求(如讀取文件、網(wǎng)絡(luò)通信等)時(shí),該線程會(huì)被阻塞,直到I/O操作完成并且數(shù)據(jù)已經(jīng)準(zhǔn)備好。
  • 線程占用:在I/O操作進(jìn)行期間,線程無法執(zhí)行其他任務(wù),導(dǎo)致線程的利用率降低。如果I/O操作耗時(shí)較長,線程可能會(huì)長時(shí)間處于空閑狀態(tài)。
  • 簡(jiǎn)單性:阻塞IO模型相對(duì)簡(jiǎn)單,因?yàn)殚_發(fā)者不需要處理I/O操作的異步通知和狀態(tài)檢查。
  • 資源消耗:在高并發(fā)場(chǎng)景下,阻塞IO模型可能導(dǎo)致大量的線程被創(chuàng)建和阻塞,從而消耗大量的系統(tǒng)資源(如內(nèi)存和CPU)。

(2) 非阻塞IO(Non-blocking IO)

  • 立即返回:在非阻塞IO模型中,當(dāng)一個(gè)線程發(fā)起一個(gè)I/O請(qǐng)求時(shí),該請(qǐng)求會(huì)立即返回,不會(huì)阻塞線程。線程可以繼續(xù)執(zhí)行其他任務(wù),而無需等待I/O操作完成。
  • 輪詢或回調(diào):非阻塞IO模型通常需要通過輪詢(polling)或回調(diào)(callback)機(jī)制來檢查I/O操作的狀態(tài)。輪詢是主動(dòng)檢查I/O操作是否完成,而回調(diào)是當(dāng)I/O操作完成時(shí)由系統(tǒng)通知應(yīng)用程序。
  • 復(fù)雜性:非阻塞IO模型相對(duì)復(fù)雜,因?yàn)殚_發(fā)者需要處理I/O操作的異步通知和狀態(tài)檢查,以及管理多個(gè)I/O請(qǐng)求的狀態(tài)。
  • 資源效率:在高并發(fā)場(chǎng)景下,非阻塞IO模型可以更有效地利用系統(tǒng)資源,因?yàn)榫€程不會(huì)被阻塞,可以處理更多的并發(fā)請(qǐng)求。然而,由于需要頻繁地檢查I/O操作的狀態(tài),可能會(huì)增加CPU的使用率。

(3) 選擇

  • 阻塞IO適用于連接請(qǐng)求較少且請(qǐng)求處理簡(jiǎn)單的場(chǎng)景,或者當(dāng)I/O操作不會(huì)顯著影響程序性能時(shí)。
  • 非阻塞IO適用于連接請(qǐng)求多且單個(gè)請(qǐng)求處理耗時(shí)較長的場(chǎng)景,或者當(dāng)需要處理大量并發(fā)請(qǐng)求時(shí)。

二、以下是NIO和AIO的差異:

(1) 工作原理

NIO:

  • 同步非阻塞I/O模型。
  • 使用一個(gè)單獨(dú)的線程或線程池來處理所有的I/O操作。
  • 當(dāng)一個(gè)操作不能立即完成時(shí),線程不會(huì)被阻塞,而是繼續(xù)執(zhí)行其他任務(wù)。
  • 使用緩沖區(qū)(Buffer)來存儲(chǔ)數(shù)據(jù),并通過選擇器(Selector)來監(jiān)聽多個(gè)通道(Channel)上的事件,從而實(shí)現(xiàn)并發(fā)處理多個(gè)I/O操作。

AIO:

  • 異步非阻塞I/O模型。
  • 不需要通過選擇器來監(jiān)聽通道的事件,而是由操作系統(tǒng)在數(shù)據(jù)準(zhǔn)備就緒時(shí)通知應(yīng)用程序。
  • 應(yīng)用程序在數(shù)據(jù)讀寫操作時(shí)不會(huì)被阻塞,而是在操作完成時(shí)收到通知。
  • 引入了異步通道的概念,簡(jiǎn)化了程序編寫,并提高了并發(fā)處理能力。

(2) 性能與資源消耗

NIO:

  • 通過并發(fā)處理多個(gè)I/O操作來提高性能。
  • 需要合理地管理線程池和緩沖區(qū)資源,以避免資源耗盡或性能下降。

AIO:

  • 通過異步操作來避免線程阻塞,從而提高了系統(tǒng)的并發(fā)處理能力。
  • 在高并發(fā)場(chǎng)景下,AIO通常能夠表現(xiàn)出更好的性能,因?yàn)樗軌蚋行У乩孟到y(tǒng)資源。

(3) 編程復(fù)雜度

NIO:

  • 編程相對(duì)復(fù)雜,需要處理緩沖區(qū)、選擇器、通道等組件的交互。

AIO:

  • 編程復(fù)雜度也較高,但相對(duì)于NIO來說,由于引入了異步操作的概念,可以更加靈活地處理I/O操作。
  • 開發(fā)者需要熟悉AIO的編程模型,并能夠正確地處理異步通知和回調(diào)機(jī)制。

面試官:能詳細(xì)說一下多路復(fù)用中 select 和 epoll 的工作機(jī)制和性能差異嗎?

先說說多路復(fù)用。多路復(fù)用(Multiplexing)是一種能夠同時(shí)監(jiān)控多個(gè)文件描述符(如網(wǎng)絡(luò)連接、文件、管道等),一旦某個(gè)描述符就緒(通常指有數(shù)據(jù)可讀或可寫),便能夠通知程序進(jìn)行相應(yīng)的讀寫操作的技術(shù)。

select和epoll是兩種常見的I/O多路復(fù)用機(jī)制,它們的工作機(jī)制和性能差異如下:

一、select的工作機(jī)制

基本原理:

  • select函數(shù)允許一個(gè)進(jìn)程同時(shí)監(jiān)視多個(gè)文件描述符,以查看它們是否有I/O事件發(fā)生(如可讀、可寫或有異常)。
  • 它通過監(jiān)視一個(gè)文件描述符集合,在其中的文件描述符中有I/O事件發(fā)生時(shí)返回。

使用方法:

  • 調(diào)用select時(shí),需要指定三個(gè)文件描述符集合:讀集合、寫集合和異常集合,以及一個(gè)時(shí)間限制。
  • select函數(shù)會(huì)阻塞,直到至少有一個(gè)文件描述符準(zhǔn)備好進(jìn)行I/O操作,或者超時(shí)發(fā)生。
  • 當(dāng)select返回時(shí),它會(huì)通過修改這三個(gè)集合來指示哪些文件描述符已經(jīng)準(zhǔn)備好進(jìn)行I/O操作。

性能瓶頸:

  • 當(dāng)監(jiān)視的文件描述符數(shù)量很大時(shí),select的性能會(huì)顯著下降,因?yàn)樗枰闅v整個(gè)文件描述符集合來查找哪些文件描述符已經(jīng)準(zhǔn)備好進(jìn)行I/O操作。
  • select有一個(gè)內(nèi)置的文件描述符數(shù)量限制(通常是1024個(gè)),這在某些高并發(fā)場(chǎng)景下可能不夠用。
  • select的超時(shí)參數(shù)是一個(gè)時(shí)間間隔,而不是一個(gè)精確的時(shí)間點(diǎn),這可能導(dǎo)致在某些情況下時(shí)間控制不準(zhǔn)確。

二、epoll的工作機(jī)制

基本原理:

  • epoll是Linux內(nèi)核提供的一種I/O多路復(fù)用機(jī)制,是select和poll機(jī)制的改進(jìn)版。
  • 它使用事件驅(qū)動(dòng)的方式,只通知那些真正發(fā)生了I/O事件的文件描述符。

核心API:

  • epoll_create():創(chuàng)建一個(gè)epoll實(shí)例,并返回一個(gè)文件描述符。
  • epoll_ctl():將需要監(jiān)控的文件描述符添加到epoll實(shí)例中,或從epoll實(shí)例中刪除,或?qū)ΡO(jiān)聽事件進(jìn)行修改。
  • epoll_wait():等待注冊(cè)的事件發(fā)生,返回事件的數(shù)目,并將觸發(fā)的事件寫入事件數(shù)組中。

工作流程:

  • 應(yīng)用程序通過epoll_create()系統(tǒng)調(diào)用創(chuàng)建一個(gè)epoll實(shí)例。
  • 應(yīng)用程序通過epoll_ctl()系統(tǒng)調(diào)用將需要監(jiān)控的文件描述符添加到epoll實(shí)例中。
  • 內(nèi)核會(huì)將這些文件描述符及其感興趣的事件類型(可讀、可寫、異常等)記錄在內(nèi)核的事件表中。
  • 應(yīng)用程序通過epoll_wait()系統(tǒng)調(diào)用進(jìn)入休眠狀態(tài),等待內(nèi)核通知有事件發(fā)生。
  • 內(nèi)核會(huì)不斷監(jiān)控這些文件描述符的狀態(tài)變化,一旦有事件發(fā)生,就會(huì)將就緒的文件描述符添加到就緒隊(duì)列中。
  • 當(dāng)epoll_wait()被喚醒時(shí),內(nèi)核會(huì)返回就緒隊(duì)列中的文件描述符列表以及就緒的事件類型。
  • 應(yīng)用程序根據(jù)返回的信息來處理對(duì)應(yīng)的I/O操作。

性能優(yōu)勢(shì):

  • epoll的時(shí)間復(fù)雜度接近O(1),而select是O(n),因此在處理大量文件描述符時(shí),epoll具有明顯的性能優(yōu)勢(shì)。
  • epoll沒有文件描述符數(shù)量的限制,可以支持海量的文件描述符。
  • epoll支持邊緣觸發(fā)(Edge Triggered)和水平觸發(fā)(Level Triggered)兩種工作模式,提供了更靈活的事件通知機(jī)制。

三、select與epoll的性能比較

時(shí)間復(fù)雜度:

  • select的時(shí)間復(fù)雜度為O(n),需要遍歷整個(gè)文件描述符集合來查找就緒的文件描述符。
  • epoll的時(shí)間復(fù)雜度接近O(1),因?yàn)樗褂脙?nèi)核事件表來存儲(chǔ)需要監(jiān)控的文件描述符,并只返回就緒的文件描述符。

文件描述符數(shù)量限制:

  • select有文件描述符數(shù)量的限制(通常是1024個(gè)),這在某些高并發(fā)場(chǎng)景下可能不夠用。
  • epoll沒有文件描述符數(shù)量的限制,可以支持海量的文件描述符。

系統(tǒng)資源消耗:

  • 當(dāng)監(jiān)視的文件描述符數(shù)量很大時(shí),select會(huì)消耗大量的系統(tǒng)資源,因?yàn)樗枰獜?fù)制整個(gè)文件描述符集合到用戶空間。
  • epoll則不會(huì)消耗大量的系統(tǒng)資源,因?yàn)樗恍枰谖募枋龇麪顟B(tài)發(fā)生變化時(shí)才更新內(nèi)核事件表。

可擴(kuò)展性:

  • 由于select的性能瓶頸和文件描述符數(shù)量限制,它在高并發(fā)場(chǎng)景下可能無法提供足夠的可擴(kuò)展性。
  • epoll則具有良好的可擴(kuò)展性,可以支持大量的并發(fā)連接和文件描述符。

面試官:epoll底層的數(shù)據(jù)結(jié)構(gòu)什么,請(qǐng)結(jié)合epoll的底層結(jié)構(gòu)說說epoll的工作機(jī)制。以及為什么epoll如此高效?

epoll底層主要使用了紅黑樹和就緒隊(duì)列(雙鏈表)這兩種數(shù)據(jù)結(jié)構(gòu),其高效性主要源于以下幾個(gè)方面:

一、epoll底層數(shù)據(jù)結(jié)構(gòu)

紅黑樹:

  • 用于存儲(chǔ)所有添加到epoll中的需要監(jiān)控的事件。
  • 紅黑樹的插入、刪除和查找操作的時(shí)間復(fù)雜度都是O(log n),其中n是樹中節(jié)點(diǎn)的數(shù)量。這使得epoll能夠高效地管理大量的文件描述符。

就緒隊(duì)列(雙鏈表):

  • 存放著將要通過epoll_wait返回給用戶的滿足條件的事件。
  • 當(dāng)有I/O事件發(fā)生時(shí),內(nèi)核會(huì)將對(duì)應(yīng)的事件添加到就緒隊(duì)列中。
  • epoll_wait在調(diào)用時(shí)只需要檢查就緒隊(duì)列中是否有事件即可,無需遍歷整個(gè)紅黑樹。

再聊聊epoll的具體實(shí)現(xiàn)機(jī)制。

如下圖所示:

當(dāng)某一進(jìn)程調(diào)用epoll_create方法時(shí),Linux內(nèi)核會(huì)創(chuàng)建一個(gè)eventpoll結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體中有兩個(gè)成員與epoll的使用方式密切相關(guān)。

struct eventpoll{
    ....
    /*紅黑樹的根節(jié)點(diǎn),這顆樹中存儲(chǔ)著所有添加到epoll中的需要監(jiān)控的事件*/
    struct rb_root  rbr;
    /*雙鏈表中則存放著將要通過epoll_wait返回給用戶的滿足條件的事件*/
    struct list_head rdlist;
    ....
};

每一個(gè)epoll對(duì)象都有一個(gè)獨(dú)立的eventpoll結(jié)構(gòu)體,用于存放通過epoll_ctl方法向epoll對(duì)象中添加進(jìn)來的事件。這些事件都會(huì)掛載在紅黑樹中,重復(fù)添加的事件就可以通過紅黑樹log(n)的查找復(fù)雜度高效的識(shí)別出來(其中n為樹的高度)。

所有添加到epoll中的事件都會(huì)與設(shè)備(如網(wǎng)卡)驅(qū)動(dòng)程序建立回調(diào)關(guān)系。這個(gè)回調(diào)方法會(huì)將就緒的事件添加到rdlist雙鏈表(就緒鏈表)中。

每一個(gè)事件對(duì)應(yīng)一個(gè)epitem結(jié)構(gòu)體,如下所示。

struct epitem{
    struct rb_node  rbn;//紅黑樹節(jié)點(diǎn)
    struct list_head    rdllink;//雙向鏈表節(jié)點(diǎn)
    struct epoll_filefd  ffd;  //事件句柄信息
    struct eventpoll *ep;    //指向其所屬的eventpoll對(duì)象
    struct epoll_event event; //期待發(fā)生的事件類型
}

當(dāng)調(diào)用epoll_wait檢查是否有事件發(fā)生時(shí),只需要檢查eventpoll對(duì)象中的就緒隊(duì)列rdlist中是否有epitem元素即可。如果rdlist不為空,則把發(fā)生的事件復(fù)制到用戶態(tài),同時(shí)將事件數(shù)量返回給用戶進(jìn)程,最后用戶進(jìn)程根據(jù)事件執(zhí)行相應(yīng)的讀寫處理。

二、epoll高效性原因

事件通知機(jī)制:

  • epoll使用事件通知的方式,當(dāng)監(jiān)控的文件描述符有事件發(fā)生時(shí)才會(huì)返回。
  • 這減少了不必要的系統(tǒng)調(diào)用和開銷,因?yàn)橹挥性谡嬲惺录l(fā)生時(shí)才會(huì)進(jìn)行后續(xù)的處理。

避免數(shù)據(jù)拷貝:

  • 在使用epoll時(shí),可以將要監(jiān)聽的文件描述符注冊(cè)到內(nèi)核中。
  • 之后,用戶程序只需等待內(nèi)核通知就可以,不必在每次調(diào)用時(shí)傳遞文件描述符列表。
  • 這降低了用戶空間和內(nèi)核空間之間的數(shù)據(jù)拷貝,提高了效率。

支持兩種工作模式:

  • epoll支持水平觸發(fā)(Level Triggered)和邊緣觸發(fā)(Edge Triggered)兩種工作模式。
  • 邊緣觸發(fā)模式僅在狀態(tài)變化時(shí)通知應(yīng)用程序,這允許開發(fā)者實(shí)現(xiàn)更高效的處理邏輯,因?yàn)榭梢员苊庵貜?fù)檢測(cè)已處理過的事件。

動(dòng)態(tài)管理文件描述符:

  • epoll沒有文件描述符數(shù)量的限制,能夠動(dòng)態(tài)管理大量的文件描述符。
  • 這使得epoll在需要高并發(fā)、高效能I/O操作的網(wǎng)絡(luò)編程場(chǎng)景中表現(xiàn)出色。

面試官:你剛剛有提到epoll的兩種觸發(fā)模式,請(qǐng)解釋一下邊緣觸發(fā)(ET)和水平觸發(fā)(LT)的區(qū)別?

一、邊緣觸發(fā)(ET)與水平觸發(fā)(LT)的區(qū)別

觸發(fā)機(jī)制:

  • 邊緣觸發(fā)(ET):只在文件描述符的狀態(tài)從不可讀/不可寫變?yōu)榭勺x/可寫時(shí)觸發(fā)一次。也就是說,它在狀態(tài)發(fā)生變化的時(shí)候通知應(yīng)用程序,且通知僅發(fā)送一次。
  • 水平觸發(fā)(LT):當(dāng)文件描述符處于可讀或可寫狀態(tài)時(shí),會(huì)持續(xù)通知應(yīng)用程序,直到文件描述符不再處于該狀態(tài)。即,只要文件描述符保持在可讀/可寫狀態(tài),epoll就會(huì)不斷通知應(yīng)用程序。

應(yīng)用程序響應(yīng):

  • 邊緣觸發(fā)(ET):要求應(yīng)用程序在接收到通知后立即且徹底地處理所有可讀/可寫的數(shù)據(jù),否則可能會(huì)錯(cuò)過后續(xù)數(shù)據(jù),導(dǎo)致數(shù)據(jù)丟失。因此,使用ET模式時(shí),應(yīng)用程序需要具備更高的復(fù)雜性和更精細(xì)的控制。
  • 水平觸發(fā)(LT):應(yīng)用程序可以按需處理數(shù)據(jù),不必立即處理完所有數(shù)據(jù)。即使只處理部分?jǐn)?shù)據(jù),下次調(diào)用epoll_wait時(shí),epoll仍會(huì)繼續(xù)通知應(yīng)用程序該文件描述符仍然可讀/可寫。

適用場(chǎng)景:

  • 邊緣觸發(fā)(ET):適用于高性能場(chǎng)景,因?yàn)樗鼫p少了不必要的系統(tǒng)調(diào)用。同時(shí),它也要求應(yīng)用程序能夠高效、徹底地處理數(shù)據(jù)。
  • 水平觸發(fā)(LT):適用于大多數(shù)情況,特別是當(dāng)應(yīng)用程序需要持續(xù)處理就緒事件或處理多個(gè)相關(guān)事件時(shí)。它提供了更簡(jiǎn)單、直觀的通知機(jī)制。

以下是一個(gè)使用Python的epoll模塊來演示ET和LT觸發(fā)模式的示例:


import os  
import socket  
import select  
import epoll  

# 創(chuàng)建一個(gè)TCP/IP套接字  
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  
server_sock.bind(('localhost', 12345))  
server_sock.listen(5)  
server_sock.setblocking(0)  # 設(shè)置為非阻塞模式  

# 創(chuàng)建一個(gè)epoll實(shí)例  
epoller = epoll.epoll()  

# 為服務(wù)器套接字注冊(cè)事件,LT模式  
epoller.register(server_sock.fileno(), epoll.EPOLLIN | epoll.EPOLLET if False else epoll.EPOLLIN)  # 將if False改為True以使用ET模式  

# 客戶端連接處理函數(shù)  
def handle_connection(client_sock):  
    data = client_sock.recv(1024)  
    if data:  
        print(f"Received: {data.decode()}")  
        client_sock.sendall(data)  # Echo the data back to the client  
    else:  
        # 沒有數(shù)據(jù)表示客戶端已經(jīng)關(guān)閉連接  
        client_sock.close()  
        print("Client disconnected.")  

# 主循環(huán)  
try:  
    while True:  
        events = epoller.poll(1000)  # 超時(shí)時(shí)間為1000毫秒  
        for fileno, event in events:  
            if fileno == server_sock.fileno():  
                # 服務(wù)器套接字上有新的連接請(qǐng)求  
                client_sock, client_addr = server_sock.accept()  
                client_sock.setblocking(0)  # 設(shè)置為非阻塞模式  
                print(f"Accepted connection from {client_addr}")  

                # 為客戶端套接字注冊(cè)事件,這里我們根據(jù)之前的條件選擇ET或LT模式  
                epoller.register(client_sock.fileno(), epoll.EPOLLIN | epoll.EPOLLET if False else epoll.EPOLLIN)  
            else:  
                # 客戶端套接字上有數(shù)據(jù)可讀或連接關(guān)閉等事件  
                client_sock = socket.socket(fileno=fileno)  # 從文件描述符恢復(fù)套接字對(duì)象  

                # 根據(jù)ET或LT模式的不同,處理數(shù)據(jù)的方式也會(huì)有所不同  
                if False:  # 如果為True,則表示使用ET模式  
                    # ET模式下,需要確保一次讀取所有可用數(shù)據(jù)  
                    while True:  
                        try:  
                            data = client_sock.recv(1024)  
                            if not data:  
                                break  # 沒有數(shù)據(jù)表示連接已關(guān)閉  
                            print(f"Received (ET): {data.decode()}")  
                            client_sock.sendall(data)  # Echo the data back to the client  
                        except BlockingIOError:  
                            # 在ET模式下,如果沒有更多數(shù)據(jù)可讀,會(huì)拋出BlockingIOError異常  
                            break  
                else:  
                    # LT模式下,每次有數(shù)據(jù)可讀時(shí)都會(huì)觸發(fā)事件  
                    handle_connection(client_sock)  

                # 如果客戶端連接已關(guān)閉,則注銷該套接字  
                if not client_sock.fileno() in [fd for fd, _ in epoller.poll(0)]:  # 使用非阻塞poll檢查連接是否仍然活躍  
                    epoller.unregister(client_sock.fileno())  
                    client_sock.close()  
except KeyboardInterrupt:  
    print("Server stopped by user.")  
finally:  
    epoller.close()  
    server_sock.close()

代碼中通過epoll.EPOLLIN | epoll.EPOLLET來選擇ET模式,而僅通過epoll.EPOLLIN來選擇LT模式。你可以通過修改條件(即if False改為if True)來切換模式。

  • 在ET模式下,由于只有在狀態(tài)變化時(shí)才會(huì)觸發(fā)事件,因此我們需要使用循環(huán)來讀取所有可用的數(shù)據(jù),直到recv拋出BlockingIOError異常,表示當(dāng)前沒有更多數(shù)據(jù)可讀。
  • 在LT模式下,每次有數(shù)據(jù)可讀時(shí)都會(huì)觸發(fā)事件,因此我們可以直接調(diào)用handle_connection函數(shù)來處理連接。

由于epoll模塊是Linux特有的,因此這段代碼只能在Linux環(huán)境下運(yùn)行。

為了簡(jiǎn)化示例,錯(cuò)誤處理和資源清理可能不是最完善的。在實(shí)際應(yīng)用中,你需要更仔細(xì)地處理這些情況。

使用epoll.poll(0)來檢查連接是否仍然活躍是一種非阻塞的檢查方式,但它可能不是最優(yōu)雅或最高效的方法。在實(shí)際應(yīng)用中,你可能需要設(shè)計(jì)更復(fù)雜的邏輯來處理連接的狀態(tài)。面試官:說說看你知道的IO模型有哪些?

責(zé)任編輯:趙寧寧 來源: 程序員阿沛
相關(guān)推薦

2023-07-11 08:40:02

IO模型后臺(tái)

2023-10-06 00:16:21

RedisMySQL事務(wù)

2020-04-16 15:20:43

PHP前端BIO

2020-11-04 07:49:04

Select

2022-04-16 16:52:24

Netty網(wǎng)絡(luò)服務(wù)器客戶端程序

2011-03-31 10:41:49

BIONIOIO

2021-03-05 08:51:00

Go語言make

2018-09-19 14:53:02

NIOBIO運(yùn)行

2015-10-21 10:24:05

TCPIP網(wǎng)絡(luò)協(xié)議

2022-11-15 10:03:34

2023-06-26 07:39:10

2021-06-11 17:26:06

代碼Java網(wǎng)絡(luò)編程

2023-10-23 11:07:37

HTTPRPC

2022-06-03 10:52:55

selectpolepoll

2023-12-06 07:28:47

阻塞IO異步IO

2020-10-10 19:37:27

BIO 、NIO 、A

2021-03-15 14:00:56

PythonC語言編程語言

2022-02-22 08:55:29

SelectPoll/ Epoll

2025-02-06 08:44:11

MySQLEXISTSIN

2016-03-21 10:40:53

RDDSpark SQL數(shù)據(jù)集
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 中文字幕亚洲一区二区三区 | 日韩精品福利 | 日韩视频在线免费观看 | 中文字幕亚洲欧美日韩在线不卡 | 免费精品在线视频 | 天堂综合网 | 亚洲黄色片免费观看 | 亚卅毛片| 国产jizz女人多喷水99 | 国产福利在线 | 美国十次成人欧美色导视频 | 宅女噜噜66国产精品观看免费 | 久久久久久av | 亚洲一区二区三区视频免费观看 | 日韩三级在线 | 国产精品99久久久久久宅男 | 九九导航 | 久久久久一区二区三区四区 | 国产一在线观看 | 久久精品久久久久久 | 欧美激情久久久 | 久久aⅴ乱码一区二区三区 亚洲国产成人精品久久久国产成人一区 | 国产乱码一区 | 欧美中文字幕在线观看 | 成人在线精品视频 | 国产精品久久久久影院色老大 | 中文字幕一页二页 | 国产精品久久久久久久久久久久久 | 国产乱码精品一区二区三区五月婷 | 国产欧美久久精品 | 91精品久久久久久久久久入口 | 人人九九精 | 亚洲啊v在线 | 一级欧美黄色片 | xxx.在线观看 | 国产精品美女久久久久久久网站 | 久久久久亚洲精品国产 | 国产精品久久久久久久久免费软件 | 久久国产精品久久久久 | 日韩av第一页 | 日本三级做a全过程在线观看 |