1. 程式人生 > >從 inode 瞭解 Linux 檔案系統

從 inode 瞭解 Linux 檔案系統

原文地址:https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/index.html

理解 Linux 的硬連結與軟連結

從 inode 瞭解 Linux 檔案系統


Linux 的檔案與目錄

現代作業系統為解決資訊能獨立於程序之外被長期儲存引入了檔案,檔案作為程序建立資訊的邏輯單元可被多個程序併發使用。在 UNIX 系統中,作業系統為磁碟上的文字與影象、滑鼠與鍵盤等輸入裝置及網路互動等 I/O 操作設計了一組通用 API,使他們被處理時均可統一使用位元組流方式。換言之,UNIX 系統中除程序之外的一切皆是檔案,而 Linux 保持了這一特性。為了便於檔案的管理,Linux 還引入了目錄(有時亦被稱為資料夾)這一概念。目錄使檔案可被分類管理,且目錄的引入使 Linux 的檔案系統形成一個層級結構的目錄樹。

清單 1.所示的是普通 Linux 系統的頂層目錄結構,其中 /dev 是存放了裝置相關檔案的目錄。

清單 1. Linux 系統的頂層目錄結構
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /              根目錄 ├── bin     存放使用者二進位制檔案 ├── boot    存放核心引導配置檔案 ├── dev     存放裝置檔案
├── etc     存放系統配置檔案 ├── home    使用者主目錄 ├── lib     動態共享庫 ├── lost+found  檔案系統恢復時的恢復檔案 ├── media   可解除安裝儲存介質掛載點 ├── mnt     檔案系統臨時掛載點 ├── opt     附加的應用程式包 ├── proc    系統記憶體的對映目錄,提供核心與程序資訊 ├── root    root 使用者主目錄 ├── sbin    存放系統二進位制檔案 ├── srv     存放服務相關資料 ├── sys     sys 虛擬檔案系統掛載點 ├── tmp     存放臨時檔案 ├── usr     存放使用者應用程式 └── var     存放郵件、系統日誌等變化檔案

Linux 與其他類 UNIX 系統一樣並不區分檔案與目錄:目錄是記錄了其他檔名的檔案。使用命令 mkdir 建立目錄時,若期望建立的目錄的名稱與現有的檔名(或目錄名)重複,則會建立失敗。

1 2 3 4 5 6 # ls -F /usr/bin/zi* /usr/bin/zip*       /usr/bin/zipgrep*  /usr/bin/zipnote* /usr/bin/zipcloak*  /usr/bin/zipinfo*  /usr/bin/zipsplit*   # mkdir -p /usr/bin/zip mkdir: cannot create directory `/usr/bin/zip': File exists

Linux 將裝置當做檔案進行處理,清單 2.展示瞭如何開啟裝置檔案 /dev/input/event5 並讀取檔案內容。檔案 event5 表示一種輸入裝置,其可能是滑鼠或鍵盤等。檢視檔案 /proc/bus/input/devices 可知 event5 對應裝置的型別。裝置檔案 /dev/input/event5 使用 read() 以字元流的方式被讀取。結構體 input_event 被定義在核心標頭檔案 linux/input.h 中。

清單 2. 開啟並讀取裝置檔案
1 2 3 4 5 6 7 int fd; struct input_event ie; fd = open("/dev/input/event5", O_RDONLY); read(fd, &ie, sizeof(struct input_event)); printf("type = %d  code = %d  value = %d\n",              ie.type, ie.code, ie.value); close(fd);

硬連結與軟連結的聯絡與區別

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

圖 1. 通過檔名開啟檔案
清單 3. 移動或重新命名檔案
1 2 3 4 5 6 7 8 9 10 # 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 建立硬連結。

1 2 link oldfile newfile ln oldfile newfile

由於硬連結是有著相同 inode 號僅檔名不同的檔案,因此硬連結存在以下幾點特性:

  • 檔案有相同的 inode 及 data block;
  • 只能對已存在的檔案進行建立;
  • 不能交叉檔案系統進行硬連結的建立;
  • 不能對目錄進行建立,只可對檔案建立;
  • 刪除一個硬連結檔案並不影響其他有相同 inode 號的檔案。
清單 4. 硬連結特性展示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # 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

