日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

I/O模型简述

發(fā)布時間:2025/3/21 编程问答 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 I/O模型简述 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1. 前言

最近在學(xué)習(xí) Java NIO 方面的知識,為了加深理解。特地去看了 Unix/Linux I/O 方面的知識,并寫了一些代碼進行驗證。在本文接下來的一章中,我將通過舉例的方式向大家介紹五種 I/O 模型。如果大家是第一次了解 I/O 模型方面的知識,理解起來會有一定的難度。所以在看文章的同時,我更建議大家動手去實現(xiàn)這些 I/O 模型,感覺會不一樣。好了,下面咱們一起進入正題吧。

?2. I/O 模型

本章將向大家介紹五種 I/O 模型,包括阻塞 I/O、非阻塞 I/O、I/O 復(fù)用、信號驅(qū)動式 I/O 、異步 I/O 等。本文的內(nèi)容參考了《UNIX網(wǎng)絡(luò)編程》,文中所用部分圖片也是來自于本書。關(guān)于《UNIX網(wǎng)絡(luò)編程》這本書,我想就不用多說了。很多寫網(wǎng)絡(luò)編程方面的文章一般都會參考該書,本文也不例外。如果大家想進深入學(xué)習(xí)網(wǎng)絡(luò)編程,建議去讀讀這本書。

?2.1 阻塞 I/O 模型

阻塞 I/O 是最簡單的 I/O 模型,一般表現(xiàn)為進程或線程等待某個條件,如果條件不滿足,則一直等下去。條件滿足,則進行下一步操作。相關(guān)示意圖如下:

上圖中,應(yīng)用進程通過系統(tǒng)調(diào)用 recvfrom 接收數(shù)據(jù),但由于內(nèi)核還未準(zhǔn)備好數(shù)據(jù)報,應(yīng)用進程就阻塞住了。直到內(nèi)核準(zhǔn)備好數(shù)據(jù)報,recvfrom 完成數(shù)據(jù)報復(fù)制工作,應(yīng)用進程才能結(jié)束阻塞狀態(tài)。

這里簡單解釋一下應(yīng)用進程和內(nèi)核的關(guān)系。內(nèi)核即操作系統(tǒng)內(nèi)核,用于控制計算機硬件。同時將用戶態(tài)的程序和底層硬件隔離開,以保障整個計算機系統(tǒng)的穩(wěn)定運轉(zhuǎn)(如果用戶態(tài)的程序可以控制底層硬件,那么一些病毒就會針對硬件進行破壞,比如 CIH 病毒)。應(yīng)用進程即用戶態(tài)進程,運行于操作系統(tǒng)之上,通過系統(tǒng)調(diào)用與操作系統(tǒng)進行交互。上圖中,內(nèi)核指的是 TCP/IP 等協(xié)議及相關(guān)驅(qū)動程序。客戶端發(fā)送的請求,并不是直接送達給應(yīng)用程序,而是要先經(jīng)過內(nèi)核。內(nèi)核將請求數(shù)據(jù)緩存在內(nèi)核空間,應(yīng)用進程通過 recvfrom 調(diào)用,將數(shù)據(jù)從內(nèi)核空間拷貝到自己的進程空間內(nèi)。大致示意圖如下:

阻塞 I/O 理解起來并不難,不過這里還是舉個例子類比一下。假設(shè)大家日常工作流程設(shè)這樣的(其實就是我日常工作的流程?),我們寫好代碼后,本地測試無誤,通過郵件的方式,告知運維同學(xué)發(fā)布服務(wù)。運維同學(xué)通過發(fā)布腳本打包代碼,重啟服務(wù)(心疼我司的人肉運維)。一般項目比較大時,重啟一次比較耗時。而運維同學(xué)又有點死腦筋,非要等這個服務(wù)重啟好,再去做其他事。結(jié)果一天等待的時間比真正工作的時間還要長,然后就被開了。運維同學(xué)用這個例子告訴我們,阻塞式 I/O 效率不太好。

