筆記:磁盤分區、文件系統、鏈接

前言

我想要知道UNIX操作系統下closeunlink的區別,查閱了相關網絡資料和參考了《APUv3》寫下這篇記錄。

我將從計算機磁盤分區開始寫起,一直UNIX操作系統的文件系統(主要指UFS:傳統的基於BSD的UNIX文件系統),然後辨明二者區別。可以直接跳至第三部分:《i結點和目錄項》查看。

以下是本文的提綱:

  • 磁盤分區
  • 文件系統的構成是什麼?
  • i結點和目錄項是什麼?
  • 什麼是硬鏈接、符號鏈接?
  • openclose做了什麼?
  • unlink做了什麼?是否會刪除文件?

磁盤分區

基本單位:扇區

硬盤在出廠時要進行一次“低級格式化”,低級格式化的作用是把硬盤劃分成扇區(sector)。
一個扇區指一個磁道上面固定大小(一般是512字節)的弧段,是物理上的最小獨立單位,是硬件被OS使用的最小的操作單元,就是一個扇區一個扇區進行操作(扇區的大小在存儲設備生產時就設計好)。

引導扇區

硬盤最開頭(編號最小)的扇區叫做引導扇區(bootsector)。
計算機開機後訪問硬盤時所必須要讀取的這個扇區,用於加載計算機操作系統,並轉讓處理器控制權給操作系統,所以引導扇區是不會劃分給分區的。

引導扇區的512個字節中:

  • 有446個字節用來存放程序,這個程序的名字叫做(引導加載器)bootloader。這段程序負責啓動這個硬盤上某個分區裏面所安裝的操作系統。
  • 剩下的66個字節裏面有:

    • 64個字節用來做磁盤分區表(磁盤有多少個分區都在這裏面)。 64個字節的分區表裏面,每16個字節標識一個分區,所以一個硬盤最多可以有四個主分區
    • 還有2個字節作爲引導扇區的結束代碼

扇區構成塊

塊(block)是邏輯上劃分的,一個塊由一個或多個扇區組成,是文件系統中的最小的操作單位(此處的“最小單位”,針對虛擬文件系統和具體文件系統分別指的是物理塊和邏輯塊)。

塊可以分爲物理塊和邏輯塊。

物理塊(虛擬文件系統操作的最小單位)

雖然說是物理塊,它仍然是邏輯上劃分的。

操作系統的虛擬文件系統從硬件設備上讀取一個block,就是讀取了一個邏輯塊,實際是從硬件設備讀取一個或多個sector。
對於文件管理來說,每個文件對應的多個block可能是不連續的。block最終要映射到sector上,所以block的大小一般是sector的整數倍

物理塊的大小是可變化的,這取決於你在創建文件系統時的選擇。比如說,創建文件系統時,設置N個扇區組成一個物理塊,那麼每次操作系統的虛擬文件系統的I/O操作就會讀取一個虛擬塊到內存中。

虛擬文件系統:Linux支持的具體文件系統有:

  • 傳統文件系統:Ext2、ext3、ext4、Reiserfs(處理小文件)、Xfs、jfs
  • 光盤文件系統:ISO9660
  • 集羣文件系統:GFS(紅帽開發)、OCFS(oraclecluster fs)
  • 網絡文件系統:NFS、CIFS
  • Windows上的文件系統:VFAT(包括FAT、FAT32)、NTFS
  • USB文件系統:vfat

Linux把這些具體文件系統的差異屏蔽掉,構建了一個統一的虛擬文件系統(virtual file system,VFS),在用戶和基本文件系統之中加入了一層VFS,故在用戶看來Linux的文件系統只有VFS。

邏輯塊(具體文件系統操作的最小單位)

邏輯塊是相對於物理塊而言的。邏輯塊與物理塊的關係類似於虛擬內存中的頁與物理內存中的頁面的關係。

具體文件系統管理的是一個邏輯空間,這個邏輯空間就象一個大的數組,數組的每個元素就是 具體文件系統 操作的基本單位——邏輯塊。

邏輯塊是從0開始編號的,而且,邏輯塊是連續的。與邏輯塊相對的是物理塊,物理塊是數據在磁盤上的存取單位,也就是每進行一次I/O操作,最小傳輸的數據大小。

只在需要進行I/O操作時才進行邏輯塊到物理塊的映射,這顯然避免了大量的I/O操作,因而文件系統能夠變得高效。邏輯塊作爲一個抽象的概念,它必然要映射到具體的物理塊上去,因此,邏輯塊的大小必須是物理塊大小的整數倍,一般說來,兩者是一樣大的。

