之前談論的都是已打開文件在操作系統的中的管理,但是還有更多的文件沒有被打開,被存在磁盤中,如何管理這些磁盤中的文件,就是本篇的學習目標。
目錄
1.理解硬件
磁盤結構
扇區的讀寫
CHS地址定位
磁盤的邏輯結構
2. 引??件系統
分區
inode :? ?
Data Blocks:
Bitmap:
Super Block和GDT
格式化
inode與block的分配
3. inode和block的映射
4. 目錄文件(重點)
5. dentry(directory entry)
6. 文件描述符、內存、進程與 目錄緩存路徑 的關系?(難點)
7. 掛載mount(了解)
1.理解硬件
磁盤、服務器、機柜、機房
計算機中有許多重要的硬件,比如寄存器(由各種各樣的門電路邏輯電路組合而成),而機械磁盤是計算機中的唯一的一個機械設備。
磁盤是一種外設,不同于現在的ssd卡,雖然早期的磁盤都相對速度慢,同時單位容量的價格也更便宜,任然廣泛運用于計算機中。
磁盤、二進制
計算機只認識二進制?是一種宏觀表示,這個二進制的具體實現在不同的物理設備上有不同的具體體現。
比如高低電平,網卡上的電脈沖,或者是磁盤上的NS磁性
所以,在磁盤中,改變0、1,其實是將N極改成S極,或者將s極改成n極,然后由磁性得到1或者0
? ? ? ? ? ? ? ? ? ? ?
磁頭和磁盤是懸浮著(距離極小)的,外殼相對于內層一定是完全密封的,因為灰塵直徑遠大于磁頭和磁盤的距離,灰塵在上方撞擊可能導致數據丟失的問題。主軸能讓磁盤旋轉,永磁鐵能讓磁頭沿半徑移動,讀取所有 磁道 上的內容
真實情況:
相鄰磁極之間的磁性遠大于內部的磁性,所以本質是用兩個微磁體之間是否由磁性來確定1和0。
磁盤結構
側面來看 ,磁盤可以分成三層或者兩層或者四層等(下圖是三層),每層兩個面都能記錄數據;
俯視來看,每個面上,不同的半徑形成的是不同的磁道,一定距離的磁道構成一個扇區。
? ? ? ? ? ? ? ? ? ?
所有面上的相同半徑構成了一個圓柱面,
扇區:是磁盤存儲數據的基本單位,512字節,塊設備(就算只訪問這512字節當中的一個字節,也需要全部訪問這個扇區,整個扇區是一塊完整的存儲結構)
從最外圈的磁道開始,編號為0,然后依次向內遞增;盤面的編號也是從0開始的。這一點在之后的計算中有用。![]()
扇區的讀寫
? ? ? ? ? ??
能定位在任何磁道上,就能定位在任何扇區上。
如何定位?個扇區呢??可以先確定磁頭要訪問哪?個柱?(磁道)(cylinder)?再定位磁頭(哪一層)(header)?定位?個扇區(sector)
CHS地址定位
?件 = 內容+屬性 都是數據,??就是占據那?個扇區的問題!( 我們認為不同半徑的磁道所能容納的扇區的數量是一樣的,但是 靠近主軸的同?圓?于停靠磁 頭,不存儲數據)磁盤容量 = 柱面個數(磁道數)*磁頭數(層數)*每個磁道扇區數*每個扇區字節數
就像一個三維坐標,只要知道了磁道位置/磁頭位置以及第幾扇區,就能定位扇區。
對早期的磁盤?常有效,知道?哪個磁頭,讀取哪個柱?上的第?扇區就可以讀到數據了。但是CHS模式?持的硬盤容量有限,不夠長。
磁盤的邏輯結構
將磁道上的扇區化曲為直,得到LBA的概念。
LBA(Logic Block Address)是一種邏輯抽象,但是實際去找的時候是否還是需要去轉換成CHS
LBA將扇區的變成一個一個獨立的單元,像三維數組一樣聯系到一起,
? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
整個盤:
同時,可以用一維數組來構建具體的LBA地址:
? ?半徑為r的一個盤面上的一個磁道:? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
由該磁道相同半徑的磁道組成的柱面:
多個柱面組成整個邏輯結構:
一個三維數組就被我們抽象成一維的線性存儲模式!!
***重點!!!
對于OS來說,知道LBA地址即可,LBA地址與CHS地址的轉換由磁盤??來做!固件(硬件電路,伺服系統)
注意:柱面號、磁頭號通常都是從0開始編號的。這種編號方式是硬盤存儲結構的一種約定俗成的規定,有助于硬盤控制器和操作系統對磁盤進行統一的尋址和數據管理。所以,一個CHS地址的柱面號表示的是當前所用地址以外已經被完全占滿的柱面。?
每?個扇區都有?個下標,我們叫做LBA(Logical Block Address)地址,其實就是線性地址。所以 ,怎么計算得到這個LBA地址呢?CHS轉成LBA:?磁頭數*每磁道扇區數 = 單個柱?的扇區總數?LBA = 柱?號C*單個柱?的扇區總數 + 磁頭號H*每磁道扇區數 + 扇區號S - 1?即:LBA = 柱?號C*(磁頭數*每磁道扇區數) + 磁頭號H*每磁道扇區數 + 扇區號S - 1?扇區號通常是從1開始的,?在LBA中,地址是從0開始的?柱?和磁道都是從0開始編號的?總柱?,磁道個數,扇區總數等信息,在磁盤內部會?動維護,上層開機的時候,會獲取到這些參數。LBA轉成CHS:?柱?號C = LBA // (磁頭數*每磁道扇區數)【就是單個柱?的扇區總數】?磁頭號H = (LBA % (磁頭數*每磁道扇區數)) // 每磁道扇區數?扇區號S = (LBA % 每磁道扇區數) + 1?"//": 表?除取整
以上公式都不需要死記硬背,對于工科生來說,理解這個圖就能理解換算辦法。![]()
2. 引??件系統
操作系統讀取硬盤數據的時候,其實是不會?個個扇區地讀取,這樣效率太低,?是?次性連續讀取多個扇區,即?次性讀取?個”塊”(block)。畢竟磁盤是外部設備,每次IO都有成本。因此, 磁盤也是一種“塊”設備。硬盤的每個分區是被劃分為?個個的”塊”。?個”塊”的??是由格式化的時候確定的,并且不可以更改,最常?的是4KB,即連續?個扇區組成?個 ”塊”。”塊”是?件存取的最?單位。![]()
所以,8個LBA數據構成一個“塊”,即塊號 = LBA // 8,LBA=塊號*8+n(n是指該塊中的哪個扇區)
或者 塊號*8=該塊的LBA起始地址
分區
對于win系統來說,我們有C盤D盤E盤F盤等等,其實這就是一個硬盤中分區的表示。
每個分區獨立,可以裝不同的文件系統,分區之間互不影響。
分治思想:管好一個組,就能管好所有的組。我們先研究操作系統如何管理分區中的一個組:
比如,我們將 group0 分成很多個 block groupdata block所占空間遠大于其他的幾個模塊文件=內容+屬性,類似于PCB或者struct files,inode就是管理磁盤上文件屬性的結構體![]()
Boot Sector也稱作啟動區,大小為1kb,任何文件系統都不能改變啟動區的內容!!啟動區里存放了啟動信息和分區信息。
宏觀來說,inode存文件屬性,data block存文件內容inode里面有相關的映射內容,便于去data block中找具體的文件內容。
在linux系統下,文件名不會存在inode中。


