Linux檔案系統詳解

聖誕發表於2019-05-29

從作業系統的角度詳解Linux檔案系統層次、檔案系統分類、檔案系統的儲存結構、不同儲存介質的區別(RAM、ROM、Flash)、儲存節點inode。本文參考:

http://blog.chinaunix.net/uid-8698570-id-1763151.html
http://www.iteye.com/topic/816268
http://soft.chinabyte.com/os/142/12315142.shtml
http://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/
http://blog.csdn.net/kension/article/details/3796603
http://www.360doc.com/content/11/0915/17/3200886_148505332.shtml
在LINUX系統中有一個重要的概念:一切都是檔案。 其實這是UNIX哲學的一個體現,而Linux是重寫UNIX而來,所以這個概念也就傳承了下來。在UNIX系統中,把一切資源都看作是檔案,包括硬體裝置。UNIX系統把每個硬體都看成是一個檔案,通常稱為裝置檔案,這樣使用者就可以用讀寫檔案的方式實現對硬體的訪問。這樣帶來優勢也是顯而易見的:
UNIX 許可權模型也是圍繞檔案的概念來建立的,所以對裝置也就可以同樣處理了。
在這裡插入圖片描述

  1. 硬碟驅動

常見的硬碟型別有PATA, SATA和AHCI等,在Linux系統中,對不同硬碟所提供的驅動模組一般都存放在核心目錄樹drivers/ata中,而對於一般通用的硬碟驅動,也許會直接被編譯到核心中,而不會以模組的方式出現,可以通過檢視/boot/config-xxx.xxx檔案來確認:

CONFIG_SATA_AHCI=y

  1. General Block Device Layer

這一層的作用,正是解答了上面提出的第一個問題,不同的硬碟驅動,會提供不同的IO介面,核心認為這種雜亂的介面,不利於管理,需要把這些介面抽象一下,形成一個統一的對外介面,這樣,不管你是什麼硬碟,什麼驅動,對外而言,它們所提供的IO介面沒什麼區別,都一視同仁的被看作塊裝置來處理。

所以,如果在一層做的任何修改,將會直接影響到所有檔案系統,不管是ext3,ext4還是其它檔案系統,只要在這一層次做了某種修改,對它們都會產生影響。

  1. 檔案系統

檔案系統這一層相信大家都再熟悉不過了,目前大多Linux發行版本預設使用的檔案系統一般是ext4,另外,新一代的btrfs也呼之欲出,不管什麼樣的檔案系統,都是由一系列的mkfs.xxx命令來建立,如:

mkfs.ext4 /dev/sda

mkfs.btrfs /dev/sdb

核心所支援的檔案系統型別,可以通過核心目錄樹 fs 目錄中的內容來檢視。

  1. 虛擬檔案系統(VFS)

Virtual File System這一層,正是用來解決上面提出的第二個問題,試想,當我們通過mkfs.xxx系列命令建立了很多不同的檔案系統,但這些檔案系統都有各自的API介面,而使用者想要的是,不管你是什麼API,他們只關心mount/umount,或open/close等操作。

所以,VFS就把這些不同的檔案系統做一個抽象,提供統一的API訪問介面,這樣,使用者空間就不用關心不同檔案系統中不一樣的API了。VFS所提供的這些統一的API,再經過System Call包裝一下,使用者空間就可以經過SCI的系統呼叫來操作不同的檔案系統。

VFS所提供的常用API有:

mount(), umount() …

open(),close() …

mkdir() …
和檔案系統關係最密切的就是儲存介質,儲存介質大致有RAM,ROM,磁碟磁帶,快閃記憶體等。

快閃記憶體(Flash Memory)是一種長壽命的非易失性(在斷電情況下仍能保持所儲存的資料資訊)的儲存器,資料刪除不是以單個的位元組為單位而是以固定的區塊為單位(注意:NOR Flash 為位元組儲存。),區塊大小一般為256KB到20MB。快閃記憶體是電子可擦除只讀儲存器(EEPROM)的變種,EEPROM與快閃記憶體不同的是,它能在位元組水平上進行刪除和重寫而不是整個晶片擦寫,這樣快閃記憶體就比EEPROM的更新速度快。由於其斷電時仍能儲存資料,快閃記憶體通常被用來儲存設定資訊,如在電腦的BIOS(基本輸入輸出程式)、PDA(個人數字助理)、數位相機中儲存資料等。
外存通常是磁性介質或光碟,像硬碟,軟盤,磁帶,CD等,能長期儲存資訊,並且不依賴於電來儲存資訊,但是由機械部件帶動,速度與CPU相比就顯得慢的多。記憶體指的就是主機板上的儲存部件,是CPU直接與之溝通,並用其儲存資料的部件,存放當前正在使用的(即執行中)的資料和程式,它的物理實質就是一組或多組具備資料輸入輸出和資料儲存功能的積體電路,記憶體只用於暫時存放程式和資料,一旦關閉電源或發生斷電,其中的程式和資料就會丟失。
RAM又分為動態的和靜態。。靜態被用作cache,動態的常用作記憶體。。網上說快閃記憶體不能代替DRAM是因為快閃記憶體不像RAM(隨機存取儲存器)一樣以位元組為單位改寫資料,因此不能取代RAM。這個以後可以瞭解下硬體的知識再來辨別.