分區

可以把一個磁盤分成多個分區。

計算機中最多可以有4個主分區,但是我們可以在分區表(見第一部分的“引導扇區”)中拿出16字節,它不標識主分區,而是標識一個擴展分區。擴展分區可以分成N個邏輯分區。微軟一般這樣用,一個主分區(C盤),加一個擴展分區,由擴展分區再衍生出多個邏輯分區。Windows操作系統可以裝在邏輯分區上。在這種情況下計算機可以有3個主分區,1個擴展分區。

擴展分區實際上不是物理可用的分區,它僅僅是一個指向下一個分區的指針,這種指針結構將形成一個單向鏈表。
這樣在主引導扇區中除了主分區外,僅需要存儲一個被稱爲擴展分區的分區數據,通過這個擴展分區的數據可以找到下一個分區(實際上也就是下一個邏輯磁盤)的起始位置,以此起始位置類推可以找到所有的分區。無論系統中建立多少個邏輯磁盤,在主引導扇區中通過一個擴展分區的參數就可以逐個找到每一個邏輯磁盤。

分區的建立

我們在給新硬盤上建立分區時都要遵循以下的順序:建立主分區→建立擴展分區→建立邏輯分區→激活主分區→格式化所有分區
注意:

  1. 一個硬盤可以有1到3個主分區和1個擴展分區,也可以只有主分區而沒有擴展分區,但主分區必須至少有1個,擴展分區則最多隻有1個,且主分區+擴展分區總共不能超過4個。邏輯分區可以有若干個。
  2. 分出主分區後,其餘的部分可以分成擴展分區,一般是剩下的部分全部分成擴展分區,也可以不全分,剩下的部分就浪費了。
  3. 擴展分區不能直接使用,必須分成若干邏輯分區。所有的邏輯分區都是擴展分區的一部分。 硬盤的容量=主分區的容量+擴展分區的容量擴展分區的容量=各個邏輯分區的容量之和
  4. 由主分區和邏輯分區構成的邏輯磁盤稱爲驅動器(Drive)或卷(Volume)。
  5. 激活的主分區會成爲“引導分區”(或稱爲“啓動分區”),引導分區會被操作系統和主板認定爲第一個邏輯磁盤(在DOS/Windows中會被識別爲“驅動器C:”或“本地磁盤C:”,即通稱的C盤)。有關DOS/Windows啓動的重要文件,如引導記錄、boot.inintldrntdetect.com等,必須放在引導分區中。
  6. DOS/Windows 中無法看到非激活的主分區和擴展分區,但Windows 2000/Vista等NT內核的版本可以在磁盤管理中查看所有的分區。

文件系統

每個分區上可以包含一個文件系統。
UNIX文件系統包括引導塊、超級塊、i節點區、文件存儲區(數據區)、進程對換區等。

  • 引導塊佔用第0號物理塊,不屬於文件系統管轄。如果系統中有多個文件系統,只有根文件系統纔有引導程序放在引導塊中,其餘文件系統都不使用引導塊。
  • 超級塊佔用第1號物理塊,是文件系統的控制塊,超級塊包括:文件系統的大小、空閒塊數目、空閒塊索引表、空閒i節點數目、空閒i節點索引表、封鎖標記等。超級塊是文件系統爲文件分配存儲空間、回收存儲空間的依據。
  • i節點區存放i節點,i節點是對文件進行控制和管理的一種數據結構。
  • 文件存儲區是存放文件內容的區域,文件存儲區中各數據塊的使用情況在超級塊中有記錄,系統利用超級塊中的記錄完成對數據塊的分配和回收。

i 結點

每一個文件都由自己的i節點,每個i節點都有唯一的i節點號。i結點保存了文件的屬性和類型、存放文件內容的物理塊地址、最近一次的存取時間、最後一次修改時間、創建此文件的時間。