inode :? ?
?存放?件屬性 如 ?件??,所有者,最近修改時間等?當前分組所有Inode屬性的集合?inode編號以分區為單位,整體劃分,不可跨分區
我們還認為:inode結構里還有個block數組,這個數組用于記錄屬于該inode對應文件內容所存儲的塊號
Data Blocks:
按照“塊”的大小一個一個存儲在內存中,一個塊4字節,用于存儲文件內容。
Bitmap:
inode Bitmap:來顯示inode是否存在的位圖。比如該區域有8個inode,位圖為0010 0001,則表示第1、6個已經有內容了。一個塊寫進內存(一萬個比特位的位圖也才不到2kb,一個塊4kb),所以不影響我們前文提到的內存要加載4kb來進行操作。整體修改完了再寫出來即可。Block Bitmap :同理,檢測對應的data blocks是否合法的存在(是否已經被使用)。
Super Block和GDT
SB一般在每個分組的第一個字段中,存儲的是該分區的文件系統信息(每個分區的文件系統可以不一樣),但并不是每個分組中都有,也不是只有第一個分組有,這樣能保證一個超級塊出問題時,其他超級塊能用正確的信息來修復。當所有的超級塊都被破壞時,說明這個文件系統已經崩潰了。該字段記錄的包括不限于:總的inode數和block數,還能使用的inode數和block數,最近一次掛載時間、修改時間等。
struct ext2_super_block { __le32 s_inodes_count; /* Inodes count */ __le32 s_blocks_count; /* Blocks count */ __le32 s_r_blocks_count; /* Reserved blocks count */ __le32 s_free_blocks_count; /* Free blocks count */ __le32 s_free_inodes_count; /* Free inodes count */ __le32 s_first_data_block; /* First Data Block */ __le32 s_log_block_size; /* Block size */ __le32 s_log_frag_size; /* Fragment size */ __le32 s_blocks_per_group; /* # Blocks per group */ __le32 s_frags_per_group; /* # Fragments per group */ __le32 s_inodes_per_group; /* # Inodes per group */
GDT:塊組描述符,描述當前塊組中哪里到哪里是哪些內容。
先在inode bitmap中找一個可以用的位置,將0置1,加載對應的文件屬性到 inode 中去,此時文件內容少,直接將內容放到inode指向的data block中去。
刪一個文件,相當于是把inode Bitmap和Block Bitmap對應的比特值從0置1所以刪除一個文件是可以恢復的。刪除的本質其實是讓文件無效,刪除之后,如果想恢復,最好的辦法就是什么都不做,避免被其他文件覆蓋,覆蓋了就真的無了
格式化
? ? ? 我們想要在硬盤上儲?件,必須先把硬盤格式化為某種格式的?件系統,才能存儲?件。?件系統的?的就是組織和管理硬盤中的?件。
格式化的本質是在一個分區中寫入新的(空的)文件系統,兩個bitmap要清0,GDT里的使用率要降到0,SB中未使用的block和inode要加到最大。
包括在win系統中,格式化也是對于每一個分區來說的!從分區落實到分組,每個組里的inode和block的個數是動態計算后固定的,所以比例也是固定的。inode和block會成比例分配!比如一個inode分配十個block,這比例是固定的。磁盤中存在inode被分配完的情況(都是小文件)。![]()
inode與block的分配
每個分組占據了多少編號到多少編號的inode或者block都記錄在GDT里
因此,每次只需要確定每個分組的起始inode值即可。block同理。
?分區之后的格式化操作,就是對分區進?分組,在每個分組中寫?SB、GDT、BlockBitmap、Inode Bitmap等管理信息,這些管理信息統稱: ?件系統?只要知道?件的inode號,就能在指定分區中確定是哪?個分組,進?在哪?個分組確定是哪?個inode?拿到inode?件屬性和內容就全部都有了,可以通過Inode完成對文件的增刪查改
查區間,inode減去起始編號,查對應的bitmap,從而查到對應的inode table中的數據,根據映射關系找到data block
3. inode和block的映射
一個inode128字節,一個指針4字節,就算全部存指針,也只能存32個塊,一個塊能存4kb,32*4 = 128kb,甚至不到1MB,這樣的文件也太小了吧!!
一共有15個塊指針,12個是直接塊指針,還有三個間接塊指針。?

比如一級間接塊索引,,指向的是一個不裝文件數據、只存其他塊號的塊,一個塊能存1024個塊號,相當于通過這個一級索引能找1024*4kb=4mb的空間。同理,二級索引指向的全是只存一級索引塊,能裝4gb,三級能裝4tb。
所以,一個inode實際能裝的大小遠超過一個正常文件的大小,甚至大于整個磁盤的大小。
4. 目錄文件(重點)
linux文件系統基礎--VFS中的file、dentry和inode--講得非常透的一篇文章_vfs dentry-CSDN博客
建議有能力的讀者去看看這篇深度好文。
/就是一個目錄文件,/user也是一個目錄文件,文件夾就是目錄,前者是win的概念,后者是linux的概念,目錄文件存放他下面的“子目錄文件的文件名和inode的映射”以及普通文件的文件名和inode的映射,每個文件的inode和文件名的映射是存在其所在的文件夾中的。
在上層使用時,好像從來沒有用過inode,都是直接操作文件名。文件名和inode的關系是什么??
正如上所述,在linux中,文件名不存儲在inode中,文件名的信息是存在其所存在的目錄
所以地址的意義就在此,/user/zhangsan/test.cc,在/中找user文件名(即“user”)對應的inode,找到這個inode在其對應的data block中找到zhangsan對應的inode,依次找到test.cc,將test.cc加載到內存中去.......

5. dentry(directory entry)
兩個現象:1,在文件夾中能直接進行tree命令2,諸如下圖這種情況,只能不斷的訪問磁盤文件系統嗎?
文件系統是外設,速度很慢!
解決辦法:引入結構體dentry。?
將曾經訪問過、經常訪問的路徑在內存中緩存起來,形成一棵多叉樹,這樣就不需要一直訪問磁盤了!!!
6. 文件描述符、內存、進程與 目錄緩存路徑 的關系?(難點)
task_struct中的files_struct維護該進程打開的文件列表,列表中存的是一個個打開的file對象(經過VFS系統封裝出來的,讓所有文件都是file對象), 通過file對象,我們可以找到包括但不止于:1文件對應的操作表(就是將每個文件的具體操作封裝成write,read等接口的函數指針數組),2文件內核緩沖區(緩沖夠了直接由OS往外設發送),3 ?f_path,f_path指向了一個包含dentry的結構體。再觀察dentry的結構,有parent和child,由此說明dentry維護的 的確是一個樹形結構。dentry里還有一個inode對象,其對應的 inode值 就是此時file所對應的 inode值
file中前面兩個包含的結構是在上一篇文章中學習過的,提出來只是為了復習。
所以當一個目錄文件被打開后,不再需要去訪問磁盤找data block從而找到子目錄或文件,而是能通過file中的dentry直接找到子目錄或文件(dentry已經在內存中了!!!!)?
注意:不是將整個結構樹給塞進內存,而是將結構記錄在dentry中,加載對應的文件的時候就能把該文件所對應的目錄關系給加載到內存中去。?

最終,就能通過dentry找到希望打開文件的inode了。
? ? ? ? 這個樹形結構,整體構成了Linux的路徑緩存結構,打開訪問任何?件,都在先在這棵樹下根據路徑進?查找,找到就返回屬性inode和內容,沒找到就從磁盤加載路徑,添加dentry 結構,緩存新路徑![]()
7. 掛載mount(了解)
df -h 查看磁盤的掛載狀態。linux中, 必須將分區掛載到一個文件目錄樹上才能使用該分區。

剛創建好是查不到也用不了的,要先掛載才能用