Linux下的檔案系統結構如下:
在這裡插入圖片描述
Linux啟動時,第一個必須掛載的是根檔案系統;若系統不能從指定裝置上掛載根檔案系統,則系統會出錯而退出啟動。之後可以自動或手動掛載其他的檔案系統。因此,一個系統中可以同時存在不同的檔案系統。
  不同的檔案系統型別有不同的特點,因而根據儲存裝置的硬體特性、系統需求等有不同的應用場合。在嵌入式Linux應用中,主要的儲存裝置為RAM(DRAM, SDRAM)和ROM(常採用FLASH儲存器),常用的基於儲存裝置的檔案系統型別包括:jffs2, yaffs, cramfs, romfs, ramdisk, ramfs/tmpfs等。
  1. 基於FLASH的檔案系統
  Flash(快閃記憶體)作為嵌入式系統的主要儲存媒介,有其自身的特性。Flash的寫入操作只能把對應位置的1修改為0,而不能把0修改為1(擦除Flash就是把對應儲存塊的內容恢復為1),因此,一般情況下,向Flash寫入內容時,需要先擦除對應的儲存區間,這種擦除是以塊(block)為單位進行的。
 快閃記憶體主要有NOR和NAND兩種技術。Flash儲存器的擦寫次數是有限的,NAND快閃記憶體還有特殊的硬體介面和讀寫時序。因此,必須針對Flash的硬體特性設計符合應用要求的檔案系統;傳統的檔案系統如ext2等,用作Flash的檔案系統會有諸多弊端。
  在嵌入式Linux下,MTD(Memory Technology Device,儲存技術裝置)為底層硬體(快閃記憶體)和上層(檔案系統)之間提供一個統一的抽象介面,即Flash的檔案系統都是基於MTD驅動層的(參見上面的Linux下的檔案系統結構圖)。使用MTD驅動程式的主要優點在於,它是專門針對各種非易失性儲存器(以快閃記憶體為主)而設計的,因而它對Flash有更好的支援、管理和基於扇區的擦除、讀/寫操作介面。
  順便一提,一塊Flash晶片可以被劃分為多個分割槽,各分割槽可以採用不同的檔案系統;兩塊Flash晶片也可以合併為一個分割槽使用,採用一個檔案系統。即檔案系統是針對於儲存器分割槽而言的,而非儲存晶片。
  (1) jffs2
  JFFS檔案系統最早是由瑞典Axis Communications公司基於Linux2.0的核心為嵌入式系統開發的檔案系統。JFFS2是RedHat公司基於JFFS開發的快閃記憶體檔案系統,最初是針對RedHat公司的嵌入式產品eCos開發的嵌入式檔案系統,所以JFFS2也可以用在Linux, uCLinux中。
  Jffs2: 日誌快閃記憶體檔案系統版本2 (Journalling Flash FileSystem v2)
  主要用於NOR型快閃記憶體,基於MTD驅動層,特點是:可讀寫的、支援資料壓縮的、基於雜湊表的日誌型檔案系統,並提供了崩潰/掉電安全保護,提供“寫平衡”支援等。缺點主要是當檔案系統已滿或接近滿時,因為垃圾收集的關係而使jffs2的執行速度大大放慢。
  目前jffs3正在開發中。關於jffs系列檔案系統的使用詳細文件,可參考MTD補丁包中mtd-jffs-HOWTO.txt。
  jffsx不適合用於NAND快閃記憶體主要是因為NAND快閃記憶體的容量一般較大,這樣導致jffs為維護日誌節點所佔用的記憶體空間迅速增大,另外,jffsx檔案系統在掛載時需要掃描整個FLASH的內容,以找出所有的日誌節點,建立檔案結構,對於大容量的NAND快閃記憶體會耗費大量時間。
   (2) yaffs:Yet Another Flash File System
  yaffs/yaffs2是專為嵌入式系統使用NAND型快閃記憶體而設計的一種日誌型檔案系統。與jffs2相比,它減少了一些功能(例如不支援資料壓縮),所以速度更快,掛載時間很短,對記憶體的佔用較小。另外,它還是跨平臺的檔案系統,除了Linux和eCos,還支援WinCE, pSOS和ThreadX等。
  yaffs/yaffs2自帶NAND晶片的驅動,並且為嵌入式系統提供了直接訪問檔案系統的API,使用者可以不使用Linux中的MTD與VFS,直接對檔案系統操作。當然,yaffs也可與MTD驅動程式配合使用。
  yaffs與yaffs2的主要區別在於,前者僅支援小頁(512 Bytes) NAND快閃記憶體,後者則可支援大頁(2KB) NAND快閃記憶體。同時,yaffs2在記憶體空間佔用、垃圾回收速度、讀/寫速度等方面均有大幅提升。
  (3) Cramfs:Compressed ROM File System
  Cramfs是Linux的創始人 Linus Torvalds參與開發的一種只讀的壓縮檔案系統。它也基於MTD驅動程式。
  在cramfs檔案系統中,每一頁(4KB)被單獨壓縮,可以隨機頁訪問,其壓縮比高達2:1,為嵌入式系統節省大量的Flash儲存空間,使系統可通過更低容量的FLASH儲存相同的檔案,從而降低系統成本。
  Cramfs檔案系統以壓縮方式儲存,在執行時解壓縮,所以不支援應用程式以XIP方式執行,所有的應用程式要求被拷到RAM裡去執行,但這並不代表比Ramfs需求的RAM空間要大一點,因為Cramfs是採用分頁壓縮的方式存放檔案,在讀取檔案時,不會一下子就耗用過多的記憶體空間,只針對目前實際讀取的部分分配記憶體,尚沒有讀取的部分不分配記憶體空間,當我們讀取的檔案不在記憶體時,Cramfs檔案系統自動計算壓縮後的資料所存的位置,再即時解壓縮到RAM中。
  另外,它的速度快,效率高,其只讀的特點有利於保護檔案系統免受破壞,提高了系統的可靠性。
  由於以上特性,Cramfs在嵌入式系統中應用廣泛。
  但是它的只讀屬性同時又是它的一大缺陷,使得使用者無法對其內容對進擴充。?
  Cramfs映像通常是放在Flash中,但是也能放在別的檔案系統裡,使用loopback 裝置可以把它安裝別的檔案系統裡。
  (4) Romfs
  傳統型的Romfs檔案系統是一種簡單的、緊湊的、只讀的檔案系統,不支援動態擦寫儲存,按順序存放資料,因而支援應用程式以XIP(eXecute In Place,片內執行)方式執行,在系統執行時,節省RAM空間。uClinux系統通常採用Romfs檔案系統。
  其他檔案系統:fat/fat32也可用於實際嵌入式系統的擴充套件儲存器(例如PDA, Smartphone, 數位相機等的SD卡),這主要是為了更好的與最流行的Windows桌面作業系統相相容。ext2也可以作為嵌入式Linux的檔案系統,不過將它用於FLASH快閃記憶體會有諸多弊端。
  2. 基於RAM的檔案系統
  (1) Ramdisk
  Ramdisk是將一部分固定大小的記憶體當作分割槽來使用。它並非一個實際的檔案系統,而是一種將實際的檔案系統裝入記憶體的機制,並且可以作為根檔案系統。將一些經常被訪問而又不會更改的檔案(如只讀的根檔案系統)通過Ramdisk放在記憶體中,可以明顯地提高系統的效能。
  在Linux的啟動階段,initrd提供了一套機制,可以將核心映像和根檔案系統一起載入記憶體。
  (2)ramfs/tmpfs
  Ramfs是Linus Torvalds開發的一種基於記憶體的檔案系統,工作於虛擬檔案系統(VFS)層,不能格式化,可以建立多個,在建立時可以指定其最大能使用的記憶體大小。(實際上,VFS本質上可看成一種記憶體檔案系統,它統一了檔案在核心中的表示方式,並對磁碟檔案系統進行緩衝。)
  Ramfs/tmpfs檔案系統把所有的檔案都放在RAM中,所以讀/寫操作發生在RAM中,可以用ramfs/tmpfs來儲存一些臨時性或經常要修改的資料,例如/tmp和/var目錄,這樣既避免了對Flash儲存器的讀寫損耗,也提高了資料讀寫速度。
  Ramfs/tmpfs相對於傳統的Ramdisk的不同之處主要在於:不能格式化,檔案系統大小可隨所含檔案內容大小變化。
  Tmpfs的一個缺點是當系統重新引導時會丟失所有資料。
  3. 網路檔案系統NFS (Network File System)
  NFS是由Sun開發並發展起來的一項在不同機器、不同作業系統之間通過網路共享檔案的技術。在嵌入式Linux系統的開發除錯階段,可以利用該技術在主機上建立基於NFS的根檔案系統,掛載到嵌入式裝置,可以很方便地修改根檔案系統的內容。
  以上討論的都是基於儲存裝置的檔案系統(memory-based file system),它們都可用作Linux的根檔案系統。實際上,Linux還支援邏輯的或偽檔案系統(logical or pseudo file system),例如procfs(proc檔案系統),用於獲取系統資訊,以及devfs(裝置檔案系統)和sysfs,用於維護裝置檔案。
  附錄:NOR快閃記憶體與NAND快閃記憶體比較
