IO模型淺析

注:
1)本人非科班出身献联,文章的來源主要是基于一些能找到的資料,在理解的基礎(chǔ)上做一些總結(jié)歸納,以期對IO相關(guān)的知識體系化里逆。水平有限进胯,還請指教!
2)本文后半部分對IO所涉及的一些概念做了簡要概述运悲,如果讀者從頭開始閱讀的情況下感覺一頭霧水龄减,可以先去閱讀并熟悉一些基本的概念,有了基本的上下文概念后班眯,再從頭看起希停。

Linux的網(wǎng)絡(luò)IO模型

網(wǎng)絡(luò)IO的本質(zhì)是socket的讀寫,socket在Linux中被抽象為流署隘,IO可以理解為對流的操作宠能。

注意緩存IO這一概念

事先說明:

  • IO本身可以分為內(nèi)存IO、網(wǎng)絡(luò)IO和磁盤IO三種磁餐,一般討論IO時更多是指后兩者违崇,尤其是網(wǎng)絡(luò)IO。

  • 阻塞/非阻塞:針對函數(shù)/方法的實現(xiàn)方式而言

    即數(shù)據(jù)就緒之前是立刻返回還是等待诊霹,即發(fā)起IO請求后是否會阻塞羞延。

  • 同步/異步

    IO讀操作指數(shù)據(jù)流經(jīng):網(wǎng)絡(luò) -> 內(nèi)核緩沖區(qū) -> 用戶內(nèi)存

    而同步和異步的主要區(qū)別在于數(shù)據(jù)從 內(nèi)核緩沖區(qū) -> 用戶內(nèi)存 這個過程需不需要用戶進(jìn)程等待。

    ?

對于一個網(wǎng)絡(luò)IO脾还,會涉及到兩個系統(tǒng)對象伴箩,一個是調(diào)用這個IO的process(or thread),另一個就是系統(tǒng)內(nèi)核(kernel)

當(dāng)一個read操作發(fā)生時鄙漏,它會經(jīng)歷兩個階段:

  1. 第一階段:等待數(shù)據(jù)準(zhǔn)備 (Waiting for the data to be ready)珍坊。
  2. 第二階段:將數(shù)據(jù)從內(nèi)核拷貝到進(jìn)程中 (Copying the data from the kernel to the process)瞎领。

對于socket流而言瓮增,

  1. 第一步:通常涉及等待網(wǎng)絡(luò)上的數(shù)據(jù)分組到達(dá)功咒,然后被復(fù)制到內(nèi)核的某個緩沖區(qū)。
  2. 第二步:把數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到應(yīng)用進(jìn)程緩沖區(qū)桦踊。

網(wǎng)絡(luò)應(yīng)用處理的是兩大類問題:網(wǎng)絡(luò)IO椅野、數(shù)據(jù)計算。前者給應(yīng)用帶來的性能瓶頸更大籍胯。

網(wǎng)絡(luò)IO的模型大致有如下幾種:

  • 同步模型(synchronous IO)
  • 阻塞IO模型(blocking IO)
  • 非阻塞IO模型(non-blocking IO)
  • 多路復(fù)用IO模型(multiplexing IO)
  • 信號驅(qū)動IO模型(signal-driven IO)
  • 異步IO(asynchronous IO)

Blocking IO

在Linux中鳄橘,默認(rèn)情況下所有的socket都是blocking,一個典型的讀操作流程如下:

[圖片上傳失敗...(image-9e85ea-1527599992824)]

當(dāng)用戶進(jìn)程調(diào)用了recvfrom這個系統(tǒng)調(diào)用芒炼,如上所述,會有兩個階段

  • 準(zhǔn)備數(shù)據(jù)术徊。很多時候數(shù)據(jù)在一開始還沒有到達(dá)本刽,這個時候kernel就要等待足夠的數(shù)據(jù)到來。而用戶進(jìn)程會一直阻塞。
  • 當(dāng)kernel等到數(shù)據(jù)準(zhǔn)備好了子寓,它會將數(shù)據(jù)從kernel中拷貝到用戶內(nèi)存暗挑,然后kernel返回,用戶進(jìn)程結(jié)束block狀態(tài)斜友,重新運行炸裆。

Blocking IO的特點就是IO執(zhí)行的兩個階段都是block了的。

Non-Blocking IO

在Linux中鲜屏,可以通過設(shè)置socket使其變?yōu)閚on-blocking烹看,其流程如下:

