Linux中的IO模型

IO模型
  linux系統IO分爲內核準備數據和將數據從內核拷貝到用戶空間兩個階段。
 
這張圖大致描述了數據從外部磁盤向運行中程序的內存中移動的過程。
用戶空間、內核空間
  現在操作系統都是採用虛擬存儲器,那麼對32位操作系統而言,它的尋址空間(虛擬儲存空間)爲4G(2的32次方)。操作系統的核心是內核,獨立於普通的應用程序,可以訪問受保護的內存空間,也有訪問底層硬件設備的所有權限。爲了保證用戶進程不能直接操作內核,保證內核的安全,操作系統將虛擬空間劃分爲兩個部分,一個部分爲內核空間,一部分爲用戶空間。
  如何分配這兩個空間的大小也是有講究的,如windows 32位操作系統,默認的用戶空間:內核空間的比例是1:1;而在32位Linux系統中的默認比例是3:1(3G用戶空間,1G內核空間)。
進程切換
  爲了控制進程的執行,內核必須要有能力掛起正在CPU上運行的進程,並恢復以前掛起的某個進程的執行。這種行爲成爲進程的切換。任何進程都是在操作系統內核的支持下運行的,是與內核緊密相關的。
進程切換的過程,會經過下面這些變化:
1、保存處理機上下文,包括程序計數器和其他寄存器。
2、更新PCB信息。
3、將進程的PCB移入相應的隊列,如就緒、在某事件阻塞等隊列。
4、選擇另外一個進程執行,並更新PCB
5、更新內存管理的數據結構。
6、恢復處理機上下文
緩存IO
  緩存IO又稱稱爲標準IO,大多數文件系統的默認IO操作都是緩存IO。在Linux的緩存IO機制中,操作系統會將IO的數據緩存在文件系統的頁緩存(page cache)。也就是說,數據會先被拷貝到操作系統內核的緩衝區中,然後纔會從操作系統內核的緩存區拷貝到應用程序的地址空間中。
  這種做法的缺點就是,需要在應用程序地址空間和內核進行多次拷貝,這些拷貝動作所帶來的CPU以及內存開銷是非常大的。
同步、異步、阻塞、非阻塞
同步與異步:描述的是用戶線程與內核的交互方式,同步指用戶線程發起IO請求後需要等待或者輪詢內核IO操作完成後才能繼續執行;而異步是指用戶線程發起IO請求後仍然繼續執行,當內核IO操作完成後會通知用戶線程,或者調用用戶線程註冊的回調函數。
阻塞與非阻塞:描述是用戶線程調用內核IO操作的方式,阻塞是指IO操作需要徹底完成後才返回到用戶空間;而非阻塞是指IO操作被調用後立即返回給用戶一個狀態值,無需等到IO操作徹底完成。
Linux IO模型
  網絡IO的本質就是socket的讀取,socket在linux系統被抽象爲流,IO可以理解爲對流的操作。文章開始的時候也提到了,對於一次IO訪問(以read爲例),數據會先被拷貝到操作系統內核的緩衝區,然後纔會從操作系統內核的緩衝區拷貝到應用程序的地址空間中。所以說,當一個read操作發生時,它會經歷兩個階段:
第一個階段:等待數據準備。
第二個階段:將數據從內核拷貝到進程中
對於socket流而言:
第一步:通常涉及等待網絡上的數據分組到達,然後複製到內核的某個緩衝區。
第二步:把數據從內核緩衝區複製到應用進程緩衝區。
當然,如果內核空間的緩衝區中已經有數據了,那麼就可以省略第一步。至於爲什麼不能直接讓磁盤控制器把數據送到應用程序的地址空間中呢?最簡單的一個原因就是應用程序不能直接操作底層硬件。
網絡應用需要處理的無非就是兩大類問題,網絡IO,數據計算。相對於後者,網絡IO的延遲,給應用帶來的性能瓶頸大於後者。網絡IO的模型大致分爲如下五種:
1、阻塞IO
2、非阻塞IO
3、多路複用IO
4、信號驅動IO
5、異步IO
前四種都是同步,只有最後一種是異步IO。下面的模型介紹先以生活中的例子來說明概念:週末和女友去商場逛街,到了晚上飯點,準備吃完飯再去逛街,但是週末人多,新白鹿飯店需要排隊,於是有如下幾種方案可供選擇:
1、阻塞IO模型
場景描述:
  在飯店領完號後,前面還有n桌,不知道什麼時候到我們,但是又不能離開,因爲過號之後必須重新取號。只好在飯店裏等,一直等到叫號到我們才吃完晚飯,然後去逛街。中間等待的時間什麼事情都不能做。
網絡模型:
  在這個模型中,應用程序爲了執行這個read操作,會調用相應的一個system call,將系統控制權交給內核,然後就進行等待(這個等待的過程就是被阻塞了),內核開始執行這個system call,執行完畢後會嚮應用程序返回響應,應用程序得到響應後,就不再阻塞,並進行後面的工作。
優點:
  能夠及時返回數據,無延遲。
缺點:
  對用戶來說處於等待就要付出性能代價。
2、非阻塞IO
場景描述:
  等待過程是在太無聊,於是我們就去逛商場,每隔一段時間就回來詢問服務員,叫號是否到我們了,整個過程來來回回好多次。這就是非阻塞,但是需要不斷的詢問。
網絡模型:
  當用戶進程發出read操作時,調用相應的system call,這個system call會立即從內核中返回。但是在返回的這個時間點,內核中的數據可能還沒有準備好,也就是說內核只是很快就返回了system call,只有這樣纔不會阻塞用戶進程,對於應用程序,雖然這個IO操作很快就返回了,但是它並不知道這個IO操作是否真的成功了,爲了知道IO操作是否成功,應用程序需要主動的循環去問內核。
