<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          小白也看得懂的 I/O 多路復(fù)用解析

          共 7775字,需瀏覽 16分鐘

           ·

          2022-05-11 09:35

          前言

          IO多路復(fù)用目前在大廠的面試中,一般在兩個(gè)地方可能會被問到,一個(gè)是在問到網(wǎng)絡(luò)這一塊的時(shí)候,另一個(gè)是在問到 Redis 這一塊的時(shí)候,因?yàn)?Redis 底層也是使用了IO多路復(fù)用,所以整體來說 IO多路復(fù)用,也算是一道比較高頻的一個(gè)面試題,所以今天跟大家來分享一下。


          本文內(nèi)容有視頻版本,喜歡看視頻的同學(xué)可以直接通過下面的二維碼觀看。如果你對文章的內(nèi)容有疑惑,可以先看視頻的對應(yīng)內(nèi)容,視頻可能講的會更細(xì)一點(diǎn)。

          基礎(chǔ)概念

          首先我們了解下2個(gè)基礎(chǔ)概念,這2個(gè)概念在后續(xù)的文章中會反復(fù)用到。


          Socket

          套接字。百科:對網(wǎng)絡(luò)中不同主機(jī)上的應(yīng)用進(jìn)程之間進(jìn)行雙向通信的端點(diǎn)的抽象。

          例子1:客戶端將數(shù)據(jù)通過網(wǎng)線發(fā)送到服務(wù)端,客戶端發(fā)送數(shù)據(jù)需要一個(gè)出口,服務(wù)端接收數(shù)據(jù)需要一個(gè)入口,這兩個(gè)“口子”就是 Socket。

          例子2:兩個(gè)人通過電話進(jìn)行通信,兩個(gè)人都需要持有1個(gè)電話,socket 就類似于這個(gè)電話。


          FD:file descriptor

          文件描述符,非負(fù)整數(shù)。“一切皆文件”,linux 中的一切資源都可以通過文件的方式訪問和管理。而 FD 就類似文件的索引(符號、指針),指向某個(gè)資源,內(nèi)核(kernel)利用 FD 來訪問和管理資源。


          之前在視頻中有同學(xué)問既然有 socket,為什么文章內(nèi)容全是用的 FD 來舉例,這是因?yàn)楫?dāng)我們調(diào)用內(nèi)核函數(shù)創(chuàng)建 socket 后,內(nèi)核返回給我們的是 socket 對應(yīng)的文件描述符(fd),所以我們對 socket 的操作基本都是通過 fd 來進(jìn)行。


          Socket 通信

          接著我們通過一張圖來看下客戶端和服務(wù)器使用 socket 進(jìn)行通信的核心流程。

          圖中函數(shù)的含義如下:

          socket:創(chuàng)建一個(gè)套接字

          bind:將 socket 綁定到指定地址

          listen:使套接字處于監(jiān)聽狀態(tài),等待客戶端連接到來

          accept:接受客戶端連接

          connect:客戶端發(fā)起連接

          read:從 fd 對應(yīng)的 socket 中讀取數(shù)據(jù)

          write:將數(shù)據(jù)寫入 fd 對應(yīng)的 socket 中

          close:關(guān)閉 socket 文件描述符

          核心交互流程如下:

          1)服務(wù)器端通過 socket、bind、listen 對 socket 進(jìn)行初始化,最后阻塞在 accept 等待客戶端請求到來。

          2)客戶端通過 socket 進(jìn)行初始化,然后使用 connect 向服務(wù)端發(fā)起連接請求。此時(shí)客戶端會和服務(wù)端進(jìn)行 TCP 三次握手,三次握手完成后,客戶端和服務(wù)端建立連接完畢,開始進(jìn)入數(shù)據(jù)傳輸過程。

          3)客戶端發(fā)起 write 系統(tǒng)調(diào)用寫入數(shù)據(jù),數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間 socket 緩沖區(qū),最后內(nèi)核將數(shù)據(jù)通過網(wǎng)絡(luò)發(fā)送到服務(wù)器。

          4)數(shù)據(jù)經(jīng)過網(wǎng)絡(luò)傳輸?shù)竭_(dá)服務(wù)器網(wǎng)卡,接著內(nèi)核將數(shù)據(jù)拷貝到對應(yīng)的 socket 接收隊(duì)列,最后將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間。

          5)客戶端和服務(wù)器完成交互后,調(diào)用 close 函數(shù)來斷開連接。


          IO模型小例子

          接著我們通過一個(gè)例子來了解下各種IO模型。

          例子:你是一個(gè)老師,讓學(xué)生做作業(yè),學(xué)生做完作業(yè)后收作業(yè)。

          同步阻塞:逐個(gè)收作業(yè),先收A,再收B,接著是C、D,如果有一個(gè)學(xué)生還未做完,則你會等到他寫完,然后才繼續(xù)收下一個(gè)。

          解析:這就是同步阻塞的特點(diǎn),只要中間有一個(gè)未就緒,則你會被阻塞住,從而影響到后面的其他學(xué)生。

          同步非阻塞:逐個(gè)收作業(yè),先收A,再收B,接著是C、D,如果有一個(gè)學(xué)生還未做完,則你會跳過該學(xué)生,繼續(xù)去收下一個(gè)。

          解析:可以看到同步非阻塞相較于同步阻塞已經(jīng)是更好的方案了,你不會因?yàn)槟硞€(gè)學(xué)生未就緒而阻塞住,這樣就可以減少對后續(xù)學(xué)生的影響。但是這個(gè)方案也可能會出現(xiàn)其他問題,如果你下去收作業(yè)的時(shí)候,全部學(xué)生都還沒做完,則你可能會白走一圈,然后一個(gè)作業(yè)也沒收到。

          select/poll:學(xué)生寫完了作業(yè)會舉手,但是你不知道是誰舉手,需要一個(gè)個(gè)的去詢問。

          解析:這個(gè)方案相較于同步非阻塞來說有一點(diǎn)好處,就是你是確認(rèn)有學(xué)生做完的,所以你下去肯定能收到作業(yè),但是他有一個(gè)不好的點(diǎn)在于你需要一個(gè)個(gè)的去詢問。

          epoll:學(xué)生寫完了作業(yè)會舉手,你知道是誰舉手,你直接去收作業(yè)。

          解析:這個(gè)方案就很高效了,每次都能準(zhǔn)確的收到作業(yè)。


          同步阻塞IO

          核心流程:當(dāng)應(yīng)用程序發(fā)起 read 系統(tǒng)調(diào)用時(shí),在內(nèi)核數(shù)據(jù)沒有準(zhǔn)備好之前,應(yīng)用程序會一直處于阻塞等待狀態(tài),直到內(nèi)核把數(shù)據(jù)準(zhǔn)備好了返回給應(yīng)用程序


          交互流程

          我們通過兩段代碼的一個(gè)動(dòng)圖模擬同步阻塞IO下服務(wù)端和客戶端的執(zhí)行流程:


          大致流程如下:

          1)服務(wù)端進(jìn)行初始化:新建 socket、綁定地址、轉(zhuǎn)為服務(wù)端 socket

          2)服務(wù)端調(diào)用 accept,進(jìn)入阻塞狀態(tài),等待客戶端連接

          3)客戶端新建 socket,向服務(wù)端發(fā)起連接

          4)服務(wù)端和客戶端通過 TCP 三次握手建立連接

          5)服務(wù)端繼續(xù)執(zhí)行 read 函數(shù),進(jìn)入阻塞狀態(tài),等待客戶端發(fā)送數(shù)據(jù)

          6)客戶端向服務(wù)端發(fā)送數(shù)據(jù)

          7)服務(wù)端讀取數(shù)據(jù),執(zhí)行邏輯處理


          同步阻塞IO模型

          我們通過 read 函數(shù)來看下服務(wù)器內(nèi)部用戶空間和內(nèi)核空間的調(diào)用流程,如下圖所示:

          大致流程如下:

          1)應(yīng)用進(jìn)程發(fā)起 read 系統(tǒng)調(diào)用

          2)應(yīng)用進(jìn)程阻塞等待數(shù)據(jù)就緒

          3)數(shù)據(jù)通過網(wǎng)絡(luò)傳輸?shù)竭_(dá)網(wǎng)卡,然后再到內(nèi)核socket緩沖區(qū),當(dāng)數(shù)據(jù)被拷貝到內(nèi)核 socket 緩沖區(qū)時(shí),此時(shí)處于就緒狀態(tài)

          4)將數(shù)據(jù)從內(nèi)核拷貝到應(yīng)用程序緩沖區(qū),返回成功


          多線程版本:文中使用的例子是單線程,如果是多線程則在每個(gè) socket 建立連接后新建線程去負(fù)責(zé)處理該 socket 后續(xù)的流程,這樣就不會由于單個(gè) socket 阻塞住而影響到其他 socket。


          總結(jié)

          單線程:某個(gè) socket 阻塞,會影響到其他 socket 處理。

          多線程:當(dāng)客戶端較多時(shí),會造成資源浪費(fèi),全部 socket 中可能每個(gè)時(shí)刻只有幾個(gè)就緒。同時(shí),線程的調(diào)度、上下文切換乃至它們占用的內(nèi)存,可能都會成為瓶頸。


          同步非阻塞IO

          核心流程:當(dāng)應(yīng)用程序發(fā)起 read 系統(tǒng)調(diào)用時(shí),在內(nèi)核數(shù)據(jù)沒有準(zhǔn)備好之前,內(nèi)核會直接返回錯(cuò)誤,應(yīng)用程序不斷輪詢內(nèi)核,直到內(nèi)核把數(shù)據(jù)準(zhǔn)備好了返回給應(yīng)用程序。


          交互流程

          我們通過兩段代碼的一個(gè)動(dòng)圖來模擬同步阻塞IO下服務(wù)端和客戶端的執(zhí)行流程:


          大致流程如下:

          1)服務(wù)端調(diào)用 accept,數(shù)據(jù)未就緒,內(nèi)核返回-1

          2)服務(wù)端調(diào)用 accept,數(shù)據(jù)未就緒,內(nèi)核返回-1

          3)服務(wù)端調(diào)用 accept,數(shù)據(jù)未就緒,內(nèi)核返回-1

          4)客戶端新建 socket,向服務(wù)端發(fā)起連接

          4)服務(wù)端調(diào)用 accept,服務(wù)端和客戶端通過 TCP 三次握手建立連接

          5)服務(wù)端執(zhí)行后續(xù)邏輯處理


          我們通過 read 函數(shù)來看下服務(wù)器內(nèi)部用戶空間和內(nèi)核空間的調(diào)用流程,如下圖所示:

          大致流程如下:

          1)服務(wù)端調(diào)用 read,數(shù)據(jù)未就緒,內(nèi)核返回-1

          2)服務(wù)端調(diào)用 read,數(shù)據(jù)未就緒,內(nèi)核返回-1

          3)服務(wù)端調(diào)用?read,數(shù)據(jù)就緒

          4)將數(shù)據(jù)從內(nèi)核拷貝到應(yīng)用程序緩沖區(qū),返回成功


          同步非阻塞IO模型


          總結(jié):提供了非阻塞調(diào)用的方式,從操作系統(tǒng)層面解決了阻塞問題。

          優(yōu)點(diǎn):單個(gè) socket 阻塞,不會影響到其他 socket?

          缺點(diǎn):需要不斷的遍歷進(jìn)行系統(tǒng)調(diào)用,有一定開銷


          SELECT

          核心流程:

          1)應(yīng)用程序首先發(fā)起 select 系統(tǒng)調(diào)用,傳入要監(jiān)聽的文件描述符集合

          2)內(nèi)核遍歷應(yīng)用程序傳入的?fd 集合,如果遍歷完一遍后發(fā)現(xiàn)沒有就緒的 fd 則用戶進(jìn)程會進(jìn)入阻塞狀態(tài),如果有就緒的 fd 則會對就緒的 fd 打標(biāo),然后返回

          3)應(yīng)用程序遍歷 fd 集合,找到就緒的 fd,進(jìn)行相應(yīng)的事件處理


          select 接口

          /** * 獲取就緒事件 * * @param nfds      3個(gè)監(jiān)聽集合的文件描述符最大值+1 * @param readfds   要監(jiān)聽的可讀文件描述符集合 * @param writefds  要監(jiān)聽的可寫文件描述符集合 * @param exceptfds 要監(jiān)聽的異常文件描述符集合 * @param timeval   本次調(diào)用的超時(shí)時(shí)間 * @return 大于0:已就緒的文件描述符數(shù);等于0:超時(shí);小于:出錯(cuò) */int select(int nfds,           fd_set *readfds,           fd_set *writefds,           fd_set *exceptfds,           struct timeval *timeout);
          ?


          交互流程

          我們通過一個(gè)動(dòng)圖來模擬服務(wù)器內(nèi)部用戶空間和內(nèi)核空間的調(diào)用流程,如下圖所示:

          大致流程如下:

          1)用戶空間發(fā)起 select 系統(tǒng)調(diào)用,將監(jiān)聽的 fd 集合從用戶空間拷貝到內(nèi)核空間

          2)內(nèi)核遍歷 fd 集合,檢查數(shù)據(jù)是否就緒

          3)如果遍歷一遍后發(fā)現(xiàn)沒有 fd 就緒,則會將當(dāng)前用戶進(jìn)程阻塞,讓出 CPU 給其他進(jìn)程

          4)當(dāng)客戶端將數(shù)據(jù)發(fā)送到服務(wù)端,進(jìn)入內(nèi)核后,會通過數(shù)據(jù)庫包找到對應(yīng)的socket?

          PS:客戶端發(fā)送數(shù)據(jù)到數(shù)據(jù)進(jìn)入服務(wù)端內(nèi)核的流程類似下面 epoll 的流程

          5)socket 檢查是否有阻塞等待的進(jìn)程,如果有則喚醒該進(jìn)程

          6)用戶進(jìn)程恢復(fù)運(yùn)行后,會再遍歷 fd?集合進(jìn)行檢查,此時(shí)它會檢查到某些 fd 已經(jīng)就緒了,它會給這些 fd 打上標(biāo)記,然后結(jié)束阻塞,返回到用戶空間

          7)用戶空間知道有事件就緒,遍歷 fd 集合,找到就緒的 fd,進(jìn)行相應(yīng)的事件處理,例如將數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到應(yīng)用程序緩沖區(qū)

          8)最后執(zhí)行邏輯處理。


          IO多路復(fù)用模型


          fd_set

          fd_set 在 select 的整個(gè)調(diào)用過程中表達(dá)了兩種不同的意思。

          在入?yún)r(shí),fd_set 表示應(yīng)用程序要監(jiān)聽哪些 fd;在回參時(shí),fd_set表示哪些 fd 已經(jīng)就緒了。

          應(yīng)用程序傳入的 fd_set 其實(shí)是個(gè)位圖,例如我們要監(jiān)聽 fd = 1、fd = 4,則傳入 0000 0101,也就是 5。

          這邊使用的 long 類型數(shù)組來實(shí)現(xiàn)位圖:1個(gè) long 可以表示64位,則16個(gè)long可以表示1024位。

          當(dāng)內(nèi)核處理完畢,將就緒的 fd 返回時(shí),會將就緒的 fd 對應(yīng)的位標(biāo)記為1,然后覆蓋掉入?yún)⒌?fd_set,所以我們最終返回時(shí)的 fd_set 表示的是哪些 fd 是就緒的。


          總結(jié)

          • 將 socket 是否就緒檢查邏輯下沉到操作系統(tǒng)層面,避免大量系統(tǒng)調(diào)用。

          • 告訴你有事件就緒,但是沒告訴你具體是哪個(gè) FD。


          優(yōu)點(diǎn)

          • 不需要每個(gè) FD 都進(jìn)行一次系統(tǒng)調(diào)用,解決了頻繁的用戶態(tài)內(nèi)核態(tài)切換問題


          缺點(diǎn)

          • 單進(jìn)程監(jiān)聽的 FD 存在限制,默認(rèn)1024

          • 每次調(diào)用需要將 FD 從用戶態(tài)拷貝到內(nèi)核態(tài)

          • 不知道具體是哪個(gè)文件描述符就緒,需要遍歷全部文件描述符

          • 入?yún)⒌?個(gè) fd_set 集合每次調(diào)用都需要重置



          POLL

          核心流程:基本同 select。

          poll 接口

          /** * 獲取就緒事件 * * @param pollfd  要監(jiān)聽的文件描述符集合 * @param nfds    文件描述符數(shù)量 * @param timeout 本次調(diào)用的超時(shí)時(shí)間 * @return 大于0:已就緒的文件描述符數(shù);等于0:超時(shí);小于:出錯(cuò) */int poll(struct pollfd *fds,         unsigned int nfds,         int timeout);
          struct pollfd { int fd; // 監(jiān)聽的文件描述符 short events; // 監(jiān)聽的事件 short revents; // 就緒的事件}
          ?

          poll 函數(shù)基本同 select,只是對 select 進(jìn)行了一些小優(yōu)化,一個(gè)是優(yōu)化了1024個(gè)文件描述符上限,另一個(gè)是新定義了 pollfd 數(shù)據(jù)結(jié)構(gòu),使用兩個(gè)不同的變量來表示監(jiān)聽的事件和就緒的事件,這樣就不需要像 select 那樣每次重置 fd_set 了。


          總結(jié)跟 select 基本類似,主要優(yōu)化了監(jiān)聽1024的限制。

          優(yōu)點(diǎn)

          • 不需要每個(gè) FD 都進(jìn)行一次系統(tǒng)調(diào)用,導(dǎo)致頻繁的用戶態(tài)內(nèi)核態(tài)切換

          缺點(diǎn)

          • 每次需要將 FD 從用戶態(tài)拷貝到內(nèi)核態(tài)

          • 不知道具體是哪個(gè)文件描述符就緒,需要遍歷全部文件描述符


          EPOLL

          核心流程:

          1)應(yīng)用程序調(diào)用 epoll_create,內(nèi)核會分配一塊內(nèi)存空間,創(chuàng)建一個(gè) epoll,最后將 epoll 的 fd 返回,我們后續(xù)可以通過這個(gè) fd 來操作 epoll 對象

          2)應(yīng)用程序不斷調(diào)用 epoll_ctl 將我們要監(jiān)聽的 fd 維護(hù)到 epoll,內(nèi)核通過紅黑樹的結(jié)構(gòu)來高效的維護(hù)我們傳入的 fd 集合

          3)應(yīng)用程序調(diào)用?epoll_wait 來獲取就緒事件,內(nèi)核檢查 epoll 的就緒列表,如果就緒列表為空則會進(jìn)入阻塞,否則直接返回就緒的事件。

          4)應(yīng)用程序根據(jù)內(nèi)核返回的就緒事件,進(jìn)行相應(yīng)的事件處理


          epoll 接口

          /** * 創(chuàng)建一個(gè)epoll * * @param size epoll要監(jiān)聽的文件描述符數(shù)量 * @return epoll的文件描述符 */int epoll_create(int size);
          /** * 事件注冊 * * @param epfd epoll的文件描述符,epoll_create創(chuàng)建時(shí)返回 * @param op 操作類型:新增(1)、刪除(2)、更新(3) * @param fd 本次要操作的文件描述符 * @param epoll_event 需要監(jiān)聽的事件:讀事件、寫事件等 * @return 如果調(diào)用成功返回0, 不成功返回-1 */int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
          /** * 獲取就緒事件 * * @param epfd epoll的文件描述符,epoll_create創(chuàng)建時(shí)返回 * @param events 用于回傳就緒的事件 * @param maxevents 每次能處理的最大事件數(shù) * @param timeout 等待I/O事件發(fā)生的超時(shí)時(shí)間,-1相當(dāng)于阻塞,0相當(dāng)于非阻塞 * @return 大于0:已就緒的文件描述符數(shù);等于0:超時(shí);小于:出錯(cuò) */int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
          ?


          交互流程

          我們通過一個(gè)動(dòng)圖來模擬服務(wù)器內(nèi)部用戶空間和內(nèi)核空間的調(diào)用流程,如下圖所示:

          大致流程如下:

          1)用戶空間調(diào)用?epoll_create?,內(nèi)核新建 epoll 對象,返回 epoll 的 fd,用于后續(xù)操作

          2)用戶空間反復(fù)調(diào)用 epoll_ctl 將我們要監(jiān)聽的 fd 維護(hù)到 epoll,底層通過紅黑樹來高效的維護(hù) fd 集合

          3)用戶空間調(diào)用 epoll_wait 獲取就緒事件,內(nèi)核檢查 epoll 的就緒列表,如果就緒列表為空則會進(jìn)入阻塞

          4)客戶端向服務(wù)端發(fā)送數(shù)據(jù),數(shù)據(jù)通過網(wǎng)絡(luò)傳輸?shù)椒?wù)端的網(wǎng)卡

          5)網(wǎng)卡通過?DMA 的方式將數(shù)據(jù)包寫入到指定內(nèi)存中(ring_buffer),處理完成后通過中斷信號告訴 CPU 有新的數(shù)據(jù)包到達(dá)

          6)CPU 收到中斷信號后,進(jìn)行響應(yīng)中斷,首先保存當(dāng)前執(zhí)行程序的上下文環(huán)境,然后調(diào)用中斷處理程序(網(wǎng)卡驅(qū)動(dòng)程序)進(jìn)行處理:

          • 根據(jù)數(shù)據(jù)包的ip和port找到對應(yīng)的socket,將數(shù)據(jù)放到socket的接收隊(duì)列;

          • 執(zhí)行 socket 對應(yīng)的回調(diào)函數(shù):將當(dāng)前 socket 添加到 eventpoll 的就緒列表、喚醒 eventpool 等待隊(duì)列里的用戶進(jìn)程(設(shè)置為RUNNING狀態(tài))

          7)用戶進(jìn)程恢復(fù)運(yùn)行后,檢查 eventpoll 里的就緒列表不為空,則將就緒事件填充到入?yún)⒅械?events 里,然后返回

          8)用戶進(jìn)程收到返回的事件后,執(zhí)行 events 里的事件處理,例如讀事件則將數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到應(yīng)用程序緩沖區(qū)

          9)最后執(zhí)行邏輯處理。


          IO多路復(fù)用模型

          同 select。


          總結(jié)

          epoll 直接將 fd 集合維護(hù)在內(nèi)核中,通過紅黑樹來高效管理 fd 集合,同時(shí)維護(hù)一個(gè)就緒列表,當(dāng) fd 就緒后會添加到就緒列表中,當(dāng)應(yīng)用空間調(diào)用 epoll_wait 獲取就緒事件時(shí),內(nèi)核直接判斷就緒列表即可知道是否有事件就緒。


          優(yōu)點(diǎn)

          解決了 select 和 poll 的缺點(diǎn),高效處理高并發(fā)下的大量連接,同時(shí)有非常優(yōu)異的性能。


          缺點(diǎn)

          • 跨平臺性不夠好,只支持 linux,macOS 等操作系統(tǒng)不支持

          • 相較于 epoll,select 更輕量可移植性更強(qiáng)

          • 在監(jiān)聽連接數(shù)和事件較少的場景下,select 可能更優(yōu)


          LT?VS ET

          LT:Level-triggered,水平(條件)觸發(fā),默認(rèn)。epoll_wait 檢測到事件后,如果該事件沒被處理完畢,后續(xù)每次 epoll_wait 調(diào)用都會返回該事件。

          ET:Edge-triggered,邊緣觸發(fā)。epoll_wait 檢測到事件后,只會在當(dāng)次返回該事件,不管該事件是否被處理完畢。

          小結(jié)

          epoll 和 select、poll 默認(rèn)都是 LT 模式,LT 模式會更安全一點(diǎn),而 ET 則是 epoll 為了性能開發(fā)的一種新模式,LT 模式下內(nèi)核在返回就緒事件之前都會進(jìn)行一次額外的判斷,如果 fd 量較大,會有一定的性能損耗。


          總結(jié)

          可以看到從最初的同步阻塞IO,到現(xiàn)在主流的 epoll,其實(shí)是一個(gè)不斷演進(jìn)的過程,就像我們的業(yè)務(wù)系統(tǒng)一樣。

          同步阻塞IO的方式實(shí)現(xiàn)比較簡單,同時(shí)在當(dāng)時(shí)可能已經(jīng)能滿足需求了,因此被最早提出來,然后隨著不斷的發(fā)展,在一些場景下,同步阻塞IO逐漸不能滿足需求,于是操作系統(tǒng)底層開始優(yōu)化,提出了非阻塞的模式。類似的,同步非阻塞IO也存在一定的問題,于是就有了后續(xù)的IO多路復(fù)用。

          現(xiàn)在還有一種更牛逼的IO模型也在發(fā)展,叫做異步IO,這種模型下,你只需要一次非阻塞的系統(tǒng)調(diào)用,后續(xù)的事情全部由內(nèi)核來幫你完成。不過異步IO當(dāng)前在 linux 下還不夠完善,所以當(dāng)前 linux 的主流還是 epoll。


          推薦閱讀

          全網(wǎng)最實(shí)用的 IDEA Debug 調(diào)試技巧(超詳細(xì)案例)

          百萬級QPS,支撐淘寶雙11需要哪些技術(shù)

          面試官:如何進(jìn)行 JVM 調(diào)優(yōu)(附真實(shí)案例)

          Java 基礎(chǔ)高頻面試題(2021年最新版)

          Java 集合框架高頻面試題(2021年最新版)

          面試必問的 Spring,你懂了嗎?

          面試必問的 MySQL,你懂了嗎?

          最近我將面試:阿里、字節(jié)、美團(tuán)、快手、拼多多等大廠的高頻面試整理出來,并按大廠的標(biāo)準(zhǔn)給出自己的解析。

          群里有不少同學(xué)看完拿下了阿里、美團(tuán)等大廠 Offer,希望能助你一臂之力,早日拿下大廠 Offer。

          獲取方式:關(guān)注公眾號回復(fù)【面試】即可領(lǐng)取,更多大廠面試真題解析 PDF 整理中。

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

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  抠逼网站 | 国产乱码在线 | 久久视频这里有精品 | 精品999久久久一级毛片 | 偷拍自拍色图 |