讀取檔案時,程式經歷了什麼?

2021-10-09 18:30:48 字數 3348 閱讀 3465

承接上文《一文徹底理解高效能高併發中的執行緒與執行緒池》,這是高效能、高併發系列的第二篇文章,在這裡我們來到了i/o這一話題。

你有沒有想過當我們執行i/o操作時計算機底層都發生了些什麼?

在回答這個問題之前,我們先來看下為什麼對於計算機來說i/o是極其重要的。

相信對於程式設計師來說i/o操作是最為熟悉不過的了:

想一想,如果沒有i/o計算機該是一種多麼枯燥的裝置,不能看電影、不能玩遊戲,也不能上網,這樣的計算機最多就是乙個大號的計算器。

既然i/o這麼重要,那麼到底什麼才是i/o呢?

i/o就是簡單的資料copy,僅此而已。

這一點很重要,為了加深大家的印象,來,everybody,follow me,那邊樹上的朋友,還有那邊牆上的朋友們,舉起你們的雙手,跟我唱,蒼茫的天涯是。。。sorry,i/o僅僅就是資料copy、i/o僅僅就是資料copy。

讓我們先把演唱會的事情放在一邊,既然是copy資料,又是從**copy到**呢?

如果資料是從外部裝置copy到記憶體中,這就是input。

如果資料是從記憶體copy到外部裝置,這就是output。

記憶體與外部裝置之間不嫌麻煩的來回copy資料就是input and output,簡稱i/o(input/output),僅此而已。

現在我們知道了什麼是i/o,接下來就是重點部分了,大家注意,坐穩了。

我們知道現在的cpu其主頻都是數ghz起步,這是什麼意思呢?簡單說就是cpu執行機器指令的速度是納秒級別的,而通常的i/o比如磁碟操作,一次磁碟seek大概在毫秒級別,因此如果我們把cpu的速度比作戰鬥機的話,那麼i/o操作的速度就是肯德雞

也就是說當我們的程式跑起來時(cpu執行機器指令),其速度是要遠遠快於i/o速度的,那麼接下來的問題就是二者速度相差這麼大,那麼我們該如何設計、該如何更加合理的高效利用系統資源呢?

既然有速度差異,而且程序在執行完i/o操作前不能繼續向前推進,那麼顯然只有乙個辦法,那就是等待,wait

同樣是等待,有聰明的等待,也有傻傻的等待,簡稱傻等,那麼是選擇聰明的等待呢還是選擇傻等呢?

很顯然,更好的方法就是先去幹其它事情,快遞來了再說。

因此這裡的關鍵點就是快遞沒到前手頭上的事情可以先暫停,切換到其它任務,等快遞過來了再切換回來

理解了這一點你就能明白執行i/o操作時底層都發生了什麼。

接下來讓我們以讀取磁碟檔案為例來講解這一過程。

現在記憶體中有兩個程序,程序a和程序b,當前程序a正在執行,如圖所示:

程序a中有一段讀取檔案的**,不管在什麼語言中通常我們定義乙個用來裝資料的buff,然後呼叫read之類的函式,像這樣:

read(buff);
這就是一種典型的i/o操作,當cpu執行到這段**的時候會向磁碟傳送讀取請求,注意與cpu執行指令的速度相比,i/o操作操作是非常慢的,因此作業系統是不可能把寶貴的cpu計算資源浪費在無謂的等待上的,這時重點來了,注意接下來是重點哦。

由於外部裝置執行i/o操作是相當慢的,因此在i/o操作完成之前程序是無法繼續向前推進的,這就是所謂的阻塞,即通常所說的block。作業系統檢測到程序向i/o裝置發起請求後就暫停程序的執行,怎麼暫停執行呢?很簡單,只需要記錄下當前程序的執行狀態並把cpu的pc暫存器指向其它程序的指令就可以了。

程序有暫停就會有繼續執行,因此作業系統必須儲存被暫停的程序以備後續繼續執行,顯然我們可以用佇列來儲存被暫停執行的程序,如圖所示,程序a被暫停執行並被放到阻塞佇列中(注意,不同的作業系統會有不同的實現,可能每個i/o裝置都有乙個對應的阻塞佇列,但這種實現細節上的差異不影響我們的討論)。

這時作業系統已經向磁碟傳送了i/o請求,因此磁碟driver開始將磁碟中的資料copy到程序a的buff中,雖然這時程序a已經被暫停執行了,但這並不妨礙磁碟向記憶體中copy資料。注意,現代磁碟向記憶體copy資料時無需借助cpu的幫助,這就是所謂的dma(direct memory access),這個過程如圖所示:

讓磁碟先copy著資料,我們接著聊。

實際上作業系統中除了有阻塞佇列之外也有就緒佇列,所謂就緒佇列是指佇列裡的程序準備就緒可以被cpu執行了,你可能會問為什麼不直接執行非要有個就緒佇列呢?答案很簡單,那就是僧多粥少,在即使只有1個核的機器上也可以建立出成千上萬個程序,cpu不可能同時執行這麼多的程序,因此必然存在這樣的程序,即使其一切準備就緒也不能被分配到計算資源,這樣的程序就被放到了就緒佇列。

現在程序b就位於就緒佇列,萬事俱備只欠cpu,如圖所示:

當程序a被暫停執行後cpu是不可以閒下來的,因為就緒佇列中還有嗷嗷待哺的程序b,這時作業系統開始在就緒佇列中找下乙個可以執行的程序,也就是這裡的程序b。

此時作業系統將程序b從就緒佇列中取出,找出程序b被暫停時執行到的機器指令的位置,然後將cpu的pc暫存器指向該位置,這樣程序b就開始執行啦,如圖所示:

注意,注意,接下來的這段是重點中的重點。

最後需要注意的一點就是上面的講解中我們直接把磁碟資料copy到了程序空間中,但實際上一般情況下i/o資料是要首先copy到作業系統內部,然後作業系統再copy到程序空間中。因此我們可以看到這裡其實還有一層經過作業系統的copy,對於效能要求很高的場景其實也是可以繞過作業系統直接進行資料copy的,這也是本文描述的場景,這種繞過作業系統直接進行資料copy的技術被稱為zero-copy,也就零拷貝,高併發、高效能場景下常用的一種技術,原理上很簡單吧。

計算機內功心法 二 讀取檔案時,程式經歷了什麼?

在回答這個問題之前,我們先來看下為什麼對於計算機來說i o是極其重要的。相信對於程式設計師來說i o操作是最為熟悉不過的了 想一想,如果沒有i o計算機該是一種多麼枯燥的裝置,不能看電影 不能玩遊戲,也不能上網,這樣的計算機最多就是乙個大號的計算器。既然i o這麼重要,那麼到底什麼才是i o呢?i ...

2020,我經歷了什麼

2020年,我大學畢業了!這是一次不知所云的畢業,我們沒有穿學士服,沒有拍畢業照,最後是在校門口拿到自己的畢業證和學位證 2020年上半年,除了畢業 另外乙個重要的事情就是找工作,四月初,自己通過網路面試,拿到了一家做軟體的公司office,崗位是前端實習生,因為是自己喜歡的工作,雖然工資只有3千,...

Spring Batch CSV檔案讀取時的注意點

按照spring batch 之 sample csv檔案操作 四 的方式配置好csvitemreader,發現讀入的資料很是奇怪,通過修改配置檔案發現,commit interval 1 的時候,例程是沒有問題的.如果大於1,例如設為50,則會把第50條資料,讀50遍進來.跟蹤 的getbean ...