[圖片上傳失敗...(image-b07607-1527599992824)]

當(dāng)用戶進(jìn)程調(diào)用了recvfrom這個系統(tǒng)調(diào)用,如果kernel中的數(shù)據(jù)還沒有準(zhǔn)備好洛史,那么用戶進(jìn)程不會block而是立刻返回一個error惯殊,即從用戶的角度而言,不需要等待也殖,馬上得到一個結(jié)果土思。從圖中可以看出,用戶進(jìn)程在判斷結(jié)果是一個error后忆嗜,了解到數(shù)據(jù)還沒有準(zhǔn)備好己儒,于是就不斷重復(fù)上述操作直至kernel中的數(shù)據(jù)準(zhǔn)備好,然后它馬上將數(shù)據(jù)拷貝到了用戶內(nèi)存捆毫,然后返回闪湾。

Multiplexing IO

Select/Epoll,也被稱作是Event-Driven IO冻璃。好處是單個process可以同時處理多個網(wǎng)絡(luò)連接的IO响谓。

基本原理可見下面的“IO復(fù)用技術(shù)”。也叫多路IO就緒通知省艳。這是一種進(jìn)程預(yù)先告知內(nèi)核的能力娘纷,讓內(nèi)核發(fā)現(xiàn)進(jìn)程指定的一個或多個IO條件就緒了,就通知進(jìn)程跋炕。使得一個進(jìn)程能在一連串的事件上等待赖晶。

[圖片上傳失敗...(image-f4d1c4-1527599992824)]

這個流程和Blocking IO的流程其實并沒有太多不同,事實上僅從圖中看起來辐烂,由于需要進(jìn)行兩次系統(tǒng)調(diào)用遏插,可能更差一些。但是纠修,Select的優(yōu)勢在于它可以同時處理多個連接胳嘲。

如果處理的連接數(shù)不是很高的話,使用“Select/Epoll 的 Web Server”不一定比使用“多線程 + BIO的Web Server”性能更好扣草,反而延遲會更大了牛。

Select/Epoll的優(yōu)勢并不是對于單個連接能處理得更快颜屠,而是在于能處理更多的連接。

Asynchronous IO

[圖片上傳失敗...(image-2e105e-1527599992824)]

用戶進(jìn)程發(fā)起read操作之后鹰祸,立刻就可以開始去做其它的事甫窟。

而從kernel的角度,當(dāng)它受到一個asynchronous read之后蛙婴,首先它會立刻返回粗井,所以不會對用戶進(jìn)程產(chǎn)生任何block。然后街图,kernel會等待數(shù)據(jù)準(zhǔn)備完成浇衬,然后將數(shù)據(jù)拷貝到用戶內(nèi)存,當(dāng)這一切都完成之后台夺,kernel會給用戶進(jìn)程發(fā)送一個signal径玖,告訴它read操作完成了。

比較

[圖片上傳失敗...(image-89e729-1527599992824)]

IO復(fù)用技術(shù)

在IO編程過程中颤介,當(dāng)需要處理多個請求時梳星,可以使用 多線程 和 IO復(fù)用 的方式進(jìn)行處理。

IO復(fù)用是什么滚朵?

把多個IO的阻塞復(fù)用到一個select之類的阻塞上冤灾,從而使得系統(tǒng)在單線程的情況下同時支持處理多個請求。

IO復(fù)用常見的應(yīng)用場景

  • 服務(wù)器需要同時處理多個處于監(jiān)聽狀態(tài)和多個連接狀態(tài)的套接字辕近;
  • 服務(wù)器需要處理多種網(wǎng)絡(luò)協(xié)議的套接字

IO復(fù)用的實現(xiàn)方式目前主要有select韵吨、poll和epoll。

select和poll的原理基本相同

  • 注冊待偵聽的fd(這里的fd創(chuàng)建時最好使用非阻塞)
  • 每次調(diào)用都去檢查這些fd的狀態(tài)移宅,當(dāng)有一個或者多個fd就緒的時候返回
  • 返回結(jié)果中包括已就緒和未就緒的fd

