漫話:如何給女友解釋什麼是Linux的五種IO模型?


週日午後,剛剛放下手裏的電話,正在給剛剛的面試者寫評價。剛剛寫到『對Linux的基本IO模型理解不深』這句的時候,女友忽然出現。程序員






在Java中,主要有三種IO模型,分別是阻塞IO(BIO)、非阻塞IO(NIO)和 異步IO(AIO)。
面試




Java中提供的IO有關的API,在文件處理的時候,其實依賴操做系統層面的IO操做實現的。好比在Linux 2.6之後,Java中NIO和AIO都是經過epoll來實現的,而在Windows上,AIO是經過IOCP來實現的。
緩存

能夠把Java中的BIO、NIO和AIO理解爲是Java語言對操做系統的各類IO模型的封裝。程序員在使用這些API的時候,不須要關心操做系統層面的知識,也不須要根據不一樣操做系統編寫不一樣的代碼。只須要使用Java的API就能夠了。併發






在Linux(UNIX)操做系統中,共有五種IO模型,分別是:阻塞IO模型非阻塞IO模型IO複用模型信號驅動IO模型以及異步IO模型
異步

既然提到晚上吃魚,那就經過釣魚的例子來解釋這五種IO模型吧。socket

到底什麼是IO

咱們常說的IO,指的是文件的輸入和輸出,可是在操做系統層面是如何定義IO的呢?到底什麼樣的過程能夠叫作是一次IO呢?函數

拿一次磁盤文件讀取爲例,咱們要讀取的文件是存儲在磁盤上的,咱們的目的是把它讀取到內存中。能夠把這個步驟簡化成把數據從硬件(硬盤)中讀取到用戶空間中。工具

其實真正的文件讀取還涉及到緩存等細節,這裏就不展開講述了。關於用戶空間、內核空間以及硬件等的關係若是讀者不理解的話,能夠經過釣魚的例子理解。操作系統

釣魚的時候,剛開始魚是在魚塘裏面的,咱們的釣魚動做的最終結束標誌是魚從魚塘中被咱們釣上來,放入魚簍中。線程

這裏面的魚塘就能夠映射成磁盤,中間過渡的魚鉤能夠映射成內核空間,最終放魚的魚簍能夠映射成用戶空間。一次完整的釣魚(IO)操做,是魚(文件)從魚塘(硬盤)中轉移(拷貝)到魚簍(用戶空間)的過程。

阻塞IO模型

咱們釣魚的時候,有一種方式比較愜意,比較輕鬆,那就是咱們坐在魚竿面前,這個過程當中咱們什麼也不作,雙手一直把着魚竿,就靜靜的等着魚兒咬鉤。一旦手上感覺到魚的力道,就把魚釣起來放入魚簍中。而後再釣下一條魚。

映射到Linux操做系統中,這就是一種最簡單的IO模型,即阻塞IO。 阻塞 I/O 是最簡單的 I/O 模型,通常表現爲進程或線程等待某個條件,若是條件不知足,則一直等下去。條件知足,則進行下一步操做。

應用進程經過系統調用 recvfrom 接收數據,但因爲內核還未準備好數據報,應用進程就會阻塞住,直到內核準備好數據報,recvfrom 完成數據報復制工做,應用進程才能結束阻塞狀態。

這種釣魚方式相對來講比較簡單,對於釣魚的人來講,不須要什麼特製的魚竿,拿一根夠長的木棍就能夠清閒的開始釣魚了(實現簡單)。缺點就是比較耗費時間,比較適合那種對魚的需求量小的狀況(併發低,時效性要求低)。




非阻塞IO模型

咱們釣魚的時候,在等待魚兒咬鉤的過程當中,咱們能夠作點別的事情,好比玩一把王者榮耀、看一集《延禧攻略》等等。可是,咱們要時不時的去看一下魚竿,一旦發現有魚兒上鉤了,就把魚釣上來。

映射到Linux操做系統中,這就是非阻塞的IO模型。應用進程與內核交互,目的未達到以前,再也不一味的等着,而是直接返回。而後經過輪詢的方式,不停的去問內核數據準備有沒有準備好。若是某一次輪詢發現數據已經準備好了,那就把數據拷貝到用戶空間中。

應用進程經過 recvfrom 調用不停的去和內核交互,直到內核準備好數據。若是沒有準備好,內核會返回error,應用進程在獲得error後,過一段時間再發送recvfrom請求。在兩次發送請求的時間段,進程能夠先作別的事情。

這種方式釣魚,和阻塞IO比,所使用的工具沒有什麼變化,可是釣魚的時候能夠作些其餘事情,增長時間的利用率。






信號驅動IO模型