i節點的結構如下(參考/usr/include/sys/ino.h

struct dinode
{
    ushort  di_mode; 
    short   di_nlink;
    ushort di_uid;
    ushort di_gid;
    off_t  di_size;
    char di_addr[40];
    time_t di_atime;
    time_t di_mtime;
    time_t di_ctime;
}

從上面的結構中可以看出i節點中沒有記錄文件名字的。

事實上當我們根據文件名查找文件時

  1. 在目錄塊中根據文件名可以找到該文件所對應的i結點的編號
  2. 使用這個i結點編號我們就能找到該文件所對應的i結點
  3. 這個結點中包含了文件的各種描述信息以及文件存在磁盤的什麼地方的指針
  4. 根據這個指針我們就能得到文件的數據內容

硬鏈接

顯然,在不同的目錄中,不同(或相同)的文件名可以映射爲同一個i結點的編號,這意味着不同的文件名實際上可以引用自同一個文件內容。

在i結點結構體定義中可以找到short di_nlink;項,它就是鏈接計數,有多少個目錄項指向這個i結點,那麼它的di_nlink就是多少。和Java中的垃圾回收機制一樣,如果一個文件沒有任何目錄項引用它,即該文件不可能再被索引到,此時di_nlink==0這意味着文件可以被刪除(也就是釋放文件內容所佔用的磁盤數據塊)。

這就是爲什麼“解除對一個文件的鏈接”不總意味着“釋放文件佔用的磁盤塊”,因爲鏈接計數不爲0,說明還有其他的目錄項引用了該文件。

這也是爲什麼刪除文件是unlink而不是'delete,因爲沒有任何目錄項鍊接到該文件時,操作系統會自動釋放該文件所佔用的數據塊。

這種鏈接,就稱爲“硬鏈接”。

硬鏈接就是讓多個不在或者同在一個目錄下的文件名,同時能夠修改同一個文件,其中一個修改後,所有與其有硬鏈接的文件都一起修改了。

值得一提的是目錄鏈接

目錄鏈接

一個目錄也可以看作是一個文件,也就是說,目錄名映射爲一個i結點號,i節點號指向一個i結點,這個i結點有指針指向一個目錄數據塊,而這個目錄數據塊中就是目錄項數據,例如(文件名,i結點編號)(子目錄名,i結點編號)等記錄。

注意,子目錄中因爲有指向父目錄的項(即..),因此父目錄中的每一個子目錄都會使父目錄鏈接計數加1。

軟鏈接(符號鏈接)

符號鏈接(軟鏈接)是一類特殊的文件, 其包含有一條以絕對路徑或者相對路徑的形式指向其它文件或者目錄的引用。對符號鏈接文件進行讀寫的程序會表現得直接對目標文件進行操作。

也就是說,符號鏈接文件內容是目標文件的一個路徑,對符號鏈接文件操作,都會按照其中的路徑找到真正的目標文件,然後將操作施加上去。

一個符號鏈接文件僅包含有一個文本字符串,其被操作系統解釋爲一條指向另一個文件或者目錄的路徑。
它是一個獨立文件,其存在並不依賴於目標文件。
如果刪除一個符號鏈接,它指向的目標文件不受影響。
如果目標文件被移動、重命名或者刪除,任何指向它的符號鏈接仍然存在,但是它們將會指向一個不復存在的文件。這種情況被有時被稱爲被遺棄

opencloseunlink對鏈接數的影響

每一個文件,都可以通過一個struct stat的結構體來獲得文件信息,其中一個成員st_nlink代表文件的鏈接數。

  • 當通過shell的touch命令,或者在程序中open一個帶有O_CREAT不存在的文件時,文件的鏈接數爲1。
  • 通常open一個已存在的文件不會影響文件的鏈接數。open的作用只是使調用進程與文件之間建立一種 訪問關係 ,即open之後返回fd,調用進程可以通過fdread write等等一系列對文件的操作。
  • close()就是 消除 這種調用進程與文件之間的訪問關係。自然,不會影響文件的鏈接數。
  • 在調用close時,內核會檢查打開該文件的進程數,如果此數爲0(沒有任何進程使用該文件),進一步檢查文件的鏈接數,如果這個數也爲0,那麼就刪除文件內容。
  • link函數創建一個新目錄項,並且 增加一個鏈接數
  • unlink函數 刪除目錄項 ,並且 減少一個鏈接數。如果鏈接數達到0並且沒有任何進程打開該文件,該文件內容才被真正刪除
  • 如果在unlilnk之前沒有close,那麼依舊可以訪問文件內容。(也就是鏈接數爲0,但是仍然有進程在訪問。可以想到此時如果close後,根據第4條,文件就會被真正刪除。)

綜上所訴:

  1. 真正影響鏈接數的操作是linkunlink以及open的創建。
  2. 刪除文件內容的真正含義是文件的鏈接數爲0,而這個操作的本質完成者是unlink
  3. 如果close能夠實施刪除文件內容的操作,則必定是因爲在close之前有一個unlink操作。

延伸閱讀

參考文獻

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章