Linux網(wǎng)絡(luò)編程過程中归粉,相比于select/poll,epoll是有著更明顯優(yōu)勢的一種選擇漏峰。

  1. 支持一個進(jìn)程打開的socket描述符不受限制(僅受限于操作系統(tǒng)的最大文件句柄數(shù))糠悼。

    Select的缺陷:一個進(jìn)程所打開的FD受限,默認(rèn)是2048浅乔;盡管數(shù)值可以更改倔喂,但同樣可能導(dǎo)致網(wǎng)絡(luò)效率下降;可以選擇多進(jìn)程的解決方案靖苇,但是進(jìn)程的創(chuàng)建本身代價不小席噩,而且進(jìn)程間數(shù)據(jù)同步遠(yuǎn)比不上線程間同步的高效。

    epoll所支持的FD上限是最大可以打開文件的數(shù)目贤壁, /proc/sys/fs/file-max

  2. IO效率可能隨著文件描述符數(shù)目的增加而線性下降悼枢。

    select/poll是線性掃描FD的集合;epoll是根據(jù)FD上面的回調(diào)函數(shù)實現(xiàn)的脾拆,活躍的socket會主動去調(diào)用該回調(diào)函數(shù)萧芙,其它socket則不會给梅,相當(dāng)于市是一個AIO,只不過推動力在OS內(nèi)核双揪。

  3. 使用mmap加速內(nèi)核與用戶空間的消息傳遞。

    zero-copy的一種包帚。

  4. epoll的API更加簡單渔期。

IO復(fù)用還有一個 水平觸發(fā) 和 邊緣觸發(fā) 的概念:

  • 水平觸發(fā):當(dāng)就緒的fd未被用戶進(jìn)程處理后,下一次查詢依舊會返回渴邦,這是select和poll的觸發(fā)方式疯趟。
  • 邊緣觸發(fā):無論就緒的fd是否被處理,下一次不再返回谋梭。理論上性能更高信峻,但是實現(xiàn)相當(dāng)復(fù)雜,并且任何意外的丟失事件都會造成請求處理錯誤瓮床。epoll默認(rèn)使用水平觸發(fā)盹舞,通過相應(yīng)選項可以使用邊緣觸發(fā)。

Java的IO模型

  • 傳統(tǒng)的BIO模型
  • 偽異步
  • NIO
  • AIO

BIO

BIO是一個典型的網(wǎng)絡(luò)編程模型隘庄,是通常我們實現(xiàn)一個服務(wù)端程序的過程踢步。

步驟如下:

  • 主線程accept請求阻塞
  • 請求到達(dá),創(chuàng)建新的線程來處理這個套接字丑掺,完成對客戶端的響應(yīng)获印。
  • 主線程繼續(xù)accept下一個請求

這種模型有一個很大的問題是:當(dāng)客戶端連接增多時,服務(wù)端創(chuàng)建的線程也會暴漲街州,系統(tǒng)性能會急劇下降兼丰。

偽異步

在BIO模型的基礎(chǔ)上,類似于 tomcat的bio connector唆缴,采用的是線程池來避免對于每一個客戶端都創(chuàng)建一個線程:把請求拋到線程池中異步等待處理鳍征。

NIO

NIO API主要是三個部分:緩沖區(qū)(Buffers)、通道(Channels)和 Selector琐谤。

NIO基于事件驅(qū)動思想來實現(xiàn)的蟆技,它采用Reactor模式實現(xiàn),主要用來解決BIO模型中一個服務(wù)端無法同時并發(fā)處理大量客戶端連接的問題斗忌。

NIO基于Selector進(jìn)行輪訓(xùn)质礼,當(dāng)socket有數(shù)據(jù)可讀、可寫织阳、連接完成眶蕉、新的TCP請求接入事件時,操作系統(tǒng)內(nèi)核會觸發(fā)Selector返回準(zhǔn)備就緒的SelectionKey集合唧躲,通過SelectableChannel進(jìn)行讀寫操作造挽。

由于jdk的Selector底層基于epoll實現(xiàn)碱璃,理論上可以同時處理操作系統(tǒng)最大文件句柄個數(shù)的連接。SelectableChannel的讀寫操作都是異步非阻塞的饭入,當(dāng)由于數(shù)據(jù)沒有就緒導(dǎo)致讀半包時嵌器,立即返回,不會同步阻塞等待數(shù)據(jù)就緒谐丢,當(dāng)TCP緩沖區(qū)數(shù)據(jù)就緒之后爽航,會觸發(fā)Selector的讀事件,驅(qū)動下一次讀操作乾忱。因此讥珍,一個Reactor線程就可以同時處理N歌客戶端的連接,使得Java服務(wù)器的并發(fā)讀寫能力得到極大的提升窄瘟。

