天天看點

《Linux是怎麼樣工作的》讀書筆記

《Linux是怎麼樣工作的》讀書筆記

引言

這本書是個人看過的講作業系統底層裡面講的最通俗易懂的了,但是200多頁的内容确實講不了多深的内容,是以不要對這本書抱有過高期待,當一個入門書了解即可。

書籍介紹

  1. 原富士通一線Linux核心開發工程師基于十餘年經驗執筆,專業實用
  2. 聚焦Linux核心功能,簡明易懂地講解作業系統是怎樣工作的
  3. 198張示意圖幫助了解,詳略得當,為讀懂大部頭開路
  4. 結合大量實驗程式,邊動手操作邊學習,真實體驗系統運作過程

個人評價

内容比較基礎,但是有關Linux的内容都有涉及,另外作者用C語言程式對于作業系統的緩存,交換記憶體,CPU程序管理器,固體硬碟和機械硬碟随機讀寫和順序讀寫做驗證和測試的程式比較有意思。

但是不得不說能把作業系統這種抽象的東西講的生動形象實屬不易,作者在日本一線大廠(可以翻翻富士通相關資料)搞Linux核心開發專業程度也毋庸置疑,另外這本書的編排是由淺入深的挺不錯。

總結:非常難以定位的一本書,建議可以參考裡面的知識,根據相關内容深入即可。

資源

2022年3月出版的一本書是以沒有找到相關資源。

下面内容為随書一些C語言模拟作業系統底層工作的一些程式,感興趣可以下載下傳來看一下。

連結: ​​pan.baidu.com/s/1eU65e1OK…​​ 提取碼: pghr

筆記索引

注意筆記的索引并不是按照原書的結構組織,因為個人閱讀這本書是“倒着”讀的,結合目錄發現從後往前讀比較符合個人的了解習慣,也就是從外部存儲器到内部的工作機制比較符合個人的思考。

可以點選副标題跳轉到相關的節點。

正常的認識是知識由淺入深,其實有時候用難易交叉學習的方式可能更加符合人的學習習慣

​​第一部分:Linux與外部結構介紹​​

主要介紹了機械磁盤和SSD硬碟的工作機制對比,順序讀寫和随機讀寫之間的差别,這部分使用C語言模拟磁盤的讀寫性能比較有意思。

介紹了Linux和裝置互動的檔案系統設計,一共分為7層,當然書中隻是簡單歸納,如果要深入需要閱讀更多資料。

講述了IO排程器和預讀機制的相關内容。

​​第二部分:Linux檔案系統設計​​

這一節講述如何快速的了解一個Linux檔案系統的設計方式,檔案系統的設計當然沒有不是幾頁紙就能講清楚的,但是對于我們大緻了解Linux整體的設計思路入門還是不錯的。

​​第三部分:計算機存儲層次簡析​​

如果你對筆記電腦或者桌上型電腦主機闆等等基本配置了解,或者對整個作業系統的工作程序有一個大緻的了解,這一節的内容完全可以跳過。

計算機的存儲器層級結構是越靠近CPU和CPU關系越密切價格越高容量越小,我們常見的存儲器,速度從快到慢的排序是:寄存器 -> 高速緩存 -> 記憶體 -> 外部存儲器,這一節則針對這幾個存儲層級進行介紹。

之後會介紹關于轉譯後備緩沖區,頁面緩存,緩沖區緩存和Linux不常見也幾乎不使用的緩存調優參數。

​​第四部分:Linux記憶體管理和優化​​

針對記憶體的管理是作業系統程序管理核心和關鍵所在,此部分介紹了關于記憶體管理的内容,記憶體管理是整本書介紹最為詳細的部分,個人認為核心是掌握 請求分頁和 寫時複制,這兩個特性被大量使用,除此之外了解記憶體的配置設定方式和配置設定的細節過程也是必要的。

另外這部分個人筆記在補充的同時也将内容拆分為上下部分:

  • ​​Linux記憶體管理​​
  • ​​Linux記憶體管理優化​​

​​第五部分:程序排程器​​ CPU程序排程目前主流的方式是兩種,第一種是像window那樣搶占式排程,每一個CPU可能會出現排程時間配置設定不等的情況,而另一種是時間分片的方式,時間分片是Linux 常見的程序排程器,特點是每一個程序有近似相等的CPU使用權,在使用完成之後立馬交給下一個程序完成工作,使用分片的方式雖然可能導緻一些重要任務延遲,但這樣的處理和排程方式使得系統最為穩定。

程序排程器本身很複雜,為了減少複雜性作者沒有做過多介紹,是以個人筆記内容也相對較少。

​​第零部分:計算機程式概覽​​ 了解作業系統運作我們需要了解有關計算機資訊的基礎概念,我想如果有想法去研究作業系統底層多少對于計算機的基礎理念不會陌生,是以這一部分個人當作總結。

​​附錄​​ 此部分是對于第一個部分實體磁盤的配置設定方式資料擴充,感興趣可以閱讀。

注意⚠:最後個人的筆記組織形式将會是難-易-難混雜的組織方式。

Linux與外部結構介紹

HDD磁盤介紹

機械磁盤從俯瞰的邏輯結構了解為類似一個同心圓的多個圈,從外層到内層進行編号,磁盤通過順時針順序編号,逆時針轉動磁盤,這樣處理是考慮查找磁盤的時候可以直接按照順序掃描過去,磁頭前進方向就是編号遞增方向。

在下面的結構圖中,磁道是每一個同心圓,而扇區指的是切割磁道所形成的“扇面”,因為切割之後的樣子很像扇子的樣子是以被稱為扇區,掃描資料需要磁頭在磁道上滑動,同時扇區會從0開始編号,一個編号對應一個扇區。

注意這是磁盤的俯視圖,也就是說線的部分是實體磁盤上的“溝壑”,而扇區就是編号内的塊。
《Linux是怎麼樣工作的》讀書筆記
《Linux是怎麼樣工作的》讀書筆記

下面是磁盤的側面切面圖,垂直疊加的盤通過一個磁盤一個磁頭的組合,通過多磁頭加快資料的處理速度。

《Linux是怎麼樣工作的》讀書筆記

注意在HDD磁盤中一個扇區讀寫最小機關為512位元組,而且每一個扇區都是512位元組,不管扇區是在外層還是内層。

⚠️注意:很多架構或者資料庫會把一次讀寫的大小設定為512位元組,因為512是最小讀寫機關是以可以不需要額外的維護可以保證讀寫的原子性。

磁盤大小計算

最早期磁盤可以通過下面的公式計算出整個磁盤的大小,因為磁道和扇區的數量是一一對應的:

存儲容量=磁頭數磁道(柱面)數每道扇區數*每扇區位元組數

這樣的設計有一個顯而易見的問題那就是無論扇區面積大還是小都是固定大小,很顯然外層的扇區資料被白白浪費了。

針對這樣的問題後續的機械磁盤出現了改進,這項技術叫做ZBR(Zoned Bit Recording,區位記錄)技術,這項技術根據每一圈的扇區來劃分大小,同一個磁道圈内的扇區分布和大小相同。

這意味着越是外層的扇區數量越多,而内圈則較少,在劃分之後密度均勻分布。

由于磁盤扇區存儲形式的改進,尋址模式自然也要跟着進步,如今的硬碟大多使用 、LBA(Logical Block Addressing)邏輯塊尋址模式,了解這個尋址模式才能了解磁盤的大小的計算方式。

然而現在HDD的磁盤讀寫受限在随機讀寫速度上,過去HDD磁盤比較流行的轉速為7200轉和5400轉等等,差別的話是跑的快和跑的慢一點的螞蟻。

雖然有SAS硬碟能突破15000轉,并且現在還有研究團隊研究尋找不同材料或者其他方式突破磁盤實體轉速的限制(比如雙磁盤轉動的方式加快旋轉),然而始終無法突破機械磁盤的設計的實體限制。

市面上為什麼主流販賣7200轉的磁盤和5400的轉速的磁盤而不是别的磁盤?

一方面是7200的随機讀寫性能經過測試是最佳的,同時讨論一塊磁盤的性能不能看順序讀寫的速度而是要看随機讀寫的速度。

針對機械磁盤存在一些實體壁壘,自東芝公司在1984年研究出閃存之後,閃存技術不斷進步,又經過了5年之後的1989年,SSD磁盤逐漸走進曆史舞台。

⚠️注意:為什麼是7200轉和5400轉等等奇怪數字?

這兩個數字都要從3600說起,計算機的前十年幾乎所有的硬碟都是3600轉的,這個3600又是從哪裡來的呢?因為美國的交流電是60Hz的!于是就有了下面的公式:

  • 60Hz × 1轉/Hz ×60秒/分鐘 = 3600轉/分鐘
  • 5400 RPM = 3600 RPM × 1.5
  • 7200 RPM = 3600 RPM × 2 另外還有一個原因是專利争奪,你會發現轉速有15000卻沒有10000,9000,8000這種數字,其實都是因為整數和500的倍數轉速都被專利注冊了,但是專利注冊者估計沒想到轉速能破萬吧。

SSD磁盤介紹

SSD 的硬碟分為兩種,一種是基于閃存顆粒的閃存固态硬碟,另一種是DRAM 硬碟。

閃存顆粒的硬碟也就是我們現代筆記本電腦以及移動固态多數使用的硬碟,這種磁盤的最大優點是可以移動,同時資料的保護不依賴電源就可以存儲,在閃存顆粒中通常被分為 QLC,MLC,TLC,哪怕是壽命最短的QLC硬碟也有5-6年的壽命,而MLC壽命最長,保護得當往往可以十幾年正常工作。