NOR FLASH
介面時序同SRAM,易使用
讀取速度較快
擦除速度慢,以64-128KB的塊為單位
寫入速度慢(因為一般要先擦除)
隨機存取速度較快,支援XIP(eXecute In Place,晶片內執行),適用於程式碼儲存。在嵌入式系統中,常用於存放載入程式、根檔案系統等。
單片容量較小,1-32MB
最大擦寫次數10萬次

NAND FLASH
地址/資料線複用,資料位較窄
讀取速度較慢
擦除速度快,以8-32KB的塊為單位
寫入速度快
順序讀取速度較快,隨機存取速度慢,適用於資料儲存(如大容量的多媒體應用)。在嵌入式系統中,常用於存放使用者檔案系統等。
單片容量較大,8-128MB,提高了單元密度
http://bbs.ednchina.com/BLOG_ARTICLE_142972.HTM

三、檔案儲存結構

介紹檔案儲存結構前先來看看檔案系統如何劃分磁碟,建立一個檔案、目錄、連結的過程。

1.物理磁碟到檔案系統
我們知道檔案最終是儲存在硬碟上的。硬碟最基本的組成部分是由堅硬金屬材料製成的塗以磁性介質的碟片,不同容量硬碟的碟片數不等。每個碟片有兩面,都可記錄資訊。碟片被分成許多扇形的區域,每個區域叫一個扇區,每個扇區可儲存128×2的N次方(N=0.1.2.3)位元組資訊。在DOS中每扇區是128×2的2次方=512位元組,碟片表面上以碟片中心為圓心,不同半徑的同心圓稱為磁軌。硬碟中,不同碟片相同半徑的磁軌所組成的圓柱稱為柱面。磁軌與柱面都是表示不同半徑的圓,在許多場合,磁軌和柱面可以互換使用,我們知道,每個磁碟有兩個面,每個面都有一個磁頭,習慣用磁頭號來區分。扇區,磁軌(或柱面)和磁頭數構成了硬碟結構的基本引數,幫這些引數可以得到硬碟的容量,基計算公式為:
儲存容量=磁頭數×磁軌(柱面)數×每道扇區數×每扇區位元組數
要點:
(1)硬碟有數個碟片,每碟片兩個面,每個面一個磁頭
(2)碟片被劃分為多個扇形區域即扇區
(3)同一碟片不同半徑的同心圓為磁軌
(4)不同碟片相同半徑構成的圓柱面即柱面
(5)公式: 儲存容量=磁頭數×磁軌(柱面)數×每道扇區數×每扇區位元組數
(6)資訊記錄可表示為:××磁軌(柱面),××磁頭,××扇區
那麼這些空間又是怎麼管理起來的呢?unix/linux使用了一個簡單的方法。
它將磁碟塊分為以下三個部分:

  1. 超級塊,檔案系統中第一個塊被稱為超級塊。這個塊存放檔案系統本身的結構資訊。比如,超級塊記錄了每個區域的大小,超級塊也存放未被使用的磁碟塊的資訊。
  2. I-切點表。超級塊的下一個部分就是i-節點表。每個i-節點就是一個對應一個檔案/目錄的結構,這個結構它包含了一個檔案的長度、建立及修改時間、許可權、所屬關係、磁碟中的位置等資訊。一個檔案系統維護了一個索引節點的陣列,每個檔案或目錄都與索引節點陣列中的唯一一個元素對應。系統給每個索引節點分配了一個號碼,也就是該節點在陣列中的索引號,稱為索引節點號
  3. 資料區。檔案系統的第3個部分是資料區。檔案的內容儲存在這個區域。磁碟上所有塊的大小都一樣。如果檔案包含了超過一個塊的內容,則檔案內容會存放在多個磁碟塊中。一個較大的檔案很容易分佈上千個獨產的磁碟塊中。

Linux正統的檔案系統(如ext2、ext3)一個檔案由目錄項、inode和資料塊組成。
目錄項:包括檔名和inode節點號。
Inode:又稱檔案索引節點,是檔案基本資訊的存放地和資料塊指標存放地。
資料塊:檔案的具體內容存放地。

Linux正統的檔案系統(如ext2、3等)將硬碟分割槽時會劃分出目錄塊、inode Table區塊和data block資料區域。一個檔案由一個目錄項、inode和資料區域塊組成。Inode包含檔案的屬性(如讀寫屬性、owner等,以及指向資料塊的指標),資料區域塊則是檔案內容。當檢視某個檔案時,會先從inode table中查出檔案屬性及資料存放點,再從資料塊中讀取資料。

檔案儲存結構大概如下:
在這裡插入圖片描述
其中目錄項的結構如下(每個檔案的目錄項儲存在改檔案所屬目錄的檔案內容裡):

圖4:目錄項結構
在這裡插入圖片描述
其中檔案的inode結構如下(inode裡所包含的檔案資訊可以通過stat filename檢視得到):
在這裡插入圖片描述
以上只反映大體的結構,linux檔案系統本身在不斷髮展。但是以上概念基本是不變的。且如ext2、ext3、ext4檔案系統也存在很大差別,如果要了解可以檢視專門的檔案系統介紹。
2. 建立一個檔案的過程