JDK1.4開始引入了NIO類庫衷佃,這里的NIO指的是Non-block IO,主要是使用Selector多路復(fù)用器來實現(xiàn)蹄葱。Selector在Linux等主流操作系統(tǒng)上是通過epoll實現(xiàn)的氏义。

NIO的實現(xiàn)流程,類似于select:

  • 創(chuàng)建ServerSocketChannel監(jiān)聽客戶端連接并綁定監(jiān)聽端口新蟆,設(shè)置為非阻塞模式觅赊。
  • 創(chuàng)建Reactor線程,創(chuàng)建多路復(fù)用器(Selector)并啟動線程琼稻。
  • 將ServerSocketChannel注冊到Reactor線程的Selector上吮螺。監(jiān)聽accept事件。
  • Selector在線程run方法中無線循環(huán)輪詢準(zhǔn)備就緒的Key帕翻。
  • Selector監(jiān)聽到新的客戶端接入鸠补,處理新的請求,完成tcp三次握手嘀掸,建立物理連接紫岩。
  • 將新的客戶端連接注冊到Selector上,監(jiān)聽讀操作睬塌。讀取客戶端發(fā)送的網(wǎng)絡(luò)消息泉蝌。
  • 客戶端發(fā)送的數(shù)據(jù)就緒則讀取客戶端請求,進(jìn)行處理揩晴。

相比BIO勋陪,NIO的編程非常復(fù)雜。

AIO

JDK1.7引入NIO2.0硫兰,提供了異步文件通道和異步套接字通道的實現(xiàn)诅愚。其底層在windows上是通過IOCP,在Linux上是通過epoll來實現(xiàn)的(LinuxAsynchronousChannelProvider.java,UnixAsynchronousServerSocketChannelImpl.java)劫映。

  • 創(chuàng)建AsynchronousServerSocketChannel违孝,綁定監(jiān)聽端口
  • 調(diào)用AsynchronousServerSocketChannel的accpet方法刹前,傳入自己實現(xiàn)的CompletionHandler。包括上一步雌桑,都是非阻塞的
  • 連接傳入喇喉,回調(diào)CompletionHandler的completed方法,在里面筹燕,調(diào)用AsynchronousSocketChannel的read方法轧飞,傳入負(fù)責(zé)處理數(shù)據(jù)的CompletionHandler。
  • 數(shù)據(jù)就緒撒踪,觸發(fā)負(fù)責(zé)處理數(shù)據(jù)的CompletionHandler的completed方法。繼續(xù)做下一步處理即可大渤。
  • 寫入操作類似制妄,也需要傳入CompletionHandler。

其編程模型相比NIO有了不少的簡化泵三。

. 同步阻塞IO 偽異步IO NIO AIO
客戶端數(shù)目 :IO線程 1 : 1 m : n m : 1 m : 0
IO模型 同步阻塞IO 同步阻塞IO 同步非阻塞IO 異步非阻塞IO
吞吐量
編程復(fù)雜度 簡單 簡單 非常復(fù)雜 復(fù)雜

概念

同步異步/阻塞非阻塞 角度一

同步和異步

這兩個概念與消息的通知機制 (synchronous communication/ asynchronous communication)有關(guān)耕捞。

[圖片上傳失敗...(image-8a971a-1527599992824)]

同步:

? 一個任務(wù)的完成需要依賴另外一個任務(wù)時,只有等待被依賴的任務(wù)完成后烫幕,依賴的任務(wù)才能算完成俺抽,這是一種可靠的任務(wù)序列。要么都成功较曼,要么都失敗磷斧,兩個任務(wù)的狀態(tài)可以保持一致。

? 在發(fā)出一個調(diào)用時捷犹,在沒有得到結(jié)果之前弛饭,該調(diào)用就不返回。但是一旦調(diào)用返回萍歉,就得到返回值了侣颂。換句話說,就是由調(diào)用者主動等待這個調(diào)用的結(jié)果枪孩。

? 對于同步型的調(diào)用憔晒,應(yīng)用層需要自己去向系統(tǒng)內(nèi)核問詢,如果數(shù)據(jù)還未讀取完畢蔑舞,那此時讀取文件的任務(wù)還未完成拒担,應(yīng)用層根據(jù)其阻塞和非阻塞的劃分,或掛起或去做其他事情(所以同步和異步并不決定其等待數(shù)據(jù)返回時的狀態(tài))斗幼;如果數(shù)據(jù)已經(jīng)讀取完畢澎蛛,那此時系統(tǒng)內(nèi)核將數(shù)據(jù)返回給應(yīng)用層,應(yīng)用層即可以用取得的數(shù)據(jù)做其他相關(guān)的事情蜕窿。