⚠️注意:固态硬碟過去成本非常高是以機械磁盤是主流,廣泛普及也就這幾年時間,是以上面說的内容都是理想狀态。

在企業級的伺服器使用的固态中通常以MLC為主,SSD磁盤的最大特點是不像是HDD一樣受到實體沖擊有可能造成整塊磁盤不可用,但是SSD一旦損壞資料的修複成本很高或者說根本無法修複。

現在來看 SSD已經非常便宜了,但是HDD的大資料低成本存儲依然很受一些使用者歡迎。

DRAM是介于機械磁盤和固态硬碟中間的形式,其采用DRAM作為存儲單元,它效仿傳統硬碟的設計,可被絕大部分作業系統的檔案系統工具進行卷設定和管理,并提供工業标準的PCI和FC接口用于連接配接主機或者伺服器,但是最大問題應用範圍相對較窄。

HDD資料讀取方式

HDD的磁盤讀取資料順序如下:

  • 裝置将需要讀寫的扇區和裝置号碼以及掃描多少個扇區告訴磁盤
  • 移動磁頭和轉動盤片找到對應扇區。
  • 讀取資料,把資料寫入緩沖。
  • 如果所有的扇區掃描完成,讀取的操作則算是完成。

HDD磁盤讀寫的要點

從邏輯上來看計算扇區掃描位置和掃描數量計算處理速度是很快的,将扇區内的資料讀取或者寫入的資料也是相對較快的。

然而我們知道因為轉速限制的和磁頭和盤片實體掃描是非常慢的,整個讀寫的性能瓶頸是磁頭扇區尋址和掃描磁盤所需的實體磁盤開銷以及最後帶來的随機讀寫性能的權衡。

讀寫方式

磁盤掃描的幾種情況磁盤掃描的方式直接決定了資料處理讀寫速度:

  • 順序掃描:順序掃描就是在一個磁道上直接劃過連續的幾個扇區,一次掃描就可以擷取資料是以非常快。
  • 多次連續順序掃描:連續順序掃描是針對連續的幾個扇區進行多次掃描,這個時間開銷主要是在盤片的轉動上,雖然依然比較快但是盤片轉動依然産生一定延遲。
  • 随機讀寫:随機讀寫的開銷主要在磁道來回尋址上,此時不但可能會産生磁盤轉動,磁頭還需要尋找分散的扇區,随機讀寫的效率是非常低的
⚠️注意:對于單次IO的通路如果擷取的資料量超過磁盤請求資料量的上限,則會把請求由單次的順序讀寫,拆分為多次的順序掃描。

影響硬碟性能的因素

  1. 磁盤尋道時間:磁盤的平均尋道時間一般在3-15ms。
  2. 磁盤轉速:轉速越快
  3. 磁盤本身的讀寫性能:和磁盤的設計廠商也有關系,随機讀寫強的HDD硬碟通常具備更好的IO性能,同時磁盤資料傳輸的越快傳輸量越大效果越好(廢話)。

機械磁盤需要關注尋道時間和旋轉延遲,當然HDD的羸弱的讀寫性能實際上大同小異。

通用塊層

通用塊層:指的是在Linux系統對于HDD和SSD的抽象。

HDD和SDD它們被稱為塊裝置。塊裝置的通路方式有兩種,第一種是直接通過挂載的方式通過裝置檔案直接讀寫,第二種是根據檔案系統對于磁盤進行封裝以及提供引導入口,當然大部分的軟體使用第二種方式。

由于不同類型的塊裝置處理方式不同,這些裝置的處理的需要依賴驅動程式的控制才能實作通路,但是以我們日常使用Windows系統的經驗,不可能是一個塊裝置一個驅動程式,不然我們每一次加入新硬碟都要裝一遍驅動,這樣也太麻煩了。

那麼作業系統如何解決這個問題?這也就是通用塊層的作用了:

《Linux是怎麼樣工作的》讀書筆記

Linux和裝置的互動流程

這一個流程圖有很多細節可以了解,抽出任何一個層都能寫一篇長文出來,這裡我們以簡單了解IO排程器和磁盤預讀機機制為主。

《Linux是怎麼樣工作的》讀書筆記

IO排程器和預讀機制

在HDD中還有兩個十分影響性能的機制,IO排程器和預讀機制,但是注意針對HDD的預讀要比 SSD的預讀效果要好很多,因為有時候因為排序和預讀容易導緻SSD的負面優化。

⚠️注意:有時候我們更新老裝置将機械磁盤換固态的時候重裝系統有可能出現黑屏,這是因為部分舊主機闆會通過BIOS對機械磁盤調優,類似對于磁盤“預熱”,然而這種預熱會影響固态的啟動,是以出現類似情況可以檢查BIOS是否有勾選類似的加速機械磁盤啟動的選項。

IO排程器

IO排程器:是針對塊裝置通路的時候将請求積攢到一定的時間之後在進行一次請求。

是以針對IO排程器主要有下面兩個重要的工作:

  • 合并:把對于多個扇區的通路IO請求合并為一個請求。
  • 排序:因為每個扇區都有編号,IO排程器會把連續的扇區通路的IO進行排序之後再進行通路,使得磁盤掃描更加趨近順序掃描。
⚠️注意:IO排程器一般針對并發線程讀寫或者異步IO過程中等待IO結果的時候使用IO排程器,

預讀機制

磁盤的磁頭在掃描資料的時候,不會隻掃描裝置要求的幾個扇區,而是會多掃描周邊的扇區,注意這個預讀隻有在順序掃描的時候發揮作用,當預讀機制生效的時候如果發現預讀的扇區在下一次通路不會用到,直接丢棄即可。

Linux檔案系統設計

簡單的檔案系統如何設計

從最簡單的角度考慮設計基本的檔案系統我們可以用一個正常的檔案讀寫舉例。

最簡單的檔案系統包含下面的處理流程:

  • 首先檔案資料從0開始記錄,每一 個檔案在檔案系統中有名稱,大小和位置三條基本資訊。
  • 如果沒有檔案系統的輔助我們需要自行考慮檔案的磁盤存儲位置,需要從磁盤區域1到區域10根據檔案的大小存儲到塊裝置的對應位置,并且需要記錄目前塊的檔案寫入開始結束和結束為止,記錄存儲的資料大小。
《Linux是怎麼樣工作的》讀書筆記

為什麼會有“态”?

顯然在早期的單程序單使用者作業系統中,是不存在态這個概念的。然而随着程序和使用者的出現,當時的計算機面臨着一個重要問題,就是如何限制不同程序的操作的權限。

并不是所有的程序都能允許所有的外部使用者操作的,因為不知道未來會出現哪些新的程序運作,是以工程師為了讓系統和使用者的程序可以分開,就準備讓一些危險的操作隻允許作業系統的程序去做,使用者程序如果要做一些危險操作必須經過作業系統的“盤問”,之後再由作業系統去做。

最後“态”被設計為下面的形式:

《Linux是怎麼樣工作的》讀書筆記

模式切換

下面是Linux中使用者态核心态硬體三者的關系:

使用者态

是使用者看得見的操作,比如想要讀取某一個檔案或者想要某一個檔案改幾個字,使用者發送的這些指令通過核心轉為機器碼指令,然後在核心态對于磁盤進行IO操作。

也就是說 使用者模式:發送指令操作 -> 核心模式,翻譯使用者指令為塊裝置可以識别的指令 -> 硬體。

在上面設計的最簡單的檔案系統中,使用者模式切換到核心模式進行檔案管理隻需要關心檔案大小,檔案位置和檔案名稱。

核心态

隻有擁有系統操作權限的作業系統才有權進行操作和通路,一般都是執行一些系統的核心工作。

硬體

硬體又被稱為外部存儲,這些操作隻會和核心互動。

Linux檔案系統結構

Linux的檔案系統是樹狀結構設計,檔案系統可以支援不同格式,不同檔案系統的差别主要在最大支援操作檔案大小,檔案系統本身大小以及各種檔案操作速度差别上。

檔案系統存在ext2、ext3、ext4,它們的檔案大小和存儲形式和存儲的位置都有差别,那麼Linux是如何處理的呢?

Linux檔案系統将使用接口的形式将檔案IO操作進行抽象,無論檔案系統的結構形式如何變動,最終都是通過和下面提到相關的接口來完成互動的。

吐槽:有點像是設計模式的外觀模式
  • 建立删除:​

    ​create/unlink​

  • 打開關閉:​

    ​open/close​

  • 打開檔案讀資料:​

    ​read​

  • 打開檔案寫資料:​

    ​write​

  • 打開檔案移動到指定位置:​

    ​lseek​

  • 特殊檔案系統特殊操作:....

這一點和Linux塊裝置管理類似,在Linux與外部結構介紹中提到了塊裝置對于檔案系統提供了通用層塊進行抽象,對于使用者模式角度所看到的單塊裝置之間是沒有差别的,而真正的驅動處理出現在核心态中。

《Linux是怎麼樣工作的》讀書筆記

讀取檔案資料流程

在Linux中讀取檔案的流程如下:

  • 各個檔案系統通用處理。
  • 檔案系統專用處理,請求系統調用對應處理指令。
  • 裝置驅動執行讀寫資料操作。
  • 塊裝置驅動完成讀寫指令操作。

從邏輯結構來看整個互動流程很簡單,然而實際上這都是Linux的工程師們不斷努力的成果。

資料和中繼資料

在Linux資料的種類分為中繼資料和資料,中繼資料和檔案名稱,檔案大小,檔案位置相關,這些參數用于核心态讀取塊裝置作為參考,而資料則是我們日常使用的視訊資料,文本資料。