咱們釣魚的時候,爲了不本身一遍一遍的去查看魚竿,咱們能夠給魚竿安裝一個報警器。當有魚兒咬鉤的時候馬上報警。而後咱們再收到報警後,去把魚釣起來。

映射到Linux操做系統中,這就是信號驅動IO。應用進程在讀取文件時通知內核,若是某個 socket 的某個事件發生時,請向我發一個信號。在收到信號後,信號對應的處理函數會進行後續處理。

應用進程預先向內核註冊一個信號處理函數,而後用戶進程返回,而且不阻塞,當內核數據準備就緒時會發送一個信號給進程,用戶進程便在信號處理函數中開始把數據拷貝的用戶空間中。

這種方式釣魚,和前幾種相比,所使用的工具備了一些變化,須要有一些定製(實現複雜)。可是釣魚的人就能夠在魚兒咬鉤以前完全作別的事兒去了。等着報警器響就好了。






IO複用模型

咱們釣魚的時候,爲了保證能夠最短的時間釣到最多的魚,咱們同一時間擺放多個魚竿,同時釣魚。而後哪一個魚竿有魚兒咬鉤了,咱們就把哪一個魚竿上面的魚釣起來。

映射到Linux操做系統中,這就是IO複用模型。多個進程的IO能夠註冊到同一個管道上,這個管道會統一和內核進行交互。當管道中的某一個請求須要的數據準備好以後,進程再把對應的數據拷貝到用戶空間中。

IO多路轉接是多了一個select函數,多個進程的IO能夠註冊到同一個select上,當用戶進程調用該selectselect會監聽全部註冊好的IO,若是全部被監聽的IO須要的數據都沒有準備好時,select調用進程會阻塞。當任意一個IO所需的數據準備好以後,select調用就會返回,而後進程在經過recvfrom來進行數據拷貝。

這裏的IO複用模型,並無向內核註冊信號處理函數,因此,他並非非阻塞的。進程在發出select後,要等到select監聽的全部IO操做中至少有一個須要的數據準備好,纔會有返回,而且也須要再次發送請求去進行文件的拷貝。

這種方式的釣魚,經過增長魚竿的方式,能夠有效的提高效率。






爲何以上四種都是同步的

咱們說阻塞IO模型、非阻塞IO模型、IO複用模型和信號驅動IO模型都是同步的IO模型。緣由是由於,不管以上那種模型,真正的數據拷貝過程,都是同步進行的。

信號驅動難道不是異步的麼? 信號驅動,內核是在數據準備好以後通知進程,而後進程再經過recvfrom操做進行數據拷貝。咱們能夠認爲數據準備階段是異步的,可是,數據拷貝操做是同步的。因此,整個IO過程也不能認爲是異步的。




咱們把釣魚過程,能夠拆分爲兩個步驟:一、魚咬鉤(數據準備)。二、把魚釣起來放進魚簍裏(數據拷貝)。不管以上提到的哪一種釣魚方式,在第二步,都是須要人主動去作的,並非魚竿本身完成的。因此,這個釣魚過程其實仍是同步進行的。




燒水的報警器一響,整個燒水過程就完成了。水已是開水了。

釣魚的報警器一響,只能說明魚兒已經咬鉤了,可是尚未真正的釣上來。

因此 ,使用帶有報警器的水壺燒水,燒水過程是異步的。

而使用帶有報警器的魚竿釣魚,釣魚的過程仍是同步的。




異步IO模型

咱們釣魚的時候,採用一種高科技釣魚竿,即全自動釣魚竿。能夠自動感應魚上鉤,自動收竿,更厲害的能夠自動把魚放進魚簍裏。而後,通知咱們魚已經釣到了,他就繼續去釣下一條魚去了。

映射到Linux操做系統中,這就是異步IO模型。應用進程把IO請求傳給內核後,徹底由內核去操做文件拷貝。內核完成相關操做後,會發信號告訴應用進程本次IO已經完成。

用戶進程發起aio_read操做以後,給內核傳遞描述符、緩衝區指針、緩衝區大小等,告訴內核當整個操做完成時,如何通知進程,而後就馬上去作其餘事情了。當內核收到aio_read後,會馬上返回,而後內核開始等待數據準備,數據準備好之後,直接把數據拷貝到用戶控件,而後再通知進程本次IO已經完成。

這種方式的釣魚,無疑是最省事兒的。啥都不須要管,只須要交給魚竿就能夠了。




5種IO模型對比








介紹完這些以後,我默默的刪掉了以前寫好的那句面試評價『對Linux的基本IO模型理解不深』,改爲了『對IO體系理解的不夠深刻,只會使用封裝好的API』。



相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息