異步:

? 不需要等到被依賴的任務(wù)完成谋逻,只是通知被依賴的任務(wù)要完成什么工作呆馁,依賴的任務(wù)也立即執(zhí)行,只要自己完成了整個任務(wù)就算完成了毁兆。至于被依賴的任務(wù)最終是否真正完成浙滤,依賴它的任務(wù)無法確定,所以它是不可靠的任務(wù)序列气堕。

? 調(diào)用在發(fā)出之后纺腊,這個調(diào)用就直接返回了,所以沒有返回結(jié)果茎芭。換句話說揖膜,當(dāng)一個異步過程調(diào)用發(fā)出后,調(diào)用者不會立刻得到結(jié)果梅桩。而是在調(diào)用發(fā)出后壹粟,被調(diào)用者*通過狀態(tài)、通知來通知調(diào)用者宿百,或通過回調(diào)函數(shù)處理這個調(diào)用趁仙。

? 而對于異步型的調(diào)用,應(yīng)用層無需主動向系統(tǒng)內(nèi)核問詢垦页,在系統(tǒng)內(nèi)核讀取完文件數(shù)據(jù)之后雀费,會主動通知應(yīng)用層數(shù)據(jù)已經(jīng)讀取完畢,此時應(yīng)用層即可以接收系統(tǒng)內(nèi)核返回過來的數(shù)據(jù)痊焊,再做其他事情盏袄。

也就是說,是否是同步還是異步宋光,關(guān)注的是任務(wù)完成時消息通知的方式貌矿。由調(diào)用方盲目主動問詢的方式是同步調(diào)用,由被調(diào)用方主動通知調(diào)用方任務(wù)已完成的方式是異步調(diào)用罪佳。

消息的三種通知機制:狀態(tài)逛漫、通知和回調(diào)

前者低效赘艳,后兩者高效酌毡、類似。

阻塞與非阻塞

這兩個概念與程序(線程)等待消息通知(無所謂同步或者異步)時的狀態(tài)有關(guān)蕾管。

[圖片上傳失敗...(image-5c552e-1527599992824)]

阻塞調(diào)用:

? 是指調(diào)用結(jié)果返回之前枷踏,當(dāng)前線程會被掛起,一直處于等待消息通知掰曾,不能夠執(zhí)行其他業(yè)務(wù)旭蠕。函數(shù)只有在得到結(jié)果之后才會返回。

非阻塞調(diào)用:

? 和阻塞的概念相對應(yīng),指在不能立刻得到結(jié)果之前掏熬,該函數(shù)不會阻塞當(dāng)前線程佑稠,而會立刻返回去完成其他任務(wù)。

總結(jié)來說旗芬,是否是阻塞還是非阻塞舌胶,關(guān)注的是接口調(diào)用(發(fā)出請求)后等待數(shù)據(jù)返回時的狀態(tài)。被掛起無法執(zhí)行其他操作的則是阻塞型的疮丛,可以被立即「抽離」去完成其他「任務(wù)」的則是非阻塞型的幔嫂。

阻塞和同步的討論

有人也許會把阻塞調(diào)用和同步調(diào)用等同起來,實際上它們是不同的誊薄。**

1履恩、對于同步調(diào)用來說,很多時候當(dāng)前線程可能還是激活的呢蔫,只是從邏輯上當(dāng)前函數(shù)沒有返回而已似袁,此時,這個線程可能也會處理其他的消息咐刨。還有一點,在這里先擴展下:

(a) 如果這個線程在等待當(dāng)前函數(shù)返回時扬霜,仍在執(zhí)行其他消息處理定鸟,那這種情況就叫做同步非阻塞;

(b) 如果這個線程在等待當(dāng)前函數(shù)返回時著瓶,沒有執(zhí)行其他消息處理联予,而是處于掛起等待狀態(tài),那這種情況就叫做同步阻塞材原;

所以同步的實現(xiàn)方式會有兩種:同步阻塞沸久、同步非阻塞;同理余蟹,異步也會有兩種實現(xiàn):異步阻塞卷胯、異步非阻塞;

2威酒、對于阻塞調(diào)用來說窑睁,則當(dāng)前線程就會被掛起等待當(dāng)前函數(shù)返回;

雖然表面上看非阻塞的方式可以明顯的提高CPU的利用率葵孤,但是也帶了另外一種后果就是系統(tǒng)的線程切換增加担钮。增加的CPU執(zhí)行時間能不能補償系統(tǒng)的切換成本需要好好評估。

同步異步/阻塞非阻塞 角度二

[圖片上傳失敗...(image-3a670c-1527599992824)]

在說明synchronous IO和asynchronous IO的區(qū)別之前尤仍,需要先給出兩者的定義箫津。Stevens給出的定義(其實是POSIX的定義)是這樣子的:

? **A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes; **

An asynchronous I/O operation does not cause the requesting process to be blocked;

兩者的區(qū)別就在于synchronous IO做”IO operation”的時候會將process阻塞。按照這個定義,之前所述的blocking IO苏遥,non-blocking IO饼拍,IO multiplexing都屬于synchronous IO

有人可能會說暖眼,non-blocking IO并沒有被block啊惕耕。這里有個非常“狡猾”的地方诫肠,定義中所指的”IO operation”是指真實的IO操作司澎,就是例子中的recvfrom這個system call。non-blocking IO在執(zhí)行recvfrom這個system call的時候栋豫,如果kernel的數(shù)據(jù)沒有準(zhǔn)備好挤安,這時候不會block進(jìn)程。但是丧鸯,當(dāng)kernel中數(shù)據(jù)準(zhǔn)備好的時候蛤铜,recvfrom會將數(shù)據(jù)從kernel拷貝到用戶內(nèi)存中,這個時候進(jìn)程是被block了丛肢,在這段時間內(nèi)围肥,進(jìn)程是被block的。而asynchronous IO則不一樣蜂怎,當(dāng)進(jìn)程發(fā)起IO 操作之后穆刻,就直接返回再也不理睬了,直到kernel發(fā)送一個信號杠步,告訴進(jìn)程說IO完成氢伟。在這整個過程中,進(jìn)程完全沒有被block幽歼。

同步異步/阻塞非阻塞 角度三

"同步異步"和"阻塞非阻塞"是兩個不同范圍的概念朵锣。

synchronous / asynchronous is to describe the relation between two modules.
blocking / non-blocking is to describe the situation of one module.

An example:
"I": a
"bookstore" : b

a asks b: do you have a book named "c++ primer"?

  1. blocking: before b answers a, a keeps waiting there for the answer. Now a (one module) is blocking. a and b are two threads or two processes or one thread or one process? we DON'T know.

  2. non-blocking: before b answers a, a just leaves there and every two minutes, a comes here for looking for the answer. Here a (one module) is non-blocking. a and b are two threads or two processes or one process? we DON'T know. BUT we are sure that a and b couldn't be one thread.

  3. synchronous: before b answers a, a keeps waiting there for the answer. It means that a can't continue until b finishes its job. Now we say: a and b (two modules) is synchronous. a and b are two threads or two processes or one thread or one process? we DON'T know.

  4. asynchronous: before b answers a, a leaves there and a can do other jobs. When b gets the answer, b will call a: hey! I have it! Then a will come to b to get the book when a is free. Now we say: a and b (two modules) is asynchronous. a and b are two threads or two processes or one process? we DON'T know. BUT we are sure that a and b couldn't be one thread.

同步異步/阻塞非阻塞 角度四

同步和異步是一個非常廣的概念,它們的重點在于多個任務(wù)和事件發(fā)生時甸私,一個事件的發(fā)生或執(zhí)行是否會導(dǎo)致整個流程的暫時等待诚些。

阻塞和非阻塞的區(qū)別關(guān)鍵在于當(dāng)發(fā)出請求一個操作時,如果條件不滿足颠蕴,是會一直等待還是返回一個標(biāo)志信息泣刹。

在討論IO(硬盤、網(wǎng)絡(luò)犀被、外設(shè))時椅您,一個完整的IO讀請求操作包括兩個階段:

1)查看數(shù)據(jù)是否就緒;

2)進(jìn)行數(shù)據(jù)拷貝(內(nèi)核將數(shù)據(jù)拷貝到用戶線程)

阻塞(blocking IO)和非阻塞(non-blocking IO)的區(qū)別就在于第一個階段寡键,如果數(shù)據(jù)沒有就緒掀泳,在查看數(shù)據(jù)是否就緒的過程中是一直等待雪隧,還是直接返回一個標(biāo)志信息。

在《Unix網(wǎng)絡(luò)編程》一書中對同步IO和異步IO的定義是這樣的:

A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes.
  An asynchronous I/O operation does not cause the requesting process to be blocked.

事實上员舵,同步IO和異步IO模型是針對用戶線程和內(nèi)核的交互來說的:

同步IO:當(dāng)用戶發(fā)出IO請求操作之后脑沿,如果數(shù)據(jù)沒有就緒,需要通過用戶線程或者內(nèi)核不斷地去輪詢數(shù)據(jù)是否就緒马僻,當(dāng)數(shù)據(jù)就緒時庄拇,再將數(shù)據(jù)從內(nèi)核拷貝到用戶線程;

異步IO:只有IO請求操作的發(fā)出是由用戶線程來進(jìn)行的韭邓,IO操作的兩個階段都是由內(nèi)核自動完成措近,然后發(fā)送通知告知用戶線程IO操作已經(jīng)完成。也就是說在異步IO中女淑,不會對用戶線程產(chǎn)生任何阻塞瞭郑。

這是同步IO和異步IO關(guān)鍵區(qū)別所在,同步IO和異步IO的關(guān)鍵區(qū)別反映在數(shù)據(jù)拷貝階段是由用戶線程完成還是內(nèi)核完成鸭你。所以說<u>異步IO必須要有操作系統(tǒng)的底層支持</u>屈张。

注意同步IO和異步IO與阻塞IO和非阻塞IO是不同的兩組概念。

阻塞IO和非阻塞IO是反映在當(dāng)用戶請求IO操作時袱巨,如果數(shù)據(jù)沒有就緒阁谆,是用戶線程一直等待數(shù)據(jù)就緒,還是會收到一個標(biāo)志信息這一點上面的愉老。也就是說笛厦,阻塞IO和非阻塞IO是反映在IO操作的第一個階段,在查看數(shù)據(jù)是否就緒時是如何處理的俺夕。

同步/異步 與 阻塞/非阻塞

同步阻塞

效率是最低的,

異步阻塞

異步操作是可以被阻塞住的贱鄙,只不過它不是在處理消息時阻塞劝贸,而是在等待消息通知時被阻塞。

同步非阻塞

實際上是效率低下的逗宁,

這個程序<u>需要在兩種不同的行為之間來回的切換</u>映九,效率可想而知是低下的。

異步非阻塞

效率更高瞎颗。

用戶空間與內(nèi)核空間

現(xiàn)在操作系統(tǒng)都是采用虛擬存儲器件甥,對32位操作系統(tǒng)而言,它的尋址空間(虛擬存儲空間)為4G(2的32次方)哼拔。

操作系統(tǒng)的核心是內(nèi)核引有,獨立于普通的應(yīng)用程序,可以訪問受保護(hù)的內(nèi)存空間倦逐,也有訪問底層硬件設(shè)備的所有權(quán)限譬正。

為了保證用戶進(jìn)程不能直接操作內(nèi)核(kernel),保證內(nèi)核的安全,操作系統(tǒng)將虛擬空間劃分為兩部分曾我,<u>一部分為內(nèi)核空間粉怕,一部分為用戶空間</u>。

針對linux操作系統(tǒng)而言抒巢,將最高的1G字節(jié)(從虛擬地址0xC0000000到0xFFFFFFFF)贫贝,供內(nèi)核使用,稱為內(nèi)核空間蛉谜,而將較低的3G字節(jié)(從虛擬地址0x00000000到0xBFFFFFFF)稚晚,供各個進(jìn)程使用,稱為用戶空間悦陋。

進(jìn)程切換

為了控制進(jìn)程的執(zhí)行蜈彼,內(nèi)核必須有能力掛起正在CPU上運行的進(jìn)程,并恢復(fù)以前掛起的某個進(jìn)程的執(zhí)行俺驶。這種行為被稱為進(jìn)程切換/任務(wù)切換/上下文切換幸逆。因此可以說,任何進(jìn)程都是在操作系統(tǒng)內(nèi)核的支持下運行的暮现,是與內(nèi)核緊密相關(guān)的还绘。

從一個進(jìn)程的運行轉(zhuǎn)到另一個進(jìn)程上運行,這個過程中經(jīng)過下面這些變化:

  1. 保存處理機上下文栖袋,包括程序計數(shù)器和其他寄存器医窿。
  2. 更新PCB信息吮龄。
  3. 把進(jìn)程的PCB移入相應(yīng)的隊列,如就緒、在某事件阻塞等隊列瓶堕。
  4. 選擇另一個進(jìn)程執(zhí)行,并更新其PCB蒙揣。
  5. 更新內(nèi)存管理的數(shù)據(jù)結(jié)構(gòu)锻全。
  6. 恢復(fù)處理機上下文。

