五分鐘聊完磁盤
盤
盤可以說是硬件里面比較簡單的構(gòu)造了,同時(shí)也是最重要的。下面我們從盤談起,聊聊它的物理構(gòu)造
盤硬件
盤會有很多種類型。其中最簡單的構(gòu)造就是磁盤(magnetic hard disks), 也被稱為 hard disk,HDD等。磁盤通常與安裝在磁臂上的磁頭配對,磁頭可將數(shù)據(jù)讀取或者將數(shù)據(jù)寫入磁盤,因此磁盤的讀寫速度都同樣快。在磁盤中,數(shù)據(jù)是隨機(jī)訪問的,這也就說明可以通過任意的順序來存儲和檢索單個(gè)數(shù)據(jù)塊,所以你可以在任意位置放置磁盤來讓磁頭讀取,磁盤是一種非易失性的設(shè)備,即使斷電也能永久保留。
在計(jì)算機(jī)發(fā)展早期一般是用光盤來存儲數(shù)據(jù)的,然而隨著固態(tài)硬盤的流行,固態(tài)硬盤不包含運(yùn)動部件的特點(diǎn),成為現(xiàn)在計(jì)算機(jī)的首選存儲方式。
磁盤
為了組織和檢索數(shù)據(jù),會將磁盤組織成特定的結(jié)構(gòu),這些特定的結(jié)構(gòu)就是磁道、扇區(qū)和柱面
每一個(gè)磁盤都是由無數(shù)個(gè)同心圓組成,這些同心圓就好像樹的年輪一樣
“部分樹的年輪照片都要付費(fèi)下載了,不敢直接白嫖,闊怕闊怕。
磁盤被組織成柱面形式,每個(gè)盤用軸相連,每一個(gè)柱面包含若干磁道,每個(gè)磁道由若干扇區(qū)組成。軟盤上大約每個(gè)磁道有 8 - 32 個(gè)扇區(qū),硬盤上每條磁道上扇區(qū)的數(shù)量可達(dá)幾百個(gè),磁頭大約是 1 - 16 個(gè)。
對于磁盤驅(qū)動程序來說,一個(gè)非常重要的特性就是控制器是否能夠同時(shí)控制兩個(gè)或者多個(gè)驅(qū)動器進(jìn)行磁道尋址,這就是重疊尋道(overlapped seek)。對于控制器來說,它能夠控制一個(gè)磁盤驅(qū)動程序完成尋道操作,同時(shí)讓其他驅(qū)動程序等待尋道結(jié)束??刂破饕部梢栽谝粋€(gè)驅(qū)動程序上進(jìn)行讀寫操作,與此同時(shí)讓另外的驅(qū)動器進(jìn)行尋道操作,但是軟盤控制器不能在兩個(gè)驅(qū)動器上進(jìn)行讀寫操作。
RAID
RAID 稱為 磁盤冗余陣列,簡稱 磁盤陣列。利用虛擬化技術(shù)把多個(gè)硬盤結(jié)合在一起,成為一個(gè)或多個(gè)磁盤陣列組,目的是提升性能或數(shù)據(jù)冗余。
RAID 有不同的級別
- RAID 0 - 無容錯的條帶化磁盤陣列
- RAID 1 - 鏡像和雙工
- RAID 2 - 內(nèi)存式糾錯碼
- RAID 3 - 比特交錯奇偶校驗(yàn)
- RAID 4 - 塊交錯奇偶校驗(yàn)
- RAID 5 - 塊交錯分布式奇偶校驗(yàn)
- RAID 6 - P + Q冗余
磁盤格式化
磁盤由一堆鋁的、合金或玻璃的盤片組成,磁盤剛被創(chuàng)建出來后,沒有任何信息。磁盤在使用前必須經(jīng)過低級格式化(low-levvel format),下面是一個(gè)扇區(qū)的格式
前導(dǎo)碼相當(dāng)于是標(biāo)示扇區(qū)的開始位置,通常以位模式開始,前導(dǎo)碼還包括柱面號、扇區(qū)號等一些其他信息。緊隨前導(dǎo)碼后面的是數(shù)據(jù)區(qū),數(shù)據(jù)部分的大小由低級格式化程序來確定。大部分磁盤使用 512 字節(jié)的扇區(qū)。數(shù)據(jù)區(qū)后面是 ECC,ECC 的全稱是 error correction code ,數(shù)據(jù)糾錯碼,它與普通的錯誤檢測不同,ECC 還可以用于恢復(fù)讀錯誤。ECC 階段的大小由不同的磁盤制造商實(shí)現(xiàn)。ECC 大小的設(shè)計(jì)標(biāo)準(zhǔn)取決于設(shè)計(jì)者愿意犧牲多少磁盤空間來提高可靠性,以及程序可以處理的 ECC 的復(fù)雜程度。通常情況下 ECC 是 16 位,除此之外,硬盤一般具有一定數(shù)量的備用扇區(qū),用于替換制造缺陷的扇區(qū)。
低級格式化后的每個(gè) 0 扇區(qū)的位置都和前一個(gè)磁道存在偏移,如下圖所示
這種方式又被稱為 柱面斜進(jìn)(cylinder skew),之所以采用這種方式是為了提高程序的運(yùn)行性能。可以這樣想,磁盤在轉(zhuǎn)動的過程中會經(jīng)由磁頭來讀取扇區(qū)信息,在讀取內(nèi)側(cè)一圈扇區(qū)數(shù)據(jù)后,磁頭會進(jìn)行向外側(cè)磁道的尋址操作,尋址操作的同時(shí)磁盤在繼續(xù)轉(zhuǎn)動,如果不采用這種方式,可能剛好磁頭尋址到外側(cè),0 號扇區(qū)已經(jīng)轉(zhuǎn)過了磁頭,所以需要旋轉(zhuǎn)一圈才能等到它繼續(xù)讀取,通過柱面斜進(jìn)的方式可以消除這一問題。
柱面斜進(jìn)量取決于驅(qū)動器的幾何規(guī)格。柱面斜進(jìn)量就是兩個(gè)相鄰?fù)膱A 0 號扇區(qū)的差異量。如下圖所示
這里需要注意一點(diǎn),不只有柱面存在斜進(jìn),磁頭也會存在斜進(jìn)(head skew),但是磁頭斜進(jìn)比較小。
磁盤格式化會減少磁盤容量,減少的磁盤容量都會由前導(dǎo)碼、扇區(qū)間間隙和 ECC 的大小以及保留的備用扇區(qū)數(shù)量。
在磁盤使用前,還需要經(jīng)過最后一道工序,那就是對每個(gè)分區(qū)分別執(zhí)行一次高級格式化(high-level format),這一操作要設(shè)置一個(gè)引導(dǎo)塊、空閑存儲管理(采用位圖或者是空閑列表)、根目錄和空文件系統(tǒng)。這一步操作會把碼放在分區(qū)表項(xiàng)中,告訴分區(qū)使用的是哪種文件系統(tǒng),因?yàn)樵S多操作系統(tǒng)支持多個(gè)兼容的文件系統(tǒng)。在這一步之后,系統(tǒng)就可以進(jìn)行引導(dǎo)過程。
當(dāng)電源通電后,BIOS 首先運(yùn)行,它會讀取主引導(dǎo)記錄并跳轉(zhuǎn)到主引導(dǎo)記錄中。然后引導(dǎo)程序會檢查以了解哪個(gè)分區(qū)是處于活動的。然后,它從該分區(qū)讀取啟動扇區(qū)(boot sector)并運(yùn)行它。啟動扇區(qū)包含一個(gè)小程序來加載一個(gè)更大一點(diǎn)的引導(dǎo)器來搜索文件系統(tǒng)以找到系統(tǒng)內(nèi)核(system kernel),然后程序被轉(zhuǎn)載進(jìn)入內(nèi)存并執(zhí)行。
“這里說下什么是引導(dǎo)扇區(qū):引導(dǎo)扇區(qū)是磁盤或者存儲設(shè)備的保留扇區(qū),其中包含用于完成計(jì)算機(jī)或磁盤引導(dǎo)過程所必要的數(shù)據(jù)或者代碼。引導(dǎo)扇區(qū)存儲引導(dǎo)記錄數(shù)據(jù),這些數(shù)據(jù)用于在計(jì)算機(jī)啟動時(shí)提供指令。有兩種不同類型的引導(dǎo)扇區(qū)
Master boot record 稱為主引導(dǎo)扇區(qū)
Volume boot record 卷啟動記錄
對于分區(qū)磁盤,引導(dǎo)扇區(qū)由主引導(dǎo)記錄組成;非分區(qū)磁盤由卷啟動記錄組成。
磁盤臂調(diào)度算法
下面我們來探討一下關(guān)于影響磁盤讀寫的算法,一般情況下,影響磁盤快讀寫的時(shí)間由下面幾個(gè)因素決定
- 尋道時(shí)間 - 尋道時(shí)間指的就是將磁盤臂移動到需要讀取磁盤塊上的時(shí)間
- 旋轉(zhuǎn)延遲 - 等待合適的扇區(qū)旋轉(zhuǎn)到磁頭下所需的時(shí)間
- 實(shí)際數(shù)據(jù)的讀取或者寫入時(shí)間
這三種時(shí)間參數(shù)也是磁盤尋道的過程。一般情況下,尋道時(shí)間對總時(shí)間的影響最大,所以,有效的降低尋道時(shí)間能夠提高磁盤的讀取速度。
如果磁盤驅(qū)動程序每次接收一個(gè)請求并按照接收順序完成請求,這種處理方式也就是 先來先服務(wù)(First-Come, First-served, FCFS) ,這種方式很難優(yōu)化尋道時(shí)間。因?yàn)槊看味紩凑枕樞蛱幚?,不管順序如何,有可能這次讀完后需要等待一個(gè)磁盤旋轉(zhuǎn)一周才能繼續(xù)讀取,而其他柱面能夠馬上進(jìn)行讀取,這種情況下每次請求也會排隊(duì)。
通常情況下,磁盤在進(jìn)行尋道時(shí),其他進(jìn)程會產(chǎn)生其他的磁盤請求。磁盤驅(qū)動程序會維護(hù)一張表,表中會記錄著柱面號當(dāng)作索引,每個(gè)柱面未完成的請求會形成鏈表,鏈表頭存放在表的相應(yīng)表項(xiàng)中。
一種對先來先服務(wù)的算法改良的方案是使用 最短路徑優(yōu)先(SSF) 算法,下面描述了這個(gè)算法。
假如我們在對磁道 6 號進(jìn)行尋址時(shí),同時(shí)發(fā)生了對 11 , 2 , 4, 14, 8, 15, 3 的請求,如果采用先來先服務(wù)的原則,如下圖所示
我們可以計(jì)算一下磁盤臂所跨越的磁盤數(shù)量為 5 + 9 + 2 + 10 + 6 + 7 + 12 = 51,相當(dāng)于是跨越了 51 次盤面,如果使用最短路徑優(yōu)先,我們來計(jì)算一下跨越的盤面
跨越的磁盤數(shù)量為 4 + 1 + 1 + 4 + 3 + 3 + 1 = 17 ,相比 51 足足省了兩倍的時(shí)間。
但是,最短路徑優(yōu)先的算法也不是完美無缺的,這種算法照樣存在問題,那就是優(yōu)先級 問題,
這里有一個(gè)原型可以參考就是我們?nèi)粘I钪械碾娞?,電梯使用一種電梯算法(elevator algorithm) 來進(jìn)行調(diào)度,從而滿足協(xié)調(diào)效率和公平性這兩個(gè)相互沖突的目標(biāo)。電梯一般會保持向一個(gè)方向移動,直到在那個(gè)方向上沒有請求為止,然后改變方向。
電梯算法需要維護(hù)一個(gè)二進(jìn)制位,也就是當(dāng)前的方向位:UP(向上)或者是 DOWN(向下)。當(dāng)一個(gè)請求處理完成后,磁盤或電梯的驅(qū)動程序會檢查該位,如果此位是 UP 位,磁盤臂或者電梯倉移到下一個(gè)更高級未完成的請求。如果高位沒有未完成的請求,則取相反方向。當(dāng)方向位是 DOWN時(shí),同時(shí)存在一個(gè)低位的請求,磁盤臂會轉(zhuǎn)向該點(diǎn)。如果不存在的話,那么它只是停止并等待。
我們舉個(gè)例子來描述一下電梯算法,比如各個(gè)柱面得到服務(wù)的順序是 4,7,10,14,9,6,3,1 ,那么它的流程圖如下
所以電梯算法需要跨越的盤面數(shù)量是 3 + 3 + 4 + 5 + 3 + 3 + 1 = 22
電梯算法通常情況下不如 SSF 算法。
一些磁盤控制器為軟件提供了一種檢查磁頭下方當(dāng)前扇區(qū)號的方法,使用這樣的控制器,能夠進(jìn)行另一種優(yōu)化。如果對一個(gè)相同的柱面有兩個(gè)或者多個(gè)請求正等待處理,驅(qū)動程序可以發(fā)出請求讀寫下一次要通過磁頭的扇區(qū)。
“這里需要注意一點(diǎn),當(dāng)一個(gè)柱面有多條磁道時(shí),相繼的請求可能針對不同的磁道,這種選擇沒有代價(jià),因?yàn)檫x擇磁頭不需要移動磁盤臂也沒有旋轉(zhuǎn)延遲。
對于磁盤來說,最影響性能的就是尋道時(shí)間和旋轉(zhuǎn)延遲,所以一次只讀取一個(gè)或兩個(gè)扇區(qū)的效率是非常低的。出于這個(gè)原因,許多磁盤控制器總是讀出多個(gè)扇區(qū)并進(jìn)行高速緩存,即使只請求一個(gè)扇區(qū)時(shí)也是這樣。一般情況下讀取一個(gè)扇區(qū)的同時(shí)會讀取該扇區(qū)所在的磁道或者是所有剩余的扇區(qū)被讀出,讀出扇區(qū)的數(shù)量取決于控制器的高速緩存中有多少可用的空間。
磁盤控制器的高速緩存和操作系統(tǒng)的高速緩存有一些不同,磁盤控制器的高速緩存用于緩存沒有實(shí)際被請求的塊,而操作系統(tǒng)維護(hù)的高速緩存由顯示地讀出的塊組成,并且操作系統(tǒng)會認(rèn)為這些塊在近期仍然會頻繁使用。
當(dāng)同一個(gè)控制器上有多個(gè)驅(qū)動器時(shí),操作系統(tǒng)應(yīng)該為每個(gè)驅(qū)動器都單獨(dú)的維護(hù)一個(gè)未完成的請求表。一旦有某個(gè)驅(qū)動器閑置時(shí),就應(yīng)該發(fā)出一個(gè)尋道請求來將磁盤臂移到下一個(gè)被請求的柱面。如果下一個(gè)尋道請求到來時(shí)恰好沒有磁盤臂處于正確的位置,那么驅(qū)動程序會在剛剛完成傳輸?shù)尿?qū)動器上發(fā)出一個(gè)新的尋道命令并等待,等待下一次中斷到來時(shí)檢查哪個(gè)驅(qū)動器處于閑置狀態(tài)。
錯誤處理
磁盤在制造的過程中可能會有瑕疵,如果瑕疵比較小,比如只有幾位,那么使用壞扇區(qū)并且每次只是讓 ECC 糾正錯誤是可行的,如果瑕疵較大,那么錯誤就不可能被掩蓋。
一般壞塊有兩種處理辦法,一種是在控制器中進(jìn)行處理;一種是在操作系統(tǒng)層面進(jìn)行處理。
這兩種方法經(jīng)常替換使用,比如一個(gè)具有 30 個(gè)數(shù)據(jù)扇區(qū)和兩個(gè)備用扇區(qū)的磁盤,其中扇區(qū) 4 是有瑕疵的。
控制器能做的事情就是將備用扇區(qū)之一重新映射。
還有一種處理方式是將所有的扇區(qū)都向上移動一個(gè)扇區(qū)
上面這這兩種情況下控制器都必須知道哪個(gè)扇區(qū),可以通過內(nèi)部的表來跟蹤這一信息,或者通過重寫前導(dǎo)碼來給出重新映射的扇區(qū)號。如果是重寫前導(dǎo)碼,那么涉及移動的方式必須重寫后面所有的前導(dǎo)碼,但是最終會提供良好的性能。
穩(wěn)定存儲器
磁盤經(jīng)常會出現(xiàn)錯誤,導(dǎo)致好的扇區(qū)會變成壞扇區(qū),驅(qū)動程序也有可能掛掉。RAID 可以對扇區(qū)出錯或者是驅(qū)動器崩潰提出保護(hù),然而 RAID 卻不能對壞數(shù)據(jù)中的寫錯誤提供保護(hù),也不能對寫操作期間的崩潰提供保護(hù),這樣就會破壞原始數(shù)據(jù)。
我們期望磁盤能夠準(zhǔn)確無誤的工作,但是事實(shí)情況是不可能的,但是我們能夠知道的是,一個(gè)磁盤子系統(tǒng)具有如下特性:當(dāng)一個(gè)寫命令發(fā)給它時(shí),磁盤要么正確地寫數(shù)據(jù),要么什么也不做,讓現(xiàn)有的數(shù)據(jù)完整無誤的保留。這樣的系統(tǒng)稱為 穩(wěn)定存儲器(stable storage)。穩(wěn)定存儲器的目標(biāo)就是不惜一切代價(jià)保證磁盤的一致性。
穩(wěn)定存儲器使用兩個(gè)一對相同的磁盤,對應(yīng)的塊一同工作形成一個(gè)無差別的塊。穩(wěn)定存儲器為了實(shí)現(xiàn)這個(gè)目的,定義了下面三種操作:
- 穩(wěn)定寫(stable write)
- 穩(wěn)定讀(stable read)
- 崩潰恢復(fù)(crash recovery)
穩(wěn)定寫指的就是首先將塊寫到比如驅(qū)動器 1 上,然后將其讀回來驗(yàn)證寫入的是否正確,如果不正確,那么就會再次嘗試寫入和讀取,一直到能夠驗(yàn)證寫入正確為止。如果塊都寫完了也沒有驗(yàn)證正確,就會換塊繼續(xù)寫入和讀取,直到正確為止。無論嘗試使用多少個(gè)備用塊,都是在對你驅(qū)動器 1 寫入成功之后,才會對驅(qū)動器 2 進(jìn)行寫入和讀取。這樣我們相當(dāng)于是對兩個(gè)驅(qū)動器進(jìn)行寫入。
穩(wěn)定讀指的就是首先從驅(qū)動器 1 上進(jìn)行讀取,如果讀取操作會產(chǎn)生錯誤的 ECC,則再次嘗試讀取,如果所有的讀取操作都會給出錯誤的 ECC,那么會從驅(qū)動器 2 上進(jìn)行讀取。這樣我們相當(dāng)于是對兩個(gè)驅(qū)動器進(jìn)行讀取。
崩潰恢復(fù)指的是崩潰之后,恢復(fù)程序掃描兩個(gè)磁盤,比較對應(yīng)的塊。如果一對塊都是好的并且是相同的,就不會觸發(fā)任何機(jī)制;如果其中一個(gè)塊觸發(fā)了 ECC 錯誤,這時(shí)候就需要使用好塊來覆蓋壞塊。
如果 CPU 沒有崩潰的話,那么這種方式是可行的。如果在穩(wěn)定寫期間出現(xiàn) CPU 崩潰會怎么樣?這就取決于崩潰發(fā)生的精確時(shí)間,有五種情況,下面來說一下
- 第一種情況是崩潰發(fā)生在寫入之前,在恢復(fù)的時(shí)候就什么都不需要修改,舊的值也會繼續(xù)存在。
- 第二種情況是 CPU 崩潰發(fā)生在寫入驅(qū)動器 1 的時(shí)候,崩潰導(dǎo)致塊內(nèi)容被破壞,然而恢復(fù)程序能夠檢測出這一種錯誤,并且從驅(qū)動器 2 恢復(fù)驅(qū)動器 1 上的塊。
- 第三種情況是崩潰發(fā)生在磁盤驅(qū)動器 1 之后但是還沒有寫驅(qū)動器 2 之前,這種情況下由于磁盤 1 已經(jīng)寫入成功
- 第四種情況是崩潰發(fā)生在磁盤驅(qū)動 1 寫入后在磁盤驅(qū)動 2 寫入時(shí),恢復(fù)期間會用好的塊替換壞的塊,兩個(gè)塊的最終值都是最新的
- 最后一種情況就是崩潰發(fā)生在兩個(gè)磁盤驅(qū)動寫入后,這種情況下不會發(fā)生任何問題
這種模式下進(jìn)行任何優(yōu)化和改進(jìn)都是可行的,但是代價(jià)高昂,一種改進(jìn)是在穩(wěn)定寫期間監(jiān)控被寫入的塊,這樣在崩潰后進(jìn)行檢驗(yàn)的塊只有一個(gè)。
有一種 非易失性 RAM 能夠在崩潰之后保留數(shù)據(jù),但是這種方式并不推薦使用。