我們從前面可以知道檔案的內容和屬性是分開存放的,那麼又是如何管理它們的呢?現在我們以建立一個檔案為例來講解。
在命令列輸入命令:
$ who > userlist
當完成這個命令時。檔案系統中增加了一個存放命令who輸出內容的新檔案userlist,那麼這整個過程到底是怎麼回事呢?
檔案主要有屬性、內容以及檔名三項。核心將檔案內容存放在資料區,檔案屬性存放在i-節點,檔名存放在目錄中。
建立成功一個檔案主要有以下四個步驟:

  1.    儲存屬性 也就是檔案屬性的儲存,核心先找到一塊空的i-節點。例如,核心找到i-節點號921130。核心把檔案的資訊記錄其中。如檔案的大小、檔案所有者、和建立時間等。
    
  2.    儲存資料 即檔案內容的儲存,由於該檔案需要3個資料塊。因此核心從自由塊的列表中找到3個自由塊。如600、200、992,核心緩衝區的第一塊資料複製到塊600,第二和第三分別複製到922和600.
    
  3.    記錄分配情況,資料儲存到了三個資料塊中。所以必須要記錄起來,以後再找到正確的資料。分配情況記錄在檔案的i-節點中的磁碟序號列表裡。這3個編號分別放在最開始的3個位置。
    
  4.    新增檔名到目錄,新檔案的名字是userlist 核心將檔案的入口(47,userlist)新增到目錄檔案裡。檔名和i-節點號之間的對應關係將檔名和檔案和檔案的內容屬性連線起來,找到檔名就找到檔案的i-節點號,通過i-節點號就能找到檔案的屬性和內容。
    

程式碼具體實現過程參考:
http://blog.csdn.net/kai_ding/article/details/9206057
3.建立一個目錄的過程

前面說了建立一個檔案的大概過程,也瞭解檔案內容、屬性以及入口的儲存方式,那麼建立一個目錄時又是怎麼回事呢?
我現在test目錄使用命令mkdir 新增一個子目錄child:

從使用者的角度看,目錄child是目錄test的一個子目錄,那麼在系統中這層關係是怎麼實現的呢?實際上test目錄包含一個指向子目錄child的i-節點的連結,原理跟普通檔案一樣,因為目錄也是檔案。

目錄其實也是檔案,只是它的內容比較特殊。所以它的建立過程和檔案建立過程一樣,只是第二步寫的內容不同。

  1.  系統找到空閒的i-節點號887220,寫入目錄的屬性
    
  2.  找到空閒的資料塊1002來儲存目錄的內容,只是目錄的內容比較特殊,包含檔名字列表,列表一般包含兩個部分:i-節點號和檔名,這個列表其實也就是檔案的入口,新建的目錄至少包含三個目錄”.”和”..”其中”.”指向自己,”..”指向上級目錄,我們可以通過比較對應的i-節點號來驗證,887270 對應著上級目錄中的child對應的i-節點號
    
  3.  記錄分配情況。這個和建立檔案完全一樣
    
  4.  新增目錄的入口到父目錄,即在父目錄中的child入口。
    

一般都說檔案存放在某個目錄中,其實目錄中存入的只是檔案在i-節點表的入口,而檔案的內容則儲存在資料區。我們一般會說“檔案userlist在目錄test中”,其實這意味著目錄test中有一個指向i-節點921130的連結,這個連結所附加的檔名為userlist,這也可以這樣理解:目錄包含的是檔案的引用,每個引用被稱為連結。檔案的內容儲存在資料塊。檔案的屬性被記錄在一個被稱為i-節點的結構中。I-節點的編號和檔名關聯起來存在目錄中。
注意:其中“.”表示是當前目錄。而“…”是當前目錄的父目錄。但也有特殊情況:如我們檢視根目錄/的情況:

發現“.”和“…”都指向i-節點2。實際上當我們用mkfs建立一個檔案系統時,mkfs都會將根目錄的父目錄指向自己。所以根目錄下.和…指向同一個i-節點也不奇怪了。
程式碼具體實現參考:
http://blog.csdn.net/kai_ding/article/details/9206057

  1. 理解連結

我們知道檔案都有檔名與資料,這在 Linux 上被分成兩個部分:使用者資料 (user data) 與後設資料 (metadata)。使用者資料,即檔案資料塊 (data block),資料塊是記錄檔案真實內容的地方;而後設資料則是檔案的附加屬性,如檔案大小、建立時間、所有者等資訊。在 Linux 中,後設資料中的 inode 號(inode 是檔案後設資料的一部分但其並不包含檔名,inode 號即索引節點號)才是檔案的唯一標識而非檔名。檔名僅是為了方便人們的記憶和使用,系統或程式通過 inode 號尋找正確的檔案資料塊。圖 1.展示了程式通過檔名獲取檔案內容的過程。
圖 1. 通過檔名開啟檔案
在這裡插入圖片描述

圖 1. 通過檔名開啟檔案