中繼資料除了上面的資訊種類之外還包含下面的内容:

  • 種類:判斷檔案是儲存資料的普通檔案還是目錄還是其他檔案,也就是檔案類型。
  • 時間資訊:檔案的建立時間、最後一次通路時間、最後一次修改時間。
  • 權限資訊:Linux權限控制使用者通路。

我們可以通過​

​df​

​​指令和相關參數可以詳細的了解檔案系統的參數和運作情況,​

​df​

​是重要的運維指令,可以通過它了解到磁盤的容量情況。

g@192 ~ % df

Filesystem                                                   512-blocks       Used Available Capacity  iused      ifree %iused  Mounted on

/dev/disk3s1s1                                                965595304   29663992  73597064    29%   500637  367985320    0%   /

devfs                                                               711        711         0   100%     1233          0  100%   /dev

/dev/disk3s6                                                  965595304         48  73597064     1%        0  367985320    0%   /System/Volumes/VM

/dev/disk3s2                                                  965595304    1034480  73597064     2%     2011  367985320    0%   /System/Volumes/Preboot

/dev/disk3s4                                                  965595304      31352  73597064     1%       48  367985320    0%   /System/Volumes/Update

/dev/disk1s2                                                    1024000      12328    985672     2%        3    4928360    0%   /System/Volumes/xarts

/dev/disk1s1                                                    1024000      15040    985672     2%       27    4928360    0%   /System/Volumes/iSCPreboot

/dev/disk1s3                                                    1024000       1240    985672     1%       39    4928360    0%   /System/Volumes/Hardware

/dev/disk3s5                                                  965595304  859395304  73597064    93%  1212174  367985320    0%   /System/Volumes/Data

/dev/disk6s1                                                 1000179712  807402240 192777472    81%  3153915     753037   81%   /Volumes/Untitled

/dev/disk7s1                                                 1953443840 1019557888 933885952    53%   497831     455999   52%   /Volumes/Extreme SSD

map auto_home                                                         0          0         0   100%        0          0  100%   /System/Volumes/Data/home

//GUEST:@Windows%2011._smb._tcp.local/%5BC%5D%20Windows%2011  535375864  212045648 323330216    40% 26505704   40416277   40%   /Volumes/[C] Windows 11.hidden

/dev/disk5s2                                                     462144     424224     37920    92%      596 4294966683    0%   /private/var/folders/wn/dvqxx9sx4y9dt1mr9lt_v4400000gn/T/zdKbGy      

磁盤配額

容量配額是磁盤管理中的核心,對于Linux檔案管理系統來說有下面幾種磁盤的配額方式:

  • 使用者配額:使用者配額通常指的是/home,通常每一個使用者家目錄有固定的額度配比
  • 子卷配額:限制名字為子卷的單元可用容量。
  • 目錄配額:可以通過特定目錄的可用容量,比如共享目錄的使用者可用容量,ext4和xfs可以設定目錄配額。

除了對于針對不同類型的配額之外,還需要考慮系統正常運作運作的系統配額,也就是說如果給使用者和系統按照一刀切的方式配比100%的方式劃分磁盤是一件危險的操作,對于一塊磁盤保持不超過80%是比較常見設定。

檔案系統意外恢複

資料管理最常見的問題是資料不一緻,諸如在沒有完成寫入的時候突然斷電,這種情況并不算特别少見,Linux提供了下面的兩種方式解決斷電資料狀态不一緻的問題:

  • 日志:通常出現在ext4和xfs的檔案系統。
  • 寫時複制:通常為btrfs的實作。

日志方式:

使用日志處理情況要多一些,因為日志的方式具備一定的可讀性也友善恢複,操作主要分為下面兩個步驟:

  • 資料修改之前把原子操作記錄到日志當中。
  • 當機恢複的時候根據日志記錄内容還原檔案狀态。

如果異常情況發生在日志記錄之前,可以直接丢棄寫入一部分的日志并且復原,當作檔案狀态沒有更改過。而如果異常狀态發生在原子操作的過程之後,則根據日志的記錄把操作重新執行一遍即可。

現代檔案系統更多的是來自于系統的BUG産生的資料不一緻問題,現代多使用SSD硬碟,SSD硬碟寫入都非常快基本不會出現寫入時資料不一緻問題。

寫時複制方式:

不同檔案系統對于寫時複制的實作不同,介紹寫時複制需要了解一些傳統的比如ext4和xfs的檔案系統工作機制。

這些檔案系統在檔案建立之後就會固定存放到磁盤的某個位置,哪怕删除或者更新檔案内容也隻是在原有空間上進行操作。

而​

​btrfs​

​的寫時複制的檔案系統管理方案則比較特殊,建立之後的檔案每次更新都會放到不一樣的位置。

寫時複制就是說更新和寫入都是一次類似“複制”的操作,當新資料寫入完成再把引用更新即可,原有的内容隻要不被新檔案覆寫還是可以被找到。

如果在寫入的時候突然斷電怎麼辦?

這時候資料是在另一個地方操作的,資料寫入到一半也不會對舊資料有影響,如果是其他操作情況下比如寫入剛完成沒有更新引用的情況,此時隻需要把引用更新一下即可。總之就是怎麼樣都不會影響原來的資料。

⚠️注意:其實磁盤本質上是沒有删除這個概念的,計算機所謂的删除隻是使用者程序無法通過正常操作通路被删除的檔案所在位址而已,但是通過一些特殊處理還是有辦法通過檔案碎片恢複原始檔案的。

無法恢複的意外

如果是檔案系統的BUG無法恢複的意外,對于不同的檔案系統來說處理方案也不同。

幾乎所有的檔案系統都有通用的​

​fsck​

​指令進行恢複,但是這個指令定義是有可能恢複資料狀态。

下面是關于這個指令的介紹:

fsck 指令

Linux fsck(英文全拼:file system check)指令用于檢查與修複 Linux 檔案系統,可以同時檢查一個或多個 Linux 檔案系統。

文法 fsck [-sACVRP] [-t fstype] [--] [fsck-options] filesys [...]

參數 :

  • filesys : device 名稱(eg./dev/sda1),mount 點 (eg. / 或 /usr)
  • -t : 給定檔案系統的型式,若在 /etc/fstab 中已有定義或 kernel 本身已支援的則不需加上此參數
  • -s : 依序一個一個地執行 fsck 的指令來檢查
  • -A : 對/etc/fstab 中所有列出來的 partition 做檢查
  • -C : 顯示完整的檢查進度
  • -d : 列印 e2fsck 的 debug 結果
  • -p : 同時有 -A 條件時,同時有多個 fsck 的檢查一起執行
  • -R : 同時有 -A 條件時,省略 / 不檢查
  • -V : 詳細顯示模式
  • -a : 如果檢查有錯則自動修複
  • -r : 如果檢查有錯則由使用者回答是否修複

案例:

檢查 ​

​msdos​

​​ 檔案系統的​

​/dev/hda5​

​是否正常,如果有異常便自動修複 :

fsck -t msdos -a /dev/hda5      

fsck 指令存在的問題

然而這個指令看似很強大,但是存在一些很嚴重的性能問題:

  • 周遊檔案系統并且檢查檔案系統的一緻性同時還會修複不一緻的地方,檔案系統非常龐大恢複速度會長達幾個小時或者幾天。
  • 如果中途恢複失敗,崩潰的不隻是機器。
  • 修複成功不一定會恢複到期望狀态,同時對于一切不一緻的資料和中繼資料都會删除。

計算機存儲層次簡析

存儲元件介紹

首先我們來看看不同存儲層次的介紹,包括上面提到的寄存器,高速緩存,記憶體以及他們三者之間的關系。

我們從整體上看一下存儲層次結構圖:

《Linux是怎麼樣工作的》讀書筆記
注意⚠️:小字這些參數放到現在都是比較老的了,我們隻需要簡單了解從左到右速度由最快到快到慢到最慢的遞進。

高速緩存

高速緩存是位于CPU與主記憶體間的一種容量較小但速度很高的存儲器。

記憶體的資料被讀取之後,資料不會直接進入寄存器而是先在高速緩存進行存儲,高速緩存通常分為三層,讀取的大小取決于緩存塊的大小,讀取速度取決于不同層級高速緩存的容量。

高速緩存的執行步驟如下:

  1. 根據指令把資料讀取到寄存器。
  2. 寄存器進行計算操作。
  3. 把運算結果傳輸給記憶體。

在上面的三個步驟中寄存器和高速緩存基本沒有傳輸消耗,但是記憶體的傳輸就就慢不少了,是以整個流程運算的瓶頸也是記憶體的傳輸速度,這也是為什麼使用高速緩存來解決寄存器和記憶體之間的巨大差異。

高速緩存分為L1,L2,L3,在講述理論知識之前,這裡先舉一個形象一點的例子友善了解:

  • L1 cache:就好像需要工具在我們的腰帶上可以随時取用,是以要擷取它的步驟最簡單也最快
  • L2 cache:就好像需要的工具放到工具箱裡面,我們如果需要擷取要先打開工具箱然後把工具箱的工具挂到腰上才能使用。為什麼不能從工具箱取出來再放回去呢?其實思考一下如果你需要頻繁使用那得多累呀。另外工具箱雖然比腰上的空間大不少,但是也沒有大特别多,是以L2 cache 沒有比L1 cache大多少。
  • L3 cache:L3相比L1和L2要大非常多,相當于一個倉庫,我們擷取資料需要自己走到倉庫去找工具箱然後放到身邊,然後再像是上面那樣執行一次,雖然倉庫容積很大,但是需要操作的步驟最多,時間開銷也最大。

L1 cache下面是L2 cache,L2下面是 L3 cache,根據上面介紹L2和L3都有跟L1 cache一樣的問題,要加鎖,同步,并且L2比L1慢,L3比L2慢。