進(jìn)程的阻塞

正在執(zhí)行的進(jìn)程匾乓,由于期待的某些事件未發(fā)生捞稿,如請求系統(tǒng)資源失敗、等待某種操作的完成拼缝、新數(shù)據(jù)尚未到達(dá)或無新工作做等娱局,則由系統(tǒng)自動執(zhí)行阻塞原語(Block),使自己由運行狀態(tài)變?yōu)樽枞麪顟B(tài)咧七∷テ耄可見,進(jìn)程的阻塞是進(jìn)程自身的一種主動行為继阻,也因此只有處于運行態(tài)的進(jìn)程(獲得CPU)娇斩,才可能將其轉(zhuǎn)為阻塞狀態(tài)仁卷。當(dāng)進(jìn)程進(jìn)入阻塞狀態(tài),是不占用CPU資源的犬第。

文件描述符fd

文件描述符(File descriptor)是計算機科學(xué)中的一個術(shù)語锦积,是一個用于表述指向文件的引用的抽象化概念

文件描述符在形式上是一個非負(fù)整數(shù)歉嗓。實際上丰介,它是一個索引值,指向內(nèi)核為每一個進(jìn)程所維護(hù)的該進(jìn)程打開文件的記錄表鉴分。當(dāng)程序打開一個現(xiàn)有文件或者創(chuàng)建一個新文件時哮幢,內(nèi)核向進(jìn)程返回一個文件描述符。在程序設(shè)計中志珍,一些涉及底層的程序編寫往往會圍繞著文件描述符展開橙垢。但是文件描述符這一概念往往只適用于UNIX、Linux這樣的操作系統(tǒng)伦糯。

緩存 IO

緩存 IO 又被稱作標(biāo)準(zhǔn) IO柜某,大多數(shù)文件系統(tǒng)的默認(rèn) IO 操作都是緩存 IO。在 Linux 的緩存 IO 機制中敛纲,操作系統(tǒng)會將 IO 的數(shù)據(jù)緩存在文件系統(tǒng)的頁緩存( page cache )中喂击,也就是說,數(shù)據(jù)會先被拷貝到操作系統(tǒng)內(nèi)核的緩沖區(qū)中淤翔,然后才會從操作系統(tǒng)內(nèi)核的緩沖區(qū)拷貝到應(yīng)用程序的地址空間翰绊。

緩存 IO 的缺點:

數(shù)據(jù)在傳輸過程中需要在應(yīng)用程序地址空間和內(nèi)核進(jìn)行多次數(shù)據(jù)拷貝操作,這些數(shù)據(jù)拷貝操作所帶來的 CPU 以及內(nèi)存開銷是非常大的旁壮。

IO - 同步监嗜,異步,阻塞抡谐,非阻塞

聊聊Linux 五種IO模型

聊聊同步秤茅、異步、阻塞與非阻塞

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末童叠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子课幕,更是在濱河造成了極大的恐慌厦坛,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乍惊,死亡現(xiàn)場離奇詭異杜秸,居然都是意外死亡,警方通過查閱死者的電腦和手機润绎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門撬碟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诞挨,“玉大人,你說我怎么就攤上這事呢蛤』躺担” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵其障,是天一觀的道長银室。 經(jīng)常有香客問我,道長励翼,這世上最難降的妖魔是什么蜈敢? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮汽抚,結(jié)果婚禮上抓狭,老公的妹妹穿的比我還像新娘。我一直安慰自己造烁,他們只是感情好否过,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著膨蛮,像睡著了一般叠纹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敞葛,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天誉察,我揣著相機與錄音,去河邊找鬼惹谐。 笑死持偏,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的氨肌。 我是一名探鬼主播鸿秆,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼怎囚!你這毒婦竟也來了卿叽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤恳守,失蹤者是張志新(化名)和其女友劉穎考婴,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體催烘,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡沥阱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了伊群。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片考杉。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡策精,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出崇棠,到底是詐尸還是另有隱情咽袜,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布易茬,位于F島的核電站酬蹋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏抽莱。R本人自食惡果不足惜范抓,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望食铐。 院中可真熱鬧匕垫,春花似錦、人聲如沸虐呻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斟叼。三九已至偶惠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間朗涩,已是汗流浹背忽孽。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谢床,地道東北人兄一。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像识腿,于是被迫代替她去往敵國和親出革。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容