?2.2 非阻塞 I/O 模型

與阻塞 I/O 模型相反,在非阻塞 I/O 模型下。應(yīng)用進程與內(nèi)核交互,目的未達到時,不再一味的等著,而是直接返回。然后通過輪詢的方式,不停的去問內(nèi)核數(shù)據(jù)準(zhǔn)備好沒。示意圖如下:

上圖中,應(yīng)用進程通過 recvfrom 系統(tǒng)調(diào)用不停的去和內(nèi)核交互,直到內(nèi)核準(zhǔn)備好數(shù)據(jù)報。從上面的流程中可以看出,應(yīng)用進程進入輪詢狀態(tài)時等同于阻塞,所以非阻塞的 I/O 似乎并沒有提高進程工作效率。

再用上面的例子進行類比。公司辭退了上一個怠工的運維同學(xué)后,又招了一個運維同學(xué)。這個運維同學(xué)每次重啟服務(wù),隔一分鐘去看一下,然后進入發(fā)呆狀態(tài)。雖然真正的工作時間增加了,但是沒用啊,等待的時間還是太長了。被公司發(fā)現(xiàn)后,又被辭了。

?2.3 I/O 復(fù)用模型

Unix/Linux 環(huán)境下的 I/O 復(fù)用模型包含三組系統(tǒng)調(diào)用,分別是 select、poll 和 epoll(FreeBSD 中則為 kqueue)。select 出現(xiàn)的時間最早,在 BSD 4.2中被引入。poll 則是在 AT&T System V UNIX 版本中被引入(詳情請參考 UNIX man-page)。epoll 出現(xiàn)在 Linux kernel 2.5.44 版本中,與之對應(yīng)的 kqueue 調(diào)用則出現(xiàn)在 FreeBSD 4.1,早于 epoll。select 和 poll 出現(xiàn)的時間比較早,在當(dāng)時也是比較先進的 I/O 模型了,滿足了當(dāng)時的需求。不過隨著因特網(wǎng)用戶的增長,C10K 問題出現(xiàn)。select 和 poll 已經(jīng)不能滿足需求了,研發(fā)更加高效的 I/O 模型迫在眉睫。到了 2000 年,FreeBSD 率先發(fā)布了 select、poll 的改進版 kqueue。Linux 平臺則在 2002 年 2.5.44 中發(fā)布了 epoll。好了,關(guān)于三者的一些歷史就說到這里。本節(jié)接下來將以 select 函數(shù)為例,簡述該函數(shù)的使用過程。

select 有三個文件描述符集(readfds),分別是可讀文件描述符集(writefds)、可寫文件描述符集和異常文件描述符集(exceptfds)。應(yīng)用程序可將某個 socket (文件描述符)設(shè)置到感興趣的文件描述符集中,并調(diào)用 select 等待所感興趣的事件發(fā)生。比如某個 socket 處于可讀狀態(tài)了,此時應(yīng)用進程就可調(diào)用 recvfrom 函數(shù)把數(shù)據(jù)從內(nèi)核空間拷貝到進程空間內(nèi),無需再等待內(nèi)核準(zhǔn)備數(shù)據(jù)了。示意圖如下:

一般情況下,應(yīng)用進程會將多個 socket 設(shè)置到感興趣的文件描述符集中,并調(diào)用 select 等待所關(guān)注的事件(比如可讀、可寫)處于就緒狀態(tài)。當(dāng)某些 socket 處于就緒狀態(tài)后,select 返回處于就緒狀態(tài)的 sockct 數(shù)量。注意這里返回的是 socket 的數(shù)量,并不是具體的 socket。應(yīng)用程序需要自己去確定哪些 socket 處于就緒狀態(tài)了,確定之后即可進行后續(xù)操作。

I/O 復(fù)用本身不是很好理解,所以這里還是舉例說明吧。話說公司的運維部連續(xù)辭退兩個運維同學(xué)后,運維部的 leader 覺得需要親自監(jiān)督一下大家工作。于是 leader 在周會上和大家說,從下周開始,所有的發(fā)布郵件都由他接收,并由他轉(zhuǎn)發(fā)給相關(guān)運維同學(xué),同時也由他重啟服務(wù)。各位運維同學(xué)需要告訴 leader 各自所負責(zé)監(jiān)控的項目,服務(wù)重啟好后,leader 會通過內(nèi)部溝通工具通知相關(guān)運維同學(xué)。至于服務(wù)重啟的結(jié)果(成功或失敗),leader 不關(guān)心,需要運維同學(xué)自己去看。運維同學(xué)看好后,需要把結(jié)果回復(fù)給開發(fā)同學(xué)。

上面的流程可能有點啰嗦,所以還是看圖吧。

把上面的流程進行分步,如下:

  • 開發(fā)同學(xué)將發(fā)布郵件發(fā)送給運維 leader,并指明這個郵件應(yīng)該轉(zhuǎn)發(fā)給誰
  • 運維告訴 leader,如果有發(fā)給我的郵件,請發(fā)送給我
  • leader 把郵件轉(zhuǎn)發(fā)給相關(guān)的運維同學(xué),并著手重啟服務(wù)
  • 運維同學(xué)看完郵件,告訴 leader 某某服務(wù)重啟好后,請告訴我
  • 服務(wù)重啟好,leader 通知運維同學(xué)xx服務(wù)啟動好了
  • 運維同學(xué)查看服務(wù)啟動情況,并返回信息給開發(fā)同學(xué)
  • 這種方式為什么可以提高工作效率呢?原因在于運維同學(xué)一股腦把他所負責(zé)的幾十個項目都告訴了 leader,由 leader 重啟服務(wù),并通知運維同學(xué)。運維同學(xué)這個時候等待 leader 的通知,只要其中一個或幾個服務(wù)重啟好了,運維同學(xué)就回接到通知,然后就可去干活了。而不是像以前一樣,非要等某個服務(wù)重啟好再進行后面的工作。

    說一下上面例子的角色扮演。開發(fā)同學(xué)是客戶端,leader 是內(nèi)核。開發(fā)同學(xué)發(fā)的郵件相當(dāng)于網(wǎng)絡(luò)請求,leader 接收郵件,并重啟服務(wù),相當(dāng)于內(nèi)核準(zhǔn)備數(shù)據(jù)。運維同學(xué)是服務(wù)端應(yīng)用進程,告訴 leader 自己感興趣的事情,并在最后將事情的處理結(jié)果返回給開發(fā)同學(xué)。

    不知道大家有沒有理解上面的例子,I/O 復(fù)用本身可能就不太好理解,所以看不懂也不要氣餒。另外,上面的例子只是為了說明情況,現(xiàn)實中并不會是這樣干,不然 leader 要累死了。如果大家覺得上面的例子不太好,我建議大家去看看權(quán)威資料《UNIX網(wǎng)絡(luò)編程》。同時,如果能用 select 寫個簡單的 tcp 服務(wù)器,有助于加深對 I/O 復(fù)用的理解。如果不會寫,也可以參考我寫的代碼?select_server.c。

    ?2.4 信號驅(qū)動式 I/O 模型

    信號驅(qū)動式 I/O 模型是指,應(yīng)用進程告訴內(nèi)核,如果某個 socket 的某個事件發(fā)生時,請向我發(fā)一個信號。在收到信號后,信號對應(yīng)的處理函數(shù)會進行后續(xù)處理。示意圖如下:

    再用之前的例子進行說明。某個運維同學(xué)比較聰明,他寫了一個監(jiān)控系統(tǒng)。重啟服務(wù)的過程由監(jiān)控系統(tǒng)來做,做好后,監(jiān)控系統(tǒng)會給他發(fā)個通知。在此之前,運維同學(xué)可以去做其他的事情,不用一直發(fā)呆等著了。運維同學(xué)收到通知后,首先去檢查服務(wù)重啟情況,接著再給開發(fā)同學(xué)回復(fù)郵件就行了。

    相比之前的工作方式,是不是感覺這種方式更合理。從流程上來說,這種方式確實更合理。進程在信號到來之前,可以去做其他事情,而不用忙等。但現(xiàn)實中,這種 I/O 模型用的并不多。

    ?2.5 異步 I/O 模型

    異步 I/O 是指應(yīng)用進程把文件描述符傳給內(nèi)核后,啥都不管了,完全由內(nèi)核去操作這個文件描述符。內(nèi)核完成相關(guān)操作后,會發(fā)信號告訴應(yīng)用進程,某某 I/O 操作我完成了,你現(xiàn)在可以進行后續(xù)操作了。示意圖如下:

    上圖通過 aio_read 把文件描述符、數(shù)據(jù)緩存空間,以及信號告訴內(nèi)核,當(dāng)文件描述符處于可讀狀態(tài)時,內(nèi)核會親自將數(shù)據(jù)從內(nèi)核空間拷貝到應(yīng)用進程指定的緩存空間呢。拷貝完在告訴進程 I/O 操作結(jié)束,你可以直接使用數(shù)據(jù)了。

    接著上一節(jié)的例子進行類比,運維小哥升級了他的監(jiān)控系統(tǒng)。此時,監(jiān)控系統(tǒng)不光可以監(jiān)控服務(wù)重啟狀態(tài),還能把重啟結(jié)果整理好,發(fā)送給開發(fā)小哥。而運維小哥要做的事情就更簡單了,收收郵件,點點監(jiān)控系統(tǒng)上的發(fā)布按鈕。然后就可以悠哉悠哉的繼續(xù)睡覺了,一天一天的就這么過去了。

    ?2.6 總結(jié)

    上面介紹了5種 I/O 模型,也通過舉例的形式對每種模型進行了補充說明,不知道大家看懂沒。拋開上面的 I/O 模型不談,如果某種 I/O 模型能讓進程的工作的時間大于等待的時間,那么這種模型就是高效的模型。在服務(wù)端請求量變大時,通過 I/O 復(fù)用模型可以讓進程進入繁忙的工作狀態(tài)中,減少忙等,進而提高了效率。

    I/O 復(fù)用模型結(jié)果數(shù)次改進,目前性能已經(jīng)很好了,也得到了廣泛應(yīng)用。像 Nginx,lighttd 等服務(wù)器軟件都選用該模型。好了,關(guān)于 I/O 模型就說到這里。

    最后附一張幾種 I/O 模型的對比圖:

    ?3. 寫在最后

    前面簡述了幾種 I/O 模型,并輔以例子進行說明。關(guān)于 I/O 模型的文章,網(wǎng)上有很多。大家也是各開腦洞,用了不同的例子進行類比說明,包括但不限于送外賣、送快遞、飛機調(diào)度等等。在寫這篇文章前,我也是絞盡腦汁,希望想一個不同的例子,不然如果和別人的太像,免不了有抄襲的嫌疑。除此之外,舉的例子還要盡量是大家都知道的,同時又能說明問題。所以這篇文章想例子想的也是挺累的。另外,限于本人語言水平,文中有些地方可能未能描述清楚。如果給大家造成了困擾,在這里說聲抱歉。最后聲明一下,本文的例子拿運維同學(xué)舉例,本人并無意黑運維同學(xué)。我們公司運維自動化程度不高,運維同事們還是很辛苦的,心疼5分鐘。

    好了,本文到這里就結(jié)束了,謝謝大家閱讀!

    ?參考

    • 《UNIX網(wǎng)絡(luò)編程》
    • Java NIO(3): IO模型 - 知乎
    • 本文鏈接:?https://www.tianxiaobo.com/2018/02/08/IO模型簡述/

    from:?http://www.tianxiaobo.com/2018/02/08/IO%E6%A8%A1%E5%9E%8B%E7%AE%80%E8%BF%B0/?

    總結(jié)

    以上是生活随笔為你收集整理的I/O模型简述的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。