這裡我們再舉例簡述高速緩存的内部操作:

假設需要讀取緩存塊是10個位元組,高速緩存為50個位元組,R0、R1的寄存器總計為20個位元組,當R1需要讀取某個位址的資料時,在第一次讀取資料的時候将10位元組先加載到高速緩存,然後再由高速緩存傳輸到寄存器,此時R0有10位元組的資料,如果下次還需要讀取10個位元組,同樣因為高速緩存發現緩存中有相同資料,則直接從高速緩存讀取10個位元組到R1中。

那麼如果此時R0資料被改寫會怎麼辦?首先CPU會先改寫寄存器的值,改寫寄存器值之後會同時改寫高速緩存的值,此時如果存在從記憶體進來緩存塊資料,在高速緩存中會先标記這些值,然後高速緩存會在某一個時刻把改寫的資料同步到記憶體中。

如果高速緩存不足系統會發生什麼情況?

首先高速緩存會根據緩存淘汰機制淘汰末端最少使用的高速緩存,但是如果高速緩存的“變髒”速度很快并且高速緩存的容量總是不足,此時就會發生記憶體頻繁寫入高速緩存并且不斷變動高速緩存的情況,此時就有可能會出現可感覺的系統抖動。

注意⚠️:本部分讨論的内容全部為回寫,改寫的方式分為直寫和回寫,回寫在高速緩存中存在一定的延遲,利用時間積累的方式定時改寫的方式進行記憶體的同步重新整理,而直寫的方式則會在高速緩存改變的那一刻立刻改寫記憶體的值。

如何衡量通路的局限性呢?

幾乎所有的程式都可以分為下面兩種情況:

  • 時間局限性:在一定的時間内緩存可能被通路一次,但是可以隔一小段時間再一次通路,常見的情況是一個循環中不斷取值。
  • 空間局限性:通路一段資料的同時還需要通路它周邊的資料情況,有點類似磁盤的預讀機制。

如果程序可以衡量并且把控好上面兩個點,那麼基本可以認為是一款優秀的程式,但是現實情況往往不是如此。

小結:

  • 高速緩存是利遠遠大于弊的一個設計。
  • 資料不一緻和資料同步高速緩存性能影響的主要問題。
  • 高速緩存一旦被占滿,則系統處理速度會出現一定延遲。

寄存器

寄存器包括指令寄存器(IR)和程式計數器(PC),它屬于中央處理器的組成部分,寄存器中包含指令寄存器和程式計數器以及累加器(數學運算)。

ARM走的是簡單指令集,X86走複雜指令集,雖然X86從現在來看是走到了盡頭,但是依然占據市場主導地位。

複雜指令集會包含非常多的寄存器完成複雜運算,比如下面一些寄存器:

  • 通用寄存器
  • 标志寄存器
  • 指令寄存器

如果有感興趣可以将寄存器作為深入X86架構的入口。

記憶體

記憶體不僅僅是我們熟知的電腦記憶體,從廣義上來說還包括隻讀存儲,随機存儲和高速緩存存儲。

這裡可能會有疑問為什麼記憶體使用的最多卻不如寄存器和高速緩存呢?

因為記憶體不僅僅需要和CPU通信還需要和其他的控制器和硬體打交道,管的事情越多效率自然越低,同時如果記憶體吃緊CPU還需要等待記憶體傳輸,當然這也可以反過來解釋為什麼需要高速緩存和寄存器。

除了上面提到的原因之外,還有比較關鍵的原因是主機闆的總線帶寬是有上限的并且需要共享給各路使用,比如南橋和其他的一些外接裝置等等,同時總線也是需要搶占的,并不是分片使用。

其他補充

轉譯後備緩沖區

下面的内容來自百科的解釋:

轉譯後備緩沖器(英語:Translation Lookaside Buffer,​​首字母縮略字​​:TLB),通常也被稱為頁表緩存、轉址旁路緩存,為​​CPU​​​的一種緩存,由​​記憶體管理單元​​​用于改進​​虛拟位址​​到實體位址的轉譯速度。

目前所有的桌面型及伺服器型處理器(如 ​​x86​​​)皆使用TLB。TLB具有固定數目的空間槽,用于存放将虛拟位址映射至​​實體位址​​​的​​标簽頁表​​​條目。為典型的​​結合存儲​​​(content-addressable memory,​​首字母縮略字​​:CAM)。

其搜尋關鍵字為虛拟記憶體位址,其搜尋結果為實體位址。如果請求的虛拟位址在TLB中存在,CAM 将給出一個非常快速的比對結果,之後就可以使用得到的實體位址通路存儲器。如果請求的虛拟位址不在 TLB 中,就會使用​​标簽頁表​​​進行虛實位址轉換,而​​标簽頁表​​的通路速度比TLB慢很多。

有些系統允許​​标簽頁表​​被交換到次級存儲器,那麼虛實位址轉換可能要花非常長的時間。

程序如果想要通路特殊的資料,可以通過下面提到的方式通路邏輯位址:

  • 對照實體頁表通過查表的方式把虛拟位址轉實體位址。
  • 通過通路對應的實體位址尋找實際的實體位址。
注意⚠️:這裡的操作類似二級指針的通路操作,如果想要高速緩存發揮作用必須是一級指針的查找才有意義,但是二級指針的查找是沒有太大意義的。

轉譯後備緩沖器說白了就是用于加速虛拟位址到實體位址轉化的一塊特殊空間,目的是為了提高多級嵌套映射查找的速度。

頁面緩存

注意上面提到的内容是頁表緩存,這裡是頁面緩存。

頁面緩存的作用是什麼呢?我們都知道外部的硬體存儲速度是最為緩慢的,通常應用程式操作硬碟中的資料都是預先把資料加載到記憶體再進行操作,然而資料并不是直接從磁盤拷貝到記憶體的,而是在記憶體和外部儲存設備之間多了一層頁面緩存。

頁面緩存的讀取步驟如下:

  • 程序讀取磁盤文本資料,尋找到相關資料之後将内容加載到頁面緩存。
  • 把頁面緩存的内容複制到記憶體中,此時實體資料和記憶體以及頁面緩存資料一緻。
  • 如果需要改寫檔案文本資料,首先會通知頁面緩存标記自己為“髒頁”。
  • 如果記憶體不足則空出空閑的頁面緩存給記憶體使用。
  • 如果頁面緩存和記憶體都不足就需要重新整理“髒頁”空出空間給記憶體繼續使用。
  • 通常情況下頁面緩存會定期重新整理緩存回寫到磁盤中保持資料同步。

另外需要注意如果頁面緩存一直沒有程序通路,頁面緩存會一直“膨脹”,如果頁面緩存和記憶體一直不夠用,就會不斷的回寫髒頁并且産生性能抖動問題。

緩沖區緩存

緩沖區緩存很容易和頁面緩存搞混,我們隻需要簡單了解是對原始磁盤塊的臨時存儲,也就是用來緩存磁盤的資料,比如裝置檔案直連外部的儲存設備,U盤讀寫和外接磁盤的讀寫等等,這些讀寫通過緩存區緩存進行管理。

需要注意緩沖區緩存通常不會特别大(20MB 左右),這樣核心就可以把分散的寫集中起來,統一優化磁盤的寫入, 比如可以把多次小的寫合并成單次大的寫等等。

Linux中調優參數

了解上面各個元件的内容和細節之後,我們來看幾個簡單的Linux調優參數。

回寫周期

回寫周期可以通過sysctl 的​

​vm.dirty_writeback_centisecs​

​ 參數調整,但是注意這個值的機關比較特殊,厘秒,這個參數預設設定為500,也就是5秒進行一次回寫。

厘秒(英文:centisecond,符号cs),1厘秒 = 100分之1​​秒​​。

當然除非為了實驗了解否則不要把這個值設定為0。

除了這個參數之外,還有個百分比的參數,當髒頁的數量超過百分比之後就會觸發髒頁回寫的操作防止性能劇烈抖動,下面案例的10代表了10%。

下面是這個參數的内容:

vm.dirty_backgroud_ratio = 10      

如果想要使用位元組的形式控制這個門檻值,可以通過參數​

​vm.dirty_background_bytes​

​指定,如果這個參數為0則代表不開啟這個配置。

髒頁不允許一直存在,如果髒頁積攢到一定的量的時候會核心會觸發回寫操作,可以通過​

​vm.dirty_ratio​

​ 控制,當到達此百分比核心會阻塞使用者程序并且把所有的髒頁回寫。

​vm.dirty_ratio​

​​除了設定百分比參數也可以通過位元組限制,參數是​

​vm.dirty_bytes​

​。

除了這些不太常用的參數之外,還有一些更為特殊的調優參數, 比如配置用于清空所有的頁面緩存的操作方法是向​

​/proc/sys/vm/drop_caches​

​ 寫入3,為什麼是寫入3,這裡留給讀者自己尋找答案。

超線程

超線程(HT, Hyper-Threading)是英特爾研發的一種技術,于2002年釋出。超線程的技術可以把一個核心僞裝成兩個核心看待,同時對于單核心的CPU,也可以享受模拟雙核心的優惠,當然超線程技術不隻是有好處,還有一個明顯的缺點是多線程搶占以及線程上下文帶來的開銷,同時哪怕在最理想的情況下超線程的技術最多也隻能提升 20% -30%的,但是這個優化對于當年技術實力有限的情況下的技術優化和性能提升效果是非常顯著的。

從此牙膏廠走向了擠牙膏的不歸路

小結