清單 3. 移動或重新命名檔案

 # stat /home/harris/source/glibc-2.16.0.tar.xz
  File: `/home/harris/source/glibc-2.16.0.tar.xz'
  Size: 9990512        Blocks: 19520      IO Block: 4096   regular file
Device: 807h/2055d      Inode: 2485677     Links: 1
Access: (0600/-rw-------)  Uid: ( 1000/  harris)   Gid: ( 1000/  harris)
...
...
# mv /home/harris/source/glibc-2.16.0.tar.xz /home/harris/Desktop/glibc.tar.xz
# ls -i -F /home/harris/Desktop/glibc.tar.xz
2485677 /home/harris/Desktop/glibc.tar.xz

在 Linux 系統中檢視 inode 號可使用命令 stat 或 ls -i(若是 AIX 系統,則使用命令 istat)。清單 3.中使用命令 mv 移動並重新命名檔案 glibc-2.16.0.tar.xz,其結果不影響檔案的使用者資料及 inode 號,檔案移動前後 inode 號均為:2485677。
為解決檔案的共享使用,Linux 系統引入了兩種連結:硬連結 (hard link) 與軟連結(又稱符號連結,即 soft link 或 symbolic link)。

具體關係可以看下圖:

為 Linux 系統解決了檔案的共享使用,還帶來了隱藏檔案路徑、增加許可權安全及節省儲存等好處。若一個 inode 號對應多個檔名,則稱這些檔案為硬連結。換言之,硬連結就是同一個檔案使用了多個別名(見 圖 2.hard link 就是 file 的一個別名,他們有共同的 inode)。硬連結可由命令 link 或 ln 建立。如下是對檔案 oldfile 建立硬連結。
link oldfile newfile
ln oldfile newfile
由於硬連結是有著相同 inode 號僅檔名不同的檔案,因此硬連結存在以下幾點特性:
檔案有相同的 inode 及 data block;
只能對已存在的檔案進行建立;
不能交叉檔案系統進行硬連結的建立;
不能對目錄進行建立,只可對檔案建立;
刪除一個硬連結檔案並不影響其他有相同 inode 號的檔案。

建立一個連結的步驟大概如下:
1) 通過原檔案的檔名找到檔案的i-節點號
2) 新增檔名關聯到目錄,新檔案的名字是mylink 核心將檔案的入口(921130,mylink)新增到目錄檔案裡。
和建立檔案的過程比較發現,連結少了寫檔案內容的步驟,完全相同的是把檔名關聯到目錄這一步
現在.i- 節點號921130對應了兩個檔名。連結數也會變成2個,檔案的內容並不會發生任何變化。前面我們已經講了:目錄包含的是檔案的引用,每個引用被稱為連結。所以連結檔案和原始檔案本質上是一樣的,因為它們都是指向同一個i-節點。由於此原因也就可以理解連結的下列特性:你改變其中任何一個檔案的內容,別的連結檔案也一樣是變化;另外如果你刪除某一個檔案,系統只會在所指向的i-節點上把連結數減1,只有當連結數減為零時才會真正釋放i-節點。
硬連結有兩個特點:
1)不能跨檔案系統
2)不能對目錄
清單 4. 硬連結特性展示

# ls -li 
 total 0 

 // 只能對已存在的檔案建立硬連線
 # link old.file hard.link 
 link: cannot create link `hard.link' to `old.file': No such file or directory 

 # echo "This is an original file" > old.file 
 # cat old.file 
 This is an original file 
 # stat old.file 
  File: `old.file'
  Size: 25             Blocks: 8          IO Block: 4096   regular file 
 Device: 807h/2055d      Inode: 660650      Links: 2 
 Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root) 
 ... 
 // 檔案有相同的 inode 號以及 data block 
 # link old.file hard.link | ls -li 
 total 8 
 660650 -rw-r--r-- 2 root root 25 Sep  1 17:44 hard.link 
 660650 -rw-r--r-- 2 root root 25 Sep  1 17:44 old.file 

 // 不能交叉檔案系統
 # ln /dev/input/event5 /root/bfile.txt 
 ln: failed to create hard link `/root/bfile.txt' => `/dev/input/event5': 
 Invalid cross-device link 

 // 不能對目錄進行建立硬連線
 # mkdir -p old.dir/test 
 # ln old.dir/ hardlink.dir 
 ln: `old.dir/': hard link not allowed for directory 
 # ls -iF 
 660650 hard.link  657948 old.dir/  660650 old.file

軟連結與硬連結不同,若檔案使用者資料塊中存放的內容是另一檔案的路徑名的指向,則該檔案就是軟連線。軟連結就是一個普通檔案,只是資料塊內容有點特殊。軟連結有著自己的 inode 號以及使用者資料塊(見 圖 2.)。因此軟連結的建立與使用沒有類似硬連結的諸多限制:
軟連結有自己的檔案屬性及許可權等;
可對不存在的檔案或目錄建立軟連結;
軟連結可交叉檔案系統;
軟連結可對檔案或目錄建立;
建立軟連結時,連結計數 i_nlink 不會增加;
刪除軟連結並不影響被指向的檔案,但若被指向的原檔案被刪除,則相關軟連線被稱為死連結(即 dangling link,若被指向路徑檔案被重新建立,死連結可恢復為正常的軟連結)。
圖 2. 軟連結的訪問
在這裡插入圖片描述
軟連結
實際上只是一段文字,裡面包含著它所指向的檔案的名字,系統看到軟連結後自動跳到對應的檔案位置處進行處理;相反,硬連結為檔案開設一個新的目錄項,硬連結與檔案原有的名字是平權的,在Linux看來它們是等價的。由於這個原因,硬連結不能連線兩個不同檔案系統上的檔案。

軟連線與windows下的快捷方式類似
至於硬連線,舉個例子說吧,你把dir1/file1硬連線到dir2/file2, 就是在dir2下建立一個dir1/file1的映象檔案file2,它與file1是佔用一樣大的空間的,並且改動兩者中的一個,另一個也會發生同樣的改動.
軟連線和硬連線可以這樣理解:
硬連線就像一個檔案有多個檔名,
軟連線就是產生一個新檔案(這個檔案內容,實際上就是記當要連結原檔案路徑的資訊),這個檔案指向另一個檔案的位置,
硬連線必須在同一檔案系統中,而軟連線可以跨檔案系統
硬連線 :原始檔名和連結檔名都指向相同的實體地址,目錄不能夠有硬連線,檔案在磁碟中只有一個複製,可以節省硬碟空間,由於刪除檔案要在同一個索引節點屬於唯一的連線時才能成功,因此可以防止不必要的誤刪除軟連線(符號連線)用ln -s命令建立檔案的符號連線,符號連線是linux特殊檔案的一種,作為一個檔案,它的資料是它所連線的檔案的路徑名,類似於硬體方式,可以刪除原始檔案 而連線檔案仍然存在。**
清單 5. 軟連結特性展示

# ls -li 
 total 0 

 // 可對不存在的檔案建立軟連結
 # ln -s old.file soft.link 
 # ls -liF 
 total 0 
 789467 lrwxrwxrwx 1 root root 8 Sep  1 18:00 soft.link -> old.file 

 // 由於被指向的檔案不存在,此時的軟連結 soft.link 就是死連結
 # cat soft.link 
 cat: soft.link: No such file or directory 

 // 建立被指向的檔案 old.file,soft.link 恢復成正常的軟連結
 # echo "This is an original file_A" >> old.file 
 # cat soft.link 
 This is an original file_A 

 // 對不存在的目錄建立軟連結
 # ln -s old.dir soft.link.dir 
 # mkdir -p old.dir/test 
 # tree . -F --inodes 
 . 
├── [ 789497]  old.dir/ 
│   └── [ 789498]  test/ 
├── [ 789495]  old.file 
├── [ 789495]  soft.link -> old.file 
└── [ 789497]  soft.link.dir -> old.dir/

程式碼具體實現參考:http://blog.csdn.net/kai_ding/article/details/8942884

四、檔案節點inode
可以看到inode節點好比是檔案的大腦,下面就詳細介紹一下inode。
1.inode是什麼
理解inode,要從檔案儲存說起。
扇區(sector):硬體(磁碟)上的最小的操作單位,是作業系統和塊裝置(硬體、磁碟)之間傳送資料的單位。
block由一個或多個sector組成,檔案系統中最小的操作單位;OS的虛擬檔案系統從硬體裝置上讀取一個block,實際為從硬體裝置讀取一個或多個sector。對於檔案管理來說,每個檔案對應的多個block可能是不連續的;
block最終要對映到sector上,所以block的大小一般是sector的整數倍。不同的檔案系統block可使用不同的大小,作業系統會在記憶體中開闢記憶體,存放block到所謂的block buffer中。在Ext2中,物理塊的大小是可變化的,這取決於在建立檔案系統時的選擇,之所以不限制大小,也正體現了Ext2的靈活性和可擴充性。通常,Ext2的物理塊佔一個或幾個連續的扇區,顯然,物理塊的數目是由磁碟容量等硬體因素決定的。具體檔案系統所操作的基本單位是邏輯塊,只在需要進行I/O操作時才進行邏輯塊到物理塊的對映,這顯然避免了大量的I/O操作,因而檔案系統能夠變得高效。邏輯塊作為一個抽象的概念,它必然要對映到具體的物理塊上去,因此,邏輯塊的大小必須是物理塊大小的整數倍,一般說來,兩者是一樣大的。
通常,一個檔案佔用的多個物理塊在磁碟上是不連續儲存的,因為如果連續儲存,則經過頻繁的刪除、建立、移動檔案等操作,最後磁碟上將形成大量的空洞,很快磁碟上將無空間可供使用。因此,必須提供一種方法將一個檔案佔用的多個邏輯塊對映到對應的非連續儲存的物理塊上去,Ext2等類檔案系統是用索引節點解決這個問題的。
在這裡插入圖片描述在這裡插入圖片描述
檔案資料都儲存在"塊"中,那麼很顯然,我們還必須找到一個地方儲存檔案的元資訊,比如檔案的建立者、檔案的建立日期、檔案的大小等等。這種儲存檔案元資訊的區域就叫做inode,中文譯名為"索引節點"。
在Unix/Linux上,一個檔案由一個inode 表示。inode在系統管理員看來是每一個檔案的唯一標識,在系統裡面,inode是一個結構,儲存了關於這個檔案的大部分資訊。
2.inode內容
inode包含檔案的元資訊,具體來說有以下內容:
*檔案的位元組數
檔案擁有者的UserID檔案的GroupID
*檔案的讀、寫、執行許可權
*檔案的時間戳,共有三個:ctime指inode上一次變動的時間,mtime指檔案內容上一次變動的時間,atime指檔案上一次開啟的時間。
連結數,即有多少檔名指向這個inode檔案資料block的位置可以用stat命令,檢視某個檔案的inode資訊:statexample.txt
總之,除了檔名以外的所有檔案資訊,都存在inode之中。至於為什麼沒有檔名,下文會有詳細解釋。
inode中儲存了一個檔案的以下資訊:
3.inode結構

struct inode {
        struct hlist_node       i_hash;              /* 雜湊表 */
        struct list_head        i_list;              /* 索引節點連結串列 */
        struct list_head        i_dentry;            /* 目錄項鍊表 */
        unsigned long           i_ino;               /* 節點號 */
        atomic_t                i_count;             /* 引用記數 */
        umode_t                 i_mode;              /* 訪問許可權控制 */
        unsigned int            i_nlink;             /* 硬連結數 */
        uid_t                   i_uid;               /* 使用者id */
        gid_t                   i_gid;               /* 使用者id組 */
        kdev_t                  i_rdev;              /* 實裝置識別符號 */
        loff_t                  i_size;              /* 以位元組為單位的檔案大小 */
        struct timespec         i_atime;             /* 最後訪問時間 */
        struct timespec         i_mtime;             /* 最後修改(modify)時間 */
        struct timespec         i_ctime;             /* 最後改變(change)時間 */
        unsigned int            i_blkbits;           /* 以位為單位的塊大小 */
        unsigned long           i_blksize;           /* 以位元組為單位的塊大小 */
        unsigned long           i_version;           /* 版本號 */
        unsigned long           i_blocks;            /* 檔案的塊數 */
        unsigned short          i_bytes;             /* 使用的位元組數 */
        spinlock_t              i_lock;              /* 自旋鎖 */
        struct rw_semaphore     i_alloc_sem;         /* 索引節點訊號量 */
        struct inode_operations *i_op;               /* 索引節點操作表 */
        struct file_operations  *i_fop;              /* 預設的索引節點操作 */
        struct super_block      *i_sb;               /* 相關的超級塊 */
        struct file_lock        *i_flock;            /* 檔案鎖連結串列 */
        struct address_space    *i_mapping;          /* 相關的地址對映 */
        struct address_space    i_data;              /* 裝置地址對映 */
        struct dquot            *i_dquot[MAXQUOTAS]; /* 節點的磁碟限額 */
        struct list_head        i_devices;           /* 塊裝置連結串列 */
        struct pipe_inode_info  *i_pipe;             /* 管道資訊 */
        struct block_device     *i_bdev;             /* 塊裝置驅動 */
        unsigned long           i_dnotify_mask;      /* 目錄通知掩碼 */
        struct dnotify_struct   *i_dnotify;          /* 目錄通知 */
        unsigned long           i_state;             /* 狀態標誌 */
        unsigned long           dirtied_when;        /* 首次修改時間 */
        unsigned int            i_flags;             /* 檔案系統標誌 */
        unsigned char           i_sock;              /* 可能是個套接字吧 */
        atomic_t                i_writecount;        /* 寫者記數 */
        void                    *i_security;         /* 安全模組 */
        __u32                   i_generation;        /* 索引節點版本號 */
        union {
                void            *generic_ip;         /* 檔案特殊資訊 */
        } u;
};

inode就是一個檔案的一部分描述,不是全部,在核心中,inode對應了這樣一個實際存在的結構。
複製程式碼
縱觀整個inode的C語言描述,沒有發現關於檔名的東西,也就是說檔名不由inode儲存,實際上系統是不關心檔名的,對於系統中任何的操作,大部分情況下你都是通過檔名來做的,但系統最終都要通過找到檔案對應的inode來操作檔案,由inode結構中 *i_op指向的介面來操作。
檔案系統如何存取檔案的:
1)、根據檔名,通過Directory裡的對應關係,找到檔案對應的Inodenumber
2)、再根據Inodenumber讀取到檔案的Inodetable
3)、再根據Inodetable中的Pointer讀取到相應的Blocks
這裡有一個重要的內容,就是Directory,他不是我們通常說的目錄,而是一個列表,記錄了一個檔案/目錄名稱對應的Inodenumber。

轉自: https://www.cnblogs.com/alantu2018/p/8461749.html

相關文章