檔案 old.file 與 hard.link 有著相同的 inode 號:660650 及檔案許可權,inode 是隨著檔案的存在而存在,因此只有當檔案存在時才可建立硬連結,即當 inode 存在且連結計數器(link count)不為 0 時。inode 號僅在各檔案系統下是唯一的,當 Linux 掛載多個檔案系統後將出現 inode 號重複的現象(如 清單 5.所示,檔案 t3.jpg、sync 及 123.txt 並無關聯,卻有著相同的 inode 號),因此硬連結建立時不可跨檔案系統。裝置檔案目錄 /dev 使用的檔案系統是 devtmpfs,而 /root(與根目錄 / 一致)使用的是磁碟檔案系統 ext4。清單 5.展示了使用命令 df 檢視當前系統中掛載的檔案系統型別、各檔案系統 inode 使用情況及檔案系統掛載點。

清單 5. 查詢有相同 inode 號的檔案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # df -i --print-type Filesystem     Type       Inodes  IUsed    IFree IUse% Mounted on /dev/sda7      ext4      3147760 283483  2864277   10% / udev           devtmpfs   496088    553   495535    1% /dev tmpfs          tmpfs      499006    491   498515    1% /run none           tmpfs      499006      3   499003    1% /run/lock none           tmpfs      499006     15   498991    1% /run/shm /dev/sda6      fuseblk  74383900   4786 74379114    1% /media/DiskE /dev/sda8      fuseblk  29524592  19939 29504653    1% /media/DiskF   # find / -inum 1114 /media/DiskE/Pictures/t3.jpg /media/DiskF/123.txt /bin/sync

值得一提的是,Linux 系統存在 inode 號被用完但磁碟空間還有剩餘的情況。我們建立一個 5M 大小的 ext4 型別的 mo.img 檔案,並將其掛載至目錄 /mnt。然後我們使用一個 shell 指令碼將掛載在 /mnt 下 ext4 檔案系統的 indoe 耗盡(見清單 6.)。

清單 6. 測試檔案系統 inode 耗盡但仍有磁碟空間的情景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 # dd if=/dev/zero of=mo.img bs=5120k count=1 # ls -lh mo.img -rw-r--r-- 1 root root 5.0M Sep  1 17:54 mo.img # mkfs -t ext4  -F ./mo.img ... OS type: Linux Block size=1024 (log=0) Fragment size=1024 (log=0) Stride=0 blocks, Stripe width=0 blocks 1280 inodes, 5120 blocks 256 blocks (5.00%) reserved for the super user ... ... Writing superblocks and filesystem accounting information: done   # mount -o loop ./mo.img /mnt # cat /mnt/inode_test.sh #!/bin/bash   for ((i = 1; ; i++)) do     if [ $? -eq 0 ]; then         echo  "This is file_$i" > file_$i     else         exit 0     fi done   # ./inode_test.sh ./inode_test.sh: line 6: file_1269: No space left on device   # df -iT /mnt/; du -sh /mnt/ Filesystem     Type Inodes IUsed IFree IUse% Mounted on /dev/loop0     ext4   1280  1280     0  100% /mnt 1.3M    /mnt/

硬連結不能對目錄建立是受限於檔案系統的設計(見 清單 4.對目錄建立硬連結將失敗)。現 Linux 檔案系統中的目錄均隱藏了兩個個特殊的目錄:當前目錄(.)與父目錄(..)。檢視這兩個特殊目錄的 inode 號可知其實這兩目錄就是兩個硬連結(注意目錄 /mnt/lost+found/ 的 inode 號)。若系統允許對目錄建立硬連結,則會產生目錄環。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ls -aliF /mnt/lost+found total 44 11 drwx------ 2 root root 12288 Sep  1 17:54 ./ 2 drwxr-xr-x 3 root root 31744 Sep  1 17:57 ../   # stat  /mnt/lost+found/   File: `/mnt/lost+found/'   Size: 12288        Blocks: 24         IO Block: 1024   directory Device: 700h/1792d      Inode: 11          Links: 2 Access: (0700/drwx------)  Uid: (    0/    root)   Gid: (    0/    root) Access: 2012-09-01 17:57:17.000000000 +0800 Modify: 2012-09-01 17:54:49.000000000 +0800 Change: 2012-09-01 17:54:49.000000000 +0800 Birth: -