這部分内容更像是對于家用電腦常見的幾個核心組建進行稍加深入的介紹,學習這些内容不僅對于計算機的深入了解是必要的,對于我們日常選配電腦一些商家說明也能有更深了解。

在其他的補充部分介紹了三個緩存,分别是轉譯後備緩沖,頁面緩沖,緩沖區緩沖,這三者雖然名字相近但是内部負責的工作差别還是比較大,介紹三者之後介紹了一些關于Linux的調優參數。

對于深入X86架構來說,了解各個寄存器的核心工作機制比較關鍵,而對于戰未來的ARM使用的精簡指令集對于整個生态發展更為合适。

Linux記憶體管理

簡單介紹

下面我們就來簡單介紹Linux記憶體管理的,在Linux中記憶體管理可以大緻了解為三個部分:

  • 核心使用的記憶體
  • 程序使用的記憶體
  • 可用記憶體(空閑記憶體)

其中除開核心使用的記憶體維持系統正常運作不能被釋放之外,其他均可以由作業系統自由支配。在Linux中擁有​

​free​

​指令來專門檢視記憶體的使用情況,執行的效果類似如下:

/opt/app/tdev1$free
             total       used       free     shared    buffers     cached
Mem:       8175320    6159248    2016072          0     310208    5243680
-/+ buffers/cache:     605360    7569960
Swap:      6881272      16196    6865076      

各個列的含義如下:

  • total:系統搭載實體記憶體總量,比如上面為8G。
  • free:表面可用記憶體。
  • buff/cache:緩沖區緩存和頁面緩存,在​​計算機存儲層次簡析​​中提到了當記憶體不夠可以使用釋放緩存騰出空間給記憶體使用。
  • availiable:實際可以使用的記憶體,計算公式很簡單即​

    ​核心之外的可用總記憶體 - (free + buff/cache 最大可以釋放的記憶體)​

    ​。

除了列資料之外還有一個​

​swap​

​的行,這個參數的含義将在後文進行介紹。

Linux除了​

​free​

​​指令之外,還有​

​sar -r​

​​指令,可以通過這個參數指定采集周期,比如​

​-r 1​

​就是1秒采集一次。

個人目前使用的電腦為Mac,雖然是類Unix系統但是沒有​

​free​

​相關的指令,為此可以使用下面的指令進行簡單的替代,但是不如free強大。

在Mac中使用​

​top -l 1 | head -n 10​

​檢視整體系統運作情況。

MacBook-Pro ~ % top -l 1 | head -n 10

Processes: 604 total, 2 running, 602 sleeping, 3387 threads 

2022/04/15 17:29:57

Load Avg: 2.84, 3.27, 5.68 

CPU usage: 6.8% user, 14.18% sys, 79.72% idle 

SharedLibs: 491M resident, 96M data, 48M linkedit.

MemRegions: 168374 total, 5515M resident, 235M private, 2390M shared.

PhysMem: 15G used (1852M wired), 246M unused.

VM: 221T vsize, 3823M framework vsize, 0(0) swapins, 0(0) swapouts.

Networks: packets: 312659/297M in, 230345/153M out.

Disks: 788193/14G read, 161767/3167M written.      

除此之外,可以在Mac中使用使用​

​diskutil list​

​:

```shell
~ > diskutil list

/dev/disk0 (internal):

   #:                       TYPE NAME                    SIZE       IDENTIFIER

   0:      GUID_partition_scheme                         500.3 GB   disk0

   1:             Apple_APFS_ISC ⁨⁩                        524.3 MB   disk0s1

   2:                 Apple_APFS ⁨Container disk3⁩         494.4 GB   disk0s2

   3:        Apple_APFS_Recovery ⁨⁩                        5.4 GB     disk0s3



/dev/disk3 (synthesized):

   #:                       TYPE NAME                    SIZE       IDENTIFIER

   0:      APFS Container Scheme -                      +494.4 GB   disk3

                                 Physical Store disk0s2

   1:                APFS Volume ⁨mysystem⁩                15.2 GB    disk3s1

   2:              APFS Snapshot ⁨com.apple.os.update-...⁩ 15.2 GB    disk3s1s1

   3:                APFS Volume ⁨Preboot⁩                 529.6 MB   disk3s2

   4:                APFS Volume ⁨Recovery⁩                798.6 MB   disk3s3

   5:                APFS Volume ⁨Data⁩                    455.3 GB   disk3s5

   6:                APFS Volume ⁨VM⁩                      24.6 KB    disk3s6



/dev/disk6 (external, physical):

   #:                       TYPE NAME                    SIZE       IDENTIFIER

   0:      GUID_partition_scheme                        *512.1 GB   disk6

   1:       Microsoft Basic Data ⁨⁩                        512.1 GB   disk6s1



/dev/disk7 (external, physical):

   #:                       TYPE NAME                    SIZE       IDENTIFIER

   0:      GUID_partition_scheme                        *1.0 TB     disk7

   1:       Microsoft Basic Data ⁨Extreme SSD⁩             1.0 TB     disk7s1      

下面是​

​free​

​​和​

​sar​

​這兩個指令的輸出結果對應關系:

  • total : 無對應
  • free :kbememfree
  • buff/cache :kbbufferrs + kbcached
  • available:無對應

如果記憶體使用過多,系統為了空出記憶體可能出現強制 ​

​kill​

​ 某個程序的操作,此操作是随機的并且無法被監控,商用機器上執行這種操作是十分危險的,是以有部分的商用機器會開啟一旦OOM直接把整個系統強制關閉的操作。

記憶體配置設定方式及問題

核心配置設定記憶體的時機大緻有下面兩種:

  1. 建立程序。
  2. 建立程序之後進行動态記憶體配置設定。

在程序建立之後如果程序還需要核心提供更多的記憶體,則可以向核心發出記憶體的請求申請,核心收到指令之後,則劃分可用記憶體并且把起始結束的位址給程序進行使用。

但是這種要一點給一點的方式有下面幾個常見的問題:

  • 難以執行多個任務。
  • 通路其他用途的記憶體區域。
  • 記憶體的碎片化。
注意⚠️:記憶體不僅僅需要和CPU通信還需要和其他的控制器和硬體打交道,配置設定記憶體給程序隻是諸多任務的項目之一。

難以執行多任務

可以了解為程序頻繁的需要申請記憶體的情況,這時候核心需要不斷的操作配置設定記憶體給程序,整個任務相當于被單個程序給拖累了。

另外如果多個任務出現配置設定記憶體的區域剛好相同,此時需要要完成記憶體配置設定給那個程序任務,則另一個程序等待也是可以了解的。

記憶體碎片化

原因是程序每次擷取記憶體都需要了解這部分内容要涵蓋多少區域否則就不能擷取這些記憶體。

記憶體碎片化的另一個重大問題是明明有很多富裕的記憶體但是卻拿不出一塊完整連續的空間給程序使用,導緻不斷的回收和配置設定操作。

通路其他用途的記憶體區域

這種情況程序通路被叫做缺頁通路中斷,在後續的内容會進行介紹。

虛拟位址和實體位址

為了解決上面的問題,作業系統使用了虛拟内容和實體記憶體的方式進行記憶體管理。

我們需要了解三個概念:位址空間、虛拟位址、實體位址。

位址空間:指的是可以通過位址通路的範圍都統稱為位址空間。

虛拟位址:虛拟位址指的是程序無法直接通路到真實的實體記憶體位址,而是通路和實際記憶體位址映射的虛拟記憶體位址,目的是為了保護系統硬體安全。

實體位址:也就是我們實際記憶體對應的實際的實體位址。

這裡舉一個簡單的例子:如果核心給程序配置設定100位址的虛拟記憶體位址,那麼這個虛拟記憶體位址實上可能會指向實際的600實體位址。

頁表

完成虛拟位址到實體位址的映射依靠的是頁表,在虛拟記憶體當中所有的記憶體都被劃分為頁,一個頁對應的資料條目叫做頁表項,頁表項記錄實體位址到虛拟位址的映射關系。

在x86-64的架構中一個頁的大小為4KB,程序在記憶體是有固定的起止位址的,那麼如果出現超出位址的頁通路,也就是通路了沒有虛拟位址和實體位址映射的空間會出現什麼情況呢?

如果出現越界通路,那麼此時CPU會出現缺頁中斷,并且終止在缺頁中進行操作的程序指令,同時啟動核心的中斷處理機構處理。

注意⚠️:對應通路其他用途的記憶體區域這個問題。

虛拟記憶體配置設定操作

虛拟記憶體的配置設定操作步驟我們可以了解為幾個核心的步驟:

  • 核心尋找實體位址并且把需要的實體位址空間計算。
  • 建立程序的頁表把實體位址映射到虛拟位址。
  • 如果程序需要動态記憶體管理,核心會配置設定新頁表以及新的可用記憶體給程序使用,當然同時提供對應的實體記憶體空間。
實體分頁使用的是請求分頁的方式進行處理,這個配置設定的操作十分複雜。

記憶體的上層配置設定

在C語言中配置設定記憶體的函數是​

​malloc​

​​函數,而Linux作業系統中用于配置設定記憶體的函數是​

​mmap​

​​函數,這兩者最大差別是​

​mmap​

​函數使用的是按頁的方式配置設定,而​

​malloc​

​是按照位元組的方式配置設定。

​glibc​

​​通過系統調用​

​mmap​

​​申請大量的記憶體空間作為記憶體池,程式則調用​

​malloc​

​記憶體池請求配置設定出具體的記憶體供程序使用,如果程序需要再次擷取記憶體則需要再次通過mmap擷取記憶體并且再次進行配置設定操作。

在上層程式設計語言也是使用了類似的操作,首先通過​

​glibc​

​​向核心申請記憶體執行虛拟記憶體的配置設定操作,然後​

​malloc​

​函數再去請求劃分具體的記憶體使用,隻不過更上層的語言使用了解析器和腳本進行掩蓋而已,實際上通過層層翻譯最終的操作依然是上面提到的操作。

虛拟記憶體是如何解決簡單配置設定的問題的?

這裡我們再次把上面三個問題搬出來,再解釋虛拟記憶體是如何處理問題的:

  • 難以執行多個任務:每個程序有獨立的虛拟位址空間,是以可以編寫專用位址空間程式防止多個任務阻塞等待的情況。
  • 通路其他用途的記憶體區域:虛拟位址空間是程序獨有,頁表也是程序獨有。頁表的另一個作用是限制可以防止目前的程序通路到其他線程的頁表和位址空間。
  • 記憶體的碎片化:記憶體碎片化使用頁表的方式進行配置設定,因為頁表記錄了實體位址到虛拟位址的映射,這樣就可以很好的知道未使用的空間都幹了啥。

虛拟記憶體的其他作用:

  • 檔案映射
  • 請求分頁
  • 利用寫時複制的方式快速建立程序
  • 多級頁表
  • 标準大頁

小結

這一部分簡要闡述Linux記憶體管理的入門了解部分,這一部分主要介紹了簡要的記憶體配置設定方式,以及Linux對此通過頁表的方式實作實體位址和虛拟位址的配置設定,最後闡述了作業系統和程式設計語言也就是程序之間是如何配置設定記憶體的,具體的配置設定步驟和互動邏輯介紹。

Linux記憶體管理優化

檔案映射

經過之前的内容我們了解到檔案映射通過映射虛拟記憶體的方式實作,程序通路記憶體對時候實際是檔案對應的副本虛拟記憶體位址,既然通路虛拟記憶體位置可以完成檔案的修改映射,那麼直接通路實體記憶體也就是實際記憶體修改内容也是可行的。

如果知道檔案的具體位址,甚至可以直接定位到記憶體位址對于内容進行覆寫,在書中有一個C語言寫的驗證程式比較有意思。

請求分頁

程序向核心申請記憶體的通過請求分頁的方式完成,之前提到過通過​

​mmap​

​的方式申請記憶體的方式雖然很友善但是是有問題的:

通常的記憶體配置設定方式有下面兩種:

  • 實體記憶體的直接申請和配置設定,高效。
  • 句柄配置設定的方式,也就是頁表對于虛拟記憶體和實際記憶體映射之後再給程序。

這兩種配置設定方式都存在兩個比較明顯的問題,那就是配置設定的時候如果申請了卻沒有使用會大量浪費,另外一次glibc通路需要超過程序的記憶體,但是程序此時很可能不會使用甚至可能根本不使用,此時很可能出現很大的程序管理大量被申請未使用記憶體。

請求分頁就是用來解決上面提到的問題的。

請求分頁理念

為了更好了解請求分頁需要先了解分頁的三種狀态:

  • 未配置設定頁表和實體記憶體給程序。
  • 已配置設定頁表但是未配置設定實體記憶體。
  • 已配置設定頁表和實體記憶體。

為了解決配置設定浪費的問題,配置設定程序的記憶體僅使用一次配置設定方式,請求分頁的核心是利用核心缺頁中斷的機制,當程序初次通路到已配置設定但是沒有沒有配置設定實體記憶體的空間,對于此時核心會進行缺頁中斷處理,同時給程序真正申請實體記憶體進行配置設定動作,這樣可以保證每次配置設定記憶體的動作都是有效的。

這種方式也類似懶加載的方式,即可以保證配置設定動作運作,程序無感覺缺頁中斷的情況,依然可以正常運作。

如果使用C語言按照請求分頁的特點進行實驗,可以發現當記憶體沒有使用的時候即使顯示已經配置設定記憶體,但是實際可用實體記憶體沒有變動。

另外配置設定記憶體失敗分為虛拟記憶體配置設定失敗,實體記憶體配置設定失敗,這是因為“懶加載”的設計導緻的,另外虛拟記憶體不足不一定會導緻實體記憶體不足,因為隻要可用實體在配置設定空間小于虛拟記憶體那就是沒法配置設定并且會配置設定失敗。

寫時複制

寫時複制是利用​

​fork​

​的函數提高虛拟記憶體配置設定效率。

在檔案系統的展現是​

​update​

​​或者​

​delete​

​操作不會動原資料,而是用副本完成操作,當操作完成再更新引用,如果中間當機斷電,則用日志恢複狀态即可。

Linux 系統的記憶體管理中調用 ​

​fork​

​​ 系統調用建立子程序時,并不會把父程序所有占用的記憶體頁複制一份,而是首先與父程序共用相同的頁表,而當子程序或者父程序對記憶體頁進行修改時才會進行複制 —— 這就是著名的 ​

​寫時複制​

​ 機制。

寫時複制的流程如下:

  • 在父程序調用​

    ​fork​

    ​的時候,并不是把所有記憶體複制給子程序而是遞交自己的頁表給子程序,如果子父程序隻進行隻讀操作雙方都會共享頁表,但是一旦一方要改變資料,就會立馬解除共享。
  • 在解除共享的時候會有如下操作
  • 由于寫入失去權限,是以會出發缺頁中斷,此時會切斷使用者态,儲存目前程序狀态并且進行核心态。
  • 切換至核心态,核心幹預,執行缺頁中斷。
  • 寫入方的資料複制到另一處,并且把寫入方的頁表全部更新為新複制的記憶體并且賦予寫入方寫入權限,同時把之前共享的頁表更新,并且把另一方重新整理之後的頁表重新連接配接即可。(關鍵)
  • 最後父子程序徹底寫入權限和頁表獨立。但是之前解除共享的頁表依然可以自由讀寫。
注意⚠️:注意解除共享并不是所有的共享都解除,而是解除共享需要獨立的部分,這種處理是。

另外還需要強調進行​

​fork​

​調用的時候并不會複制頁表和内容,而是真正寫入的時候會觸發複制動作,這也是寫時複制名字由來。

寫時複制中如果隻進行隻讀操作雙方都會共享頁表,但是一旦一方要改變資料,就會立馬解除共享。

交換記憶體

交換記憶體是Linux核心一種OOM情況下的補償機制,作用也是為了緩解記憶體溢出和不足的問題,交換記憶體的實作依靠的是虛拟記憶體的機制。

簡單了解就是在實體記憶體雖然不夠但是虛拟記憶體可以借用外部存儲器也就是硬碟的一部分空間來充當實體記憶體使用,這一塊分區叫做交換區,由于是借實體存儲空間,這個操作也叫做換出。

另外如果借用的空間被釋放則會立即歸還,這種操作叫做換入,由于交換記憶體以頁為機關,部分資料也叫頁面調入和調出都是一個意思。

交換記憶體很容易認為是擴充實體記憶體的美好方式,但是這裡有一個本質的問題,那就是硬碟的通路速度和記憶體相比差的次方級别的差距,另外如果長期記憶體不足很容易導緻交換記憶體不斷的換入換出出現明顯的性能抖動。

硬性頁缺失和軟性頁缺失

另外這類需要外部存儲器的缺頁中斷在術語中被稱之為硬性頁缺失,相對的不需要外部存儲器的頁缺失是軟性頁缺失,雖然本質都是核心在觸發和完整操作,但是硬性的缺失總歸比軟性缺失後果嚴重很多。

這裡也要吐槽一下M1的各種偷硬碟緩存來提高性能的操作.....這也是為什麼要把硬碟和CPU內建的原因之一。

多級頁表

多級頁表的設計核心是:避免把全部頁表一直儲存在記憶體中是多級頁表的關鍵所在,特别是那些不需要的頁表就不應該保留。

在X86-64架構當中,虛拟位址的空間大小約為128T,一個頁的大小為4KB,頁表的項目大小為8個位元組。

是以一個程序的頁表至少需要256GB記憶體!(8 * 128T / 4KB),但是我們都知道現在的電腦一般都是16GB記憶體為主。

那麼系統應該如何維護頁表?這就引入了多層頁表來進行管理,多級頁表可以從最簡單的角度當作一個多級的指針看待。

首先我們可以思考,一個程序是否需要整個頁表來管理記憶體?很顯然是不需要的,這是引入多級頁表的理由之一。

假設一個程序需要12M空間,頂端需要4M,資料部分占用4M,底部又是一些堆棧内容和記錄資訊,在資料頂端上方和堆棧之間是大量根本沒有使用的空閑區。

多級頁表實際上就是大目錄套小目錄,和我們的一本書一樣,小目錄負責小的程序,而遇到比較大的程序就放到空閑頁比較大的目錄中完成配置設定操作,多級頁表既可以高效的利用記憶體的同時,可以最大限度的減少頁表本身的資料結構在記憶體的占用,同時上面的例子也可以發現絕大多數的程序其實根本不需要太大的頁表進行維護和管理。

最後從網上的資料翻閱中發現一張下面的圖,對于多級頁表的了解有一定幫助:

《Linux是怎麼樣工作的》讀書筆記

最後X86_64 使用了4層的頁表結構,直當了解就是四級指針。

标準大頁

随着程序虛拟記憶體和頁表的使用,程序使用的實體記憶體也會增加。

根據請求分頁和寫時複制的概念,調用​

​fork​

​函數的時候會對于父子程序共享的頁表進行拷貝,雖然這個拷貝動作不會占用實體記憶體,但是需要拷貝一份完整的頁表,當頁表很大的時候也會造成性能浪費。

為了解決這個問題,Linux提供了标準大頁的機制,和他名字一樣就是比普通的頁表更大的頁,利用這種頁表可以減小程序頁表所需的記憶體使用量,通過多級頁表和标準大表可以有效的減少整個頁表項的數量。

用法:

C語言中使用​

​mmap​

​​函數參數賦予 ​

​MAP_HUGETLB​

​ 标志,表示可以擷取大頁,但是更加常用的方式是使用程式允許使用使用标準大頁而不是這種手動切換的方式。

标準大頁對于虛拟機和資料庫等需要使用大量記憶體的應用程式是很有必要的,根據實際情況決定是否使用标準大頁,通過這種設定可以減少這一類軟體記憶體占用,還能提高​

​fork​

​效率。

透明大頁

透明大頁是随着标準大頁帶來的附帶特性,主要的作用是在連續的4KB頁面如果符合指定條件就可以通過透明大頁的機制轉為一個标準大頁,以及在不滿足标準大頁條件的時候拆分為多個4KB頁面。

小結

這部分從檔案映射的内容引申了Linux兩個重要的機制:請求分頁和寫時複制,目的本質上都是盡量減少程序對于記憶體的浪費,但是需要注意的是這兩種方式都是使用了核心模式的系統中斷機制來進行處理的,是以對于核心的性能以及穩定性要求非常高。

在之後的内容介紹了交換記憶體以及多級頁表和标準大頁幾個内容,其中多級頁表内部的細節非常的複雜,通常需要對于作業系統底層有比較熟悉的認知才能完全的了解這個頁表的細節。

程序排程器

針對Linux程序排程有下面的思考:

  • 每一個CPU同一時間隻能排程一個程序。
  • 每一個程序有*近乎相等的執行時間。
  • 對于邏輯CPU而言程序排程使用輪詢的方式執行,當輪詢完成則回到第一個程序反複。
  • 程序數量消耗時間和程序量成正比。

對于程序排程來說不能保證一個程式是連續完成的,由于CPU排程和程序切換,上下文也會出現切換情況。

程序狀态

對于大部分程序來說,當我們不使用的時候多數處于睡眠狀态。

除了睡眠狀态之外,程序還有下面幾種狀态:

  • 運作狀态:獲得CPU排程,執行程序任務。
  • 僵死狀态:程序等待結束,等待父程序回收。
  • 就緒狀态:具備運作條件,等待CPU配置設定。
  • 睡眠狀态:程序不準備運作除非某種條件觸發才會獲得CPU排程配置設定。

處在睡眠态的程序觸發運作态的條件如下:

  • 外部存儲器通路。
  • 使用者鍵入或者滑鼠操作觸發事件。
  • 等待指定時間。
  • 等待指定時間。

通過​

​ps ax​

​指令可以檢視目前的程序狀态,下面的案例以個人的Mac電腦為例:

MacBook-Pro ~ % ps ax

  PID   TT  STAT      TIME COMMAND
32615   ??  Ss     0:00.11 /usr/libexec/nearbyd

32632   ??  Ss     0:00.51 /System/Library/CoreServices/Screen Time.app/Content

32634   ??  Ss     0:00.02 /System/Library/PrivateFrameworks/Categories.framewo

32635   ??  S      0:00.12 /System/Library/CoreServices/iconservicesagent

32636   ??  Ss     0:00.05 /System/Library/CoreServices/iconservicesd

32671   ??  S      0:02.44 /Applications/Microsoft Edge.app/Contents/Frameworks

32673   ??  S      0:02.86 /Applications/Microsoft Edge.app/Contents/Frameworks

32678   ??  Ss     0:00.17 /System/Library/PrivateFrameworks/UIFoundation.frame

32726   ??  S      0:00.07 /System/Library/Frameworks/CoreServices.framework/Fr

32736   ??  S      0:00.08 /System/Library/Frameworks/CoreServices.framework/Fr

32738   ??  S      0:00.75 /System/Applications/Utilities/Terminal.app/Contents

32739   ??  Ss     0:00.02 /System/Library/PrivateFrameworks/Categories.framewo

32746   ??  Ss     0:00.03 /System/Library/Frameworks/Metal.framework/Versions/

32740 s000  Ss     0:00.02 login -pf xxxxxx

32741 s000  S      0:00.03 -zsh

32750 s000  R+     0:00.01 ps ax      

​s​

​​表示​

​sleep​

​​,​

​d​

​​表示此時可能在等待磁盤IO,但是如果長時間處于​

​d​

​狀态則可能是磁盤IO等待逾時或者核心可能發生故障。

下面是根據上面的介紹繪制的程序狀态流轉圖

《Linux是怎麼樣工作的》讀書筆記

如果隻執行一個程序同時在程序中間休眠過一次,那麼此時休眠的程序在幹什麼?

程序會進入空程序的模式輪詢,但是空程序不是沒有事做,而是需要調用一些維持系統運作的線程,為了保證系統正常穩定運作。

因為CPU和空閑程序,是以同樣會不斷的切換睡眠态和運動态,運動态擷取使用者輸入操作完成動作,睡眠态則執行一些輕量操作。

針對睡眠态的程序會有如下特點:

  • 遵循同一時間CPU隻能完成一個程序操作
  • 睡眠态不占用CPU時間。

吞吐量和延遲

  • 吞吐量:處理完成的程序數量 / 耗費時間
  • 延遲:結束處理時間 - 開始處理時間

通過這兩點可以總結幾點規則:

吞吐量的上限是程序的數量多過邏輯CPU的數量,則再增加程序無法增加吞吐量,另外程序中的延遲總是平均的,也就是說多個程序執行會獲得近似平均的延遲,最後程序越多延遲越高。

但是現實系統沒有那麼多理想情況,多數情況是下面幾種:

  • 空閑程序:此時吞吐量很低,因為很多邏輯CPU都在睡眠狀态。
  • 程序運作态:此時沒有就緒:這種狀态比較理想,CPU可以安排到下一次處理,雖然會延後開始執行提高吞吐量,但是可能會出現CPU空閑的情況。
  • 程序運作态,同時就緒:此時就好像賽跑,但是隻有一個跑道,每一個程序輪流處理一會兒,是以此時延遲變長。

另外由于很多程式編寫都是單線程程式,一核運作,多核圍觀或許在過去更普遍。

優化吞吐量和延遲的方式是使用 ​

​sar​

​​ 指令找到運作時間和開銷最大程序,同時把一些死程序​

​kill​

​掉。

多CPU排程情況

分片時間每一個程序用一個CPU工作,那麼配置設定和排程CPU安排工作又是如何的?

主要有兩種方式,第一種是通過輪詢負載均衡,另一種是全局配置設定,把任務配置設定給空閑程序的邏輯CPU。

負載均衡是CPU遇到程序任務依次安排工作,當最後一個CPU安排完成之後,則再回到第一個CPU進行配置設定,同時都是對于程序執行一定的時間,也就是說出現CPU-A處理一部分,另一部分可能是CPU-B完成。

全局配置設定的方式比較簡單,就是把任務配置設定給處于空閑程序的邏輯CPU完成工作。

檢視系統邏輯CPU的指令如下:

​grep -c processor /proc/cpuinfo​

多核cpu通常隻有在同時運作多個程序的時候才會發揮作用,但是并不是說有多少核心就有多少倍性能,因為大部分時候程序很少很多CPU都在睡眠态度

如果程序超過邏輯CPU數量,無論怎麼增加程序都不會提高處理速度

最後處于睡眠狀态的程序其實可以指定睡眠時間,通過sleep函數調用完成程序休眠的操作。

小結

程序排程器的内容遠沒有上面介紹的簡單,但是作為了解程序的切入點是不錯的。

計算機程式概覽

放到最後是因為個人認為算是這本書相對沒有什麼價值的部分。

概覽

狹義上的計算機結構是:CPU,記憶體,外部裝置,其中外部裝置包含輸入輸出和外部外存儲器以及網絡擴充卡。

而從廣義上來看,計算機可以用抽象化的概念進行解釋,則可以簡單講計算機分為三部分:

  • 第一部分是應用程式,這些程式依托于環境和載體。
  • 第二部分就是運作程式的載體,負責管理系統調用,程序切換和裝置驅動這些工作,同時擔任重要的硬體抽象的角色,為應用程式掩蓋掉複雜的底層維護工作。
  • 最後一部分是硬體,這部分就是我們狹義的了解計算機的部分,這裡不多介紹。

在硬體裝置重複執行以下步驟:

  • 輸入裝置或者網絡擴充卡發起請求
  • 讀取記憶體指令,CPU上執行,把結果寫入負責儲存到記憶體區域
  • 記憶體的資料寫入HDD、SDD等存儲器

而程式大緻則分為下面幾種:

  • 應用程式:讓使用者直接使用,為使用者提供幫助程式,例如計算機等辦公軟體,計算機上的辦公軟體,智能手機和其他應用。
  • 中間件:輔助程式運作等軟體,比如WEB伺服器和資料庫
  • OS:控制硬體,為應用程式和中間件提供運作環境,Linux 叫做OS。

使用者模式和核心模式

下面的内容可以認為是把之前的内容回顧一遍:

使用者模式

使用者程序通路的時候都是使用者模式,使用者模式是受到保護或者說權限受限的,隻能夠使用核心配置設定的記憶體和CPU進行操作,失去使用權則會處于等待的情況。

使用者程序的一大特點是使用者的空間隻能使用者程序使用,是以一旦有使用者程序崩潰了,核心可以去把它給清理掉。是以增強系統的魯棒性。

核心模式

此模式下運作的代碼對 CPU 和記憶體具有無限的使用權限,這個強大的權限使得核心可以輕易腐化和崩潰整個系統,是以核心使用的空間是隻能被核心通路的,其他任何使用者都無權通路。

特點對比

核心級線程特點 使用者級線程的特點
1.程序中的一個線程被阻塞,核心能排程同一程序的其他線程(就緒态)占有處理器運作。 2.多處理器環境中,核心能同時排程同一程序的多線程,将這些線程映射到不同的處理器核心上,提高程序的執行效率。 3.應用程式線程在使用者态運作,線程排程和管理在核心實作。線程排程時,控制權從一個線程改變到另一線程,需要模式切換,系統開銷較大 1.線程切換不需要核心模式,能節省模式切換開銷和核心資源。 2.允許程序按照特定的需要選擇不同的排程算法來排程線程。排程算法需要自己實作。 3.由于其不需要核心進行支援,是以可以跨OS運作。 4.不能利用多核處理器優點,OS排程程序,每個程序僅有一個ULT能執行 5.一個ULT阻塞,将導緻整個程序的阻塞。

對于使用者線程阻塞在後續技術的發展出現了一種叫做Jacketing技術。

使用Jacketing技術把阻塞式系統調用改造成非阻塞式的。當線程陷入系統調用時,執行jacketing程式,由jacketing程式來檢查資源使用情況,以決定是否執行程序切換或傳遞控制權給另一個線程。

也就是說Jacketing技術實作了當使用者線程阻塞的時候更加靈活的進行切換,防止出現一個線程的阻塞導緻多線程阻塞這種情況。

使用者模式切換到核心模式:

一般是發生了中斷或者無法處理的系統異常情況下出現。

此時核心會剝奪使用者程序的控制權進行處理, 并且執行一些核心的修複操作,比如缺頁中斷申請記憶體并且配置設定新的頁表給程序,這種機制為請求分頁的處理方式。

注意⚠️:更多細節可以回顧[程序排程器]檢視有關程序排程的内容。

使用者模式切換到核心模式一般會經曆下面的步驟:

  • CPU模式切換
  • 儲存目前的程序狀态到核心棧。
  • 中斷異常程式排程處理

核心模式切換到使用者模式:

當中斷異常處理排程程式完成之後,核心模式會逐漸轉為使用者模式運作,此時使用者線程回從核心棧找回目前到程序狀态,并且CPU運作模式也會執行為使用者模式。

模式切換的優劣對比

其實這兩個模式用最抽象的概念就是應用程式和系統程式之間的差别,因為對于使用者來說模式切換是透明的,基本使用過電腦用過電腦程式,基本都經曆過應用程式崩潰引發系統崩潰的情況,此時就是一種使用者模式到核心模式的切換。

使用者模式的好處在于通路和空間占用都是受到核心管控的,但是有一個很大的問題是一旦出現中斷異常就會發生使用者模式和核心模式的切換,在通常情況下看起來沒什麼問題,但是随着程序需要的記憶體越來越大,每一次切換對于系統帶來影響和開銷都是非常嚴重的。

在很多大型程序中,因為模式切換帶來的問題也并不罕見,更多情況下是使用者編寫的低品質或者問題代碼出現資源浪費導緻的問題。

簡單對比Windows:

下面我們類比Windows系統的核心模式以及使用者模式的切換,這裡主要看看微軟的官方文檔是如何介紹的:

使用者模式:程序享受專用的​​虛拟位址空間​​,和Linux類似的 在使用者模式下運作的處理器無法通路為作業系統保留的虛拟位址。

核心模式:在核心模式下運作的所有代碼都共享單個​​虛拟位址空間​​。

《Linux是怎麼樣工作的》讀書筆記

其實簡單對比一下就會發現實作機制都是類似的,隻不過内部實作代碼不同和細節不同而已,基本的特性都是相似的。

C标準庫

C标準庫在很早就被國際設立為标準規範,哪怕過去這麼多年依然通用。

而C标準庫中較為核心的元件是​

​glibc​

​​,之前介紹過​

​glibc​

​是使用者程序像核心申請記憶體的關鍵實作函數,使用glibc申請記憶體再使用mmap函數申請具體的記憶體,這部分内容可以閱讀[記憶體管理]進行了解。

glibc通過系統調用向​

​mmap​

​​申請大量的記憶體空間作為記憶體池,程式則調用​

​malloc​

​根據記憶體池配置設定出具體的記憶體使用,如果程序需要再次擷取記憶體則需要再次通過mmap擷取記憶體并且再次進行配置設定操作。

另外進階的程式設計語言同樣依賴或者基于C标準庫,比如Python的​

​ppidloop​

​就是如此,除此之外還有C++對于C标準庫的進一步擴充等。

OS提供的程式

下面列舉一些OS提供的程式:

  • 初始化系統:​

    ​init​

  • 變更系統運作方式:​

    ​sysctl、nice、sync​

  • 檔案操作:​

    ​touch、mkdir​

  • 文本資料處理:​

    ​grep、sort、uniq​

  • 性能測試:​

    ​sar、iostat​

  • 編譯:​

    ​gcc​

  • 腳本語言運作環境:​

    ​perl、python、ruby​

  • shell:​

    ​bash​

  • 視窗系統:​

    ​x​

另外系統調用通常也可以分為下面幾種:

  • 程序控制:[[010106 - 程序排程器]]
  • 記憶體管理:[[010105 - Linux記憶體管理]]
  • 程序通信:[[010106 - 程序排程器]]
  • 網絡管理(本書未涉及)
  • 檔案系統操作:[[010103 - Linux檔案系統設計]]
  • 檔案操作:[[010101 - Linux與外部結構介紹]]

寫在最後

還是挺不錯的一本書,雖然沒有那些闆磚書那麼系統,但是對于初學者來說是個不錯的切入書,這本書買實體書不是很劃算,因為配圖講解工作原理的内容比較多,甚至讓我覺得作者應該多一點文字描述......

附錄

附錄部分

LBA(Logical Block Addressing)邏輯塊尋址模式

HDD常見尋址方式

CHS尋址 CHS尋址也被稱為NORMAL 普通模式,此尋址模式是最早的 IDE 方式。

在硬碟通路時,BIOS 和 IDE 控制器對參數不做任何轉換。該模式支援的最大柱面數為 1024,最大磁頭數為 16,最大扇區數為 63,每扇區位元組數為 512,是以支援最大硬碟的容量為:

512x63x16x1024=528MB。

⚠️注意:最早期的計算機僅僅隻需要500多MB就夠用,和現在确實是天差地别。

LBA尋址

LBA的尋址特點是位址不再和實體磁盤的位置一一對應,前面CHS尋址使用了三個關鍵參數:磁頭位置,存儲柱面位置,扇區位置三個參數利用三維的參數來計算容量,而LBA尋址則使用了一個參數進行尋址,同時由IDE控制計算柱面、磁頭、扇區的參數等組成的邏輯位址轉為實體位址。 LBA可以設定最大的磁頭數為255,而其他參數和CHS尋址的模式類似,是以我們對應上面的結算公式隻需要把16改為255,最終結果如下:

512x63x255x1024=8.4G。

另外在早期的LBA尋址中主機闆上大多數是28位的LBA尋址,而前面讨論了LBA的三維參數和實體位址不是一一對應的,而是通過IDE計算邏輯位址尋找最終的實體位址。

根據計算機的位操作我們可以計算出邏輯塊為2的28次方= 137,438,953,472個位元組也就是137G,意味着最早期的磁盤尋址極限為137G,當然這也是因為當時的主機闆技術跟不上硬碟技術導緻的,并且當時的計算機使用人員用不到137G的容量。

當然要突破這個限制也非常簡單,把28位的尋址提高就可以直接支援更大容量的硬碟,經過發展,目前使用的都是48位LBA尋址方式。

而48位LBA尋址方式的理論容量極限是144,115,188,075,855,872位元組=144,000,000 GB!上億的磁盤容量基本夠目前使用。

由于LARGE、LBA尋址模式采用了邏輯變換算法看上去比CHS複雜不少,但是不少的資料、磁盤工具類軟體中采用的硬碟參數介紹和計算方法卻還是按照相對而言比較簡單的CHS尋址模式。

而LBA尋址模式說白了也是在CHS尋址模式上的改進,也需要向前相容,是以CHS尋址模式是硬碟尋址模式的基礎,了解CHS尋址模式HDD硬碟使用和維護還是很有用的。

LARGE尋址

LARGE 大硬碟模式,在硬碟的柱面超過 1024 而又不為 LBA 支援時采用。LARGE 模式采用的方法非常簡單粗暴,就是把直接把柱面數除以 2,把磁頭數乘以 2,其結果總容量不變方式尋址。

⚠️注意:LBA尋址到現在依舊是主流。

容量和大小對比

  • CHS(或稱為Normal)模式: 适應容量≤504MB的硬碟。
  • LARGE(或稱LRG)模式: 适應504MB≤容量≤8.4GB的硬碟 。
  • LBA(Logical Block Addressing)模式: 适應容量≥504MB的硬碟,但BIOS需支援擴充INT13H,否則也隻能适應≤8.4GB的硬碟

小結

參考資料

  1. ​​磁盤I/O那些事​​
  2. ​​為什麼硬碟轉速是5400或者7200這麼奇怪的數字?7200轉的硬碟一定比5400快嗎?​​
  3. ​​## 求硬碟的3種尋址模式 NORMAL LBA LARGE 詳細介紹​​
  4. ​​# 硬碟基本知識(磁頭、磁道、扇區、柱面)​​
  5. ​​linux記憶體管理---虛拟位址、邏輯位址、線性位址、實體位址的差別​​
  6. ​​linux - glibc 中的 mmap 實作 - 帶有符号 mmap 的動态庫 - 堆棧溢出 (stackoverflow.com)​​

繼續閱讀