優點:
  能夠在等待的時間裏去做其他的事情。
缺點:
  任務完成的響應延遲增大了,因爲每過一段時間去輪詢一次read操作,而任務可能在兩次輪詢之間的任意時間完成,這對導致整體數據吞吐量的降低。
3、IO多路複用
場景描述:
  與第二個經常類似,飯店安裝了電子屏幕,顯示叫號的狀態,所以在逛街的時候,就不用去詢問服務員,而是看下大屏幕就可以了。(不僅僅是我們不用詢問服務員,其他所有的人都可以不用詢問服務員)
網絡模型:
  和第二種一樣,調用system call之後,並不等待內核的返回結果而是立即返回。雖然返回結果的調用函數是一個異步的方式,但應用程序會被像select、poll和epoll等具有多個文件描述符的函數阻塞住,一直等到這個system call有結果返回了,再通知應用程序。這種情況,從IO操作的實際效果來看,異步阻塞IO和第一種同步阻塞IO是一樣的,應用程序都是一直等到IO操作成功之後(數據已經被寫入或者讀取),纔開始進行下面的工作。不同點在於異步阻塞IO用一個select函數可以爲多個文件描述符提供通知,提供了併發性。舉個例子:例如有一萬個併發的read請求,但是網絡上仍然沒有數據,此時這一萬個read會同時各自阻塞,現在用select、poll、epoll這樣的函數來專門負責阻塞同時監聽這一萬個請求的狀態,一旦有數據到達了就負責通知,這樣就將一萬個等待和阻塞轉化爲一個專門的函數來負責與管理。
  多路複用技術應用於JAVA NIO的核心類庫多路複用器Selector中,目前支持I/O多路複用的系統調用有select、pselect、poll、epoll,在linux編程中有一段時間一直在使用select做輪詢和網絡事件通知的,但是select支持一個進程打開的socket描述符(FD)收到了限制,一般爲1024,由於這一限制,現在使用了epoll代替了select,而epoll支持一個進程打開的FD不受限制。
  異步IO與同步IO的區別在於:同步IO是需要應用程序主動地循環去詢問是否有數據,而異步IO是通過像select等IO多路複用函數來同時檢測多個事件句柄來告知應用程序是否有數據。
  瞭解了前面三種IO模式,在用戶進程進行系統調用的時候,他們在等待數據到來的時候,處理的方式是不一樣的,直接等待、輪詢、select或poll輪詢,兩個階段過程:
第一個階段有的阻塞,有的不阻塞,有的可以阻塞又可以不阻塞。
第二個階段都是阻塞的。
從整個IO過程來看,他們都是順序執行的,因此可以歸爲同步模型,都是進程自動等待且向內核檢查狀態。
  高併發的程序一般使用同步非阻塞模式,而不是多線程+同步阻塞模式。要理解這點,先弄明白併發和並行的區別:比如去某部門辦事需要依次去幾個窗口,辦事大廳的人數就是併發數,而窗口的個數就是並行度。就是說併發是同時進行的任務數(如同時服務的http請求),而並行數就是可以同時工作的物理資源數量(如cpu核數)。通過合理調度任務的不同階段,併發數可以遠遠大於並行度。這就是區區幾個CPU可以支撐上萬個用戶併發請求的原因。在這種高併發的情況下,爲每個用戶請求創建一個進程或者線程的開銷非常大。而同步非阻塞方式可以把多個IO請求丟到後臺去,這樣一個CPU就可以服務大量的併發IO請求。
  IO多路複用究竟是同步阻塞還是異步阻塞模型,這裏來展開說說:
  同步是需要主動等待消息通知,而異步則是被動接受消息通知,通過回調、通知、狀態等方式來被動獲取消息。IO多路複用在阻塞到select階段時,用戶進程是主動等待並調用select函數來獲取就緒狀態消息,並且其進程狀態爲阻塞。所以IO多路複用是同步阻塞模式。
4、信號驅動式IO
  應用程序提交read請求,調用system call,然後內核開始處理相應的IO操作,而同時,應用程序並不等內核返回響應,就會開始執行其他的處理操作(應用程序沒有被IO阻塞),當內核執行完畢,返回read響應,就會產生一個信號或執行一個基於線程的回調函數來完成這次IO處理過程。在這裏IO的讀寫操作是在IO事件發生之後由應用程序來完成。異步IO讀寫操作總是立即返回,而不論IO是否阻塞,因爲真正的讀寫操作已經有內核掌管。也就是說同步IO模型要求用戶代碼自行執行IO操作(將數據從內核緩衝區移動用戶緩衝區或者相反),而異步操作機制則是由內核來執行IO操作(將數據從內核緩衝區移動用戶緩衝區或者相反)。可以這樣認爲,同步IO嚮應用程序通知的是IO就緒事件,而異步IO嚮應用程序通知的是IO完成事件。
5、異步IO
  異步IO與上面的異步概念是一樣的, 當一個異步過程調用發出後,調用者不能立刻得到結果,實際處理這個調用的函數在完成後,通過狀態、通知和回調來通知調用者的輸入輸出操作。異步IO的工作機制是:告知內核啓動某個操作,並讓內核在整個操作完成後通知我們,這種模型與信號驅動的IO區別在於,信號驅動IO是由內核通知我們何時可以啓動一個IO操作,這個IO操作由用戶自定義的信號函數來實現,而異步IO模型是由內核告知我們IO操作何時完成。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章