軟連結與硬連結不同,若檔案使用者資料塊中存放的內容是另一檔案的路徑名的指向,則該檔案就是軟連線。軟連結就是一個普通檔案,只是資料塊內容有點特殊。軟連結有著自己的 inode 號以及使用者資料塊(見 圖 2.)。因此軟連結的建立與使用沒有類似硬連結的諸多限制:

  • 軟連結有自己的檔案屬性及許可權等;
  • 可對不存在的檔案或目錄建立軟連結;
  • 軟連結可交叉檔案系統;
  • 軟連結可對檔案或目錄建立;
  • 建立軟連結時,連結計數 i_nlink 不會增加;
  • 刪除軟連結並不影響被指向的檔案,但若被指向的原檔案被刪除,則相關軟連線被稱為死連結(即 dangling link,若被指向路徑檔案被重新建立,死連結可恢復為正常的軟連結)。
圖 2. 軟連結的訪問

清單 7. 軟連結特性展示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # 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/

當然軟連結的使用者資料也可以是另一個軟連結的路徑,其解析過程是遞迴的。但需注意:軟連結建立時原檔案的路徑指向使用絕對路徑較好。使用相對路徑建立的軟連結被移動後該軟連結檔案將成為一個死連結(如下所示的軟連結 a 使用了相對路徑,因此不宜被移動),因為連結資料塊中記錄的亦是相對路徑指向。

1 2 3 4 5 $ ls -li total 2136 656627 lrwxrwxrwx 1 harris harris       8 Sep  1 14:37 a -> data.txt 656662 lrwxrwxrwx 1 harris harris       1 Sep  1 14:37 b -> a 656228 -rw------- 1 harris harris 2186738 Sep  1 14:37 data.txt 6

連結相關命令

在 Linux 中檢視當前系統已掛著的檔案系統型別,除上述使用的命令 df,還可使用命令 mount 或檢視檔案 /proc/mounts。

1 2 3 4 5 6 7 # mount /dev/sda7 on / type ext4 (rw,errors=remount-ro) proc on /proc type proc (rw,noexec,nosuid,nodev) sysfs on /sys type sysfs (rw,noexec,nosuid,nodev) ... ... none on /run/shm type tmpfs (rw,nosuid,nodev)

命令 ls 或 stat 可幫助我們區分軟連結與其他檔案並檢視檔案 inode 號,但較好的方式還是使用 find 命令,其不僅可查詢某檔案的軟連結,還可以用於查詢相同 inode 的所有硬連結。(見清單 8.)

清單 8. 使用命令 find 查詢軟連結與硬連結
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // 查詢在路徑 /home 下的檔案 data.txt 的軟連結 # find /home -lname data.txt /home/harris/debug/test2/a   // 檢視路徑 /home 有相同 inode 的所有硬連結 # find /home -samefile /home/harris/debug/test3/old.file /home/harris/debug/test3/hard.link /home/harris/debug/test3/old.file   # find /home -inum 660650 /home/harris/debug/test3/hard.link /home/harris/debug/test3/old.file   // 列出路徑 /home/harris/debug/ 下的所有軟連結檔案 # find /home/harris/debug/ -type l -ls 656662 0 lrwxrwxrwx 1 harris harris 1 Sep 1 14:37 /home/harris/debug/test2/b -> a 656627 0 lrwxrwxrwx 1 harris harris 8 Sep 1 14:37 /home/harris/debug/test2/a -> data.txt 789467 0 lrwxrwxrwx 1 root root 8 Sep 1 18:00 /home/harris/debug/test/soft.link -> old.file 789496    0 lrwxrwxrwx   1 root     root            7 Sep  1 18:01 /home/harris/debug/test/soft.link.dir -> old.dir

系統根據磁碟的大小預設設定了 inode 的值(見清單 9.),如若必要,可在格式檔案系統前對該值進行修改。如鍵入命令 mkfs -t ext4 -I 512/dev/sda4,將使磁碟裝置 /dev/sda4 格式成 inode 大小是 512 位元組的 ext4 檔案系統。

清單 9. 檢視系統的 inode 值
1 2 3 4 5 6 7 // 檢視磁碟分割槽 /dev/sda7 上的 inode 值 # dumpe2fs -h /dev/sda7 | grep "Inode size" dumpe2fs 1.42 (29-Nov-2011) Inode size:              256   # tune2fs -l /dev/sda7 | grep "Inode size" Inode size: