【網絡IO系列 二】IO的五種模型,BIO、NIO、AIO、IO多路復用、 信號驅動IO


前言

在上一篇文章中,我們了解了操作系統中內核程序和用戶程序之間的區別和聯系,還提到了內核空間和用戶空間,當我們需要讀取一條數據的時候,首先需要發請求告訴內核,我需要什么數據,等內核准備好數據之后再從內核空間拷貝到用戶空間 注意加粗的部分,這兩個階段至關重要

對以上的兩個過程以及操作系統的IO流程不了解的,請務必左轉去看上一篇文章,上篇文章中是學習IO的基礎知識,只有把上一篇文章的內容看懂了,對於后續的IO幾種模型的學習和理解才會更為深刻,上一篇文章可以說是整個IO中的基石級別的知識。

文章鏈接 【網絡IO系列】 預備知識 操作系統之內核程序和用戶程序

IO的五種模型

我們回到正題,從上篇文章我們知道,當我們進行一次IO的時候,是要經過這兩個階段的,分別是

第一階段 :等待內核准備數據

第二階段:數據從內核空間拷貝到用戶空間

這兩個階段則決定着IO的各種模型的類型,通過這兩個階段,可以將IO模型分成五種,分別是

  • 阻塞式IO(BIO)
  • 非阻塞式IO(NIO)
  • IO多路復用
  • 信號驅動IO
  • 全異步IO(AIO)

阻塞

說到阻塞,在這里想先明確一下,什么是阻塞。從線程或者進程的角度來看,阻塞就是因為當前執行的這個線程,暫時的失去了CPU的執行權,被掛起等待下一次線程的調度或者線程被喚醒。如果是具體到我們的IO的話,則可以理解為,阻塞的時候你需要等待,等到數據准備好或者執行結果返回,才能繼續下一步的操作,不然只能一直等待下去。而非阻塞,則是,即便數據沒有准備好,或者執行沒有完成,你也可以去做其他的事情。

舉個例子,比如說你出門排隊買東西,假如你沒有帶手機,那你只能老老實實排隊等輪到你,在這之前,你除了排隊這件事之外,其他什么事情都干不了也不允許干,這個時候你就是阻塞的,因為你只能做排隊這一件事。那么同樣是排隊,這一次你帶了手機,那你排隊的時候,可以邊玩手機邊排隊,這個時候你就是非阻塞的。

接下來讓我們對應到上面的兩個階段,如果等待內核准備數據的時候,執行線程可以去做其他的事,那么在第一階段就是非阻塞的,否則就是阻塞的。如果在數據從內核空間拷貝到用戶空間階段,執行線程可以去干其他的事,那么第二階段就是非阻塞的,否則就是阻塞的。

阻塞式IO(BIO)

阻塞式IO,是在兩個階段都阻塞的一種IO模型,用戶發起IO請求,在等待數據和數據拷貝階段,都會被阻塞,只有這兩個階段都完成了,才能去做下一階段的事情。

就像是你沒帶手機去吃飯,你跟老板說要吃魚香肉絲,然后要等老板做好菜(准備數據),然后從廚房把菜端到你面前(數據拷貝),這兩個階段你都只能等着,什么事都干不了。

由於BIO阻塞時間長,因此相對性能就會較低,所以現在用的相對也比較少了。

非阻塞IO(NIO)

非阻塞IO,可以看作是半阻塞IO,因為他在第一階段數據准備階段不阻塞,第二階段數據拷貝階段阻塞,當用戶發出IO請求的時候,會有一個線程去詢問內核數據准備好了嗎,一直問一直問,在這期間,用戶主進程可以去干其他的事,等數據准備好了,到了第二階段,這個時候,用戶線程就要執行拷貝數據,這個時候是阻塞的。這種方式的缺點就是反復的輪訓去詢問內核數據好了沒,是很消耗CPU資源的。

就像是你帶手機去吃飯,你點好菜之后,你可以一直問老板,我的菜好了沒,老板說沒有,問完之后就可以繼續玩手機繼續等,繼續問。等到有一次你問,老板我的菜好了嗎,老板說好了,你自己過來端一下。(注意,問菜好沒有的,得是你自己問,這家NIO店的老板比較高冷,菜好了你不問他是不會主動告訴你的,這就是NIO的特點,數據准備就緒是用戶線程主動發出的詢問),這個時候菜好了,你要自己去端(數據拷貝),這個端菜的階段,你期間啥都干不了,也不能玩手機,所以NIO的第二個階段是阻塞的。

說到這里我們可以看出BIO和NIO之間的區別了,一個是傻等老板做菜給你,期間你什么都干不了,一個是自己主動詢問老板,菜好了沒,期間你可以玩手機,或者干其他的,相比BIO,NIO的效率就高了很多。當然,你可能會問了,為啥菜好沒好,還得我自己主動去問,這也太不人性化了,確實,這個問題我們想得到,計算機的科學界大師們自然也想得到,於是為了解決這個問題,於是出現了信號驅動IO和IO多路復用。

IO多路復用

通過我們上面對NIO的了解,我們可以知道,NIO多少存在着一些不夠好的地方,因為反復的輪訓也是很消耗cpu資源的。如果飯店的人少還好說,但是如果飯店人多起來了,比如說來了幾百個人,那每個人時不時就要發起一次詢問請求,那老板管不過來啊,cpu占用率也會非常高。於是,IO多路復用就出現了,IO多路復用可以說是目前用的最多的一個IO模型,在不同的操作系統內核,也有不同的實現方式,在這篇文章中,我們IO多路復用的大概思想,至於詳細介紹,后面會用一篇文章來詳細的介紹IO多路復用

IO多路復用,實際上,是通過IO請求都通過一個selector來管理,用戶進程的IO請求就不直接發給內核處理程序了,而是注冊到這個selector上面,由selector來告訴內核需要哪些數據,然后定時的去查詢內核程序,我這個selector上需要的數據,有哪些准備好了,然后再由selector告訴那些准備好了的用戶線程,讓該用戶線程去拷貝數據。在非阻塞IO中,不斷地詢問狀態時通過用戶線程去進行的,而在IO多路復用中,詢問每個狀態是內核在進行的,在IO請求非常多的時候,這個效率要比用戶線程輪詢要高的多。

就像是你帶手機去飯店吃飯,現在這家飯店的老板由於生意越來越好,人越來越多,他有點管理不過來了,於是他請了幾個服務員(selector)協助管理,然后現在飯店客戶的點餐都是告訴服務員,我需要什么菜,然后服務員把xx桌客戶的菜,記在自己的單子上。然后服務員告訴廚房他這個單子上需要哪些菜,讓廚房去做。。服務員定時問廚房看看有哪些菜已經准備好了,然后告知15號桌和89號桌客人你們的菜已經好了,請來前台端一下,然后你就去前台端菜,端菜的階段是阻塞的。

來比較一下IO多路復用和NIO,我們可以發現,當IO請求多的時候,IO多路復用效率無疑是更高的。因為對於用戶線程來說,你點完菜就可以一直玩手機了,不用因為一直問老板而分心分神,耽誤你打王者,因為菜好了,服務員會通知你

信號驅動IO

通過我們上面兩種IO模型的了解,我們可以知道,不管是NIO還是IO多路復用,本質上還是輪詢,只不過NIO是用戶線程輪詢,IO多路復用是委托給selector讓他來輪詢,那有沒有什么辦法能讓內核主動通知數據好了沒。所以,信號驅動IO出現了。信號信號,顧名思義,就是會有一個信號通知你數據已經准備好了,不用你一直去問。信號驅動IO,用戶線程發出一個請求告訴內核我需要什么數據,數據准備好了你告訴我一聲,然后內核就會記錄下這個請求,內核准備好了之后會主動通知用戶線程去執行拷貝數據,數據拷貝階段是阻塞的,需要等數據拷貝完才能做其他的事。

就像是你帶手機去吃飯,你點好菜之后,你就只管玩手機了,啥也不用管,就等老板通知你,期間你想干啥就干啥,等到菜准備好了,老板會大聲說(內核主動通知用戶進程),xxx你的魚香肉絲已經准備好了,請過來前台端一下,這個時候你要自己去端(數據拷貝),這個端菜的階段,你期間啥都干不了,也不能玩手機,所以信號驅動IO的第二個階段也是阻塞的。

我們對比信號驅動IO和NIO,可以發現最重要的區別就是NIO是用戶主動詢問內核數據好了嗎,而信號驅動IO是內核主動通知用戶數據已經好了,這就改善了上面說的NIO的問題。

全異步IO

全異步IO是最理想的一種IO模型,所謂全異步IO就是,用戶進程發起了一個IO請求,接下來可以干其他的事了,不需要等內核准備好,也不需要執行數據拷貝,數據異步拷貝到用戶空間之后,用戶進程直接拿來用就行了,這兩個階段都是由內核自動完成。完全不用用戶線程操心這些事。

前面四種IO模型實際上都屬於同步IO,只有最后一種才是是真正的異步IO,因為不管是是IO多路復用還是信號驅動,IO操作的第2個階段都會讓用戶線程阻塞,也就是內核進行數據拷貝的過程都會讓用戶線程阻塞。

舉個例子就像是,你去飯店吃飯,點好餐之后,你就可以玩手機了,飯菜做好之后,服務員會把飯菜端到你的面前,你也不需要自己去端,你需要點餐和吃飯就行了,其他的你都不用管。簡單來說,就是發出請求之后,只需要等待數據完成直接使用,等待期間,你可以做其他的事。整個過程完全的異步,體驗最好。

全異步IO雖然非常牛逼,但是現在還不是很成熟,支持全異步IO的操作系統和框架也還不是很多,所以用的也不是很多。我們只需要了解一下就行了

總結

我們這篇文章講了五種IO模型的思想,並且每種模型我們都通過一個通俗易懂的例子,來描繪其過程。相信你看完之后一定有收獲。其中比較重要的兩種是NIO和IO多路復用,這是目前來說用的最多的兩種,后面的篇幅,會專門的講這兩種模型,尤其是IO多路復用,在不同的OS上,又有select,poll,和epoll方式。等下一篇文章,我們將會細講。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM