1 Linux下的文件與目錄概念
要理解文件的軟硬鏈接 , 首先來了解了解Linux下的文件的概念吧.
在 UNIX 系統中 , 操作系統爲磁盤上的文本與圖像、鼠標與鍵盤等輸入設備及網絡交互等 I/O 操作設計了一組通用 API ,使他們被處理時均可統一使用字節流方式. 也即是說 , UNIX系統中除進程外一切皆文件, Linux 也保持了這一特性.
另外, 爲了便於文件可被分類管理 , 且目錄的引入使Linux文件系統形成一顆目錄樹 . 如下顯示了Linux系統的頂層目錄結構
/ 根目錄
├── bin 存放用戶二進制文件
├── boot 存放內核引導配置文件
├── dev 存放設備文件
├── etc 存放系統配置文件
├── home 用戶主目錄
├── lib 動態共享庫
├── lost+found 文件系統恢復時的恢復文件
├── media 可卸載存儲介質掛載點
├── mnt 文件系統臨時掛載點
├── opt 附加的應用程序包
├── proc 系統內存的映射目錄,提供內核與進程信息
├── root root 用戶主目錄
├── sbin 存放系統二進制文件
├── srv 存放服務相關數據
├── sys sys 虛擬文件系統掛載點
├── tmp 存放臨時文件
├── usr 存放用戶應用程序
└── var 存放郵件、系統日誌等變化文件
Linux 與其他類 UNIX 系統一樣並不區分文件與目錄:目錄是記錄了其他文件名的文件。使用命令 mkdir 創建目錄時,若期望創建的目錄的名稱與現有的文件名(或目錄名)重複,則會創建失敗。
Linux除了不區分目錄 , 還把設備當成文件進行處理.
下面代碼展示瞭如何打開設備文件 /dev/input/event5 並讀取文件內容 . 文件 event5 表示一種輸入設備 , 其可能是鼠標或鍵盤等 . 查看文件 /proc/bus/input/devices 可知 event5 對應設備的類型 . 設備文件 /dev/input/event5 使用 read() 以字符流的方式被讀取。結構體 input_event 被定義在內核頭文件 linux/input.h 中.
打開並讀取設備文件:
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);
2 硬鏈接和軟連接
在知道了"Linux下的萬物皆文件後 , 下面我們再來深入看看文件.
我們知道文件都有文件名與數據,這在 Linux 上被分成兩個部分:用戶數據 (user data) 與元數據 (metadata)。
- 用戶數據: 又稱文件數據塊 (data block),數據塊是記錄文件真實內容的地方;
- 元數據: 文件的附加屬性,如文件大小、創建時間、所有者等信息, 其中還有一個叫 inode號的東西。在 Linux 中,元數據中的 inode 號(inode 是文件元數據的一部分但其並不包含文件名,inode 號即索引節點號)纔是文件的唯一標識而非文件名。文件名僅是爲了方便人們的記憶和使用,系統或程序通過 inode 號尋找正確的文件數據塊。
可以用 ls -i 命令進行查看inode號 , 也可以用 stat . 然後用 mv 移動並重命名文件 glibc-2.16.0.tar.xz,其結果不影響文件的用戶數據及 inode 號,文件移動前後 inode 號均爲:2485677。
# stat java
File: 'java'
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: 801h/2049d Inode: 16522851 Links: 3
Access: (0775/drwxrwxr-x) Uid: ( 1000/ lowfree) Gid: ( 1000/ lowfree)
Access: 2019-11-01 14:06:31.479058447 +0800
Modify: 2019-08-28 20:52:33.092621986 +0800
Change: 2019-08-28 20:52:33.092621986 +0800
Birth: -
...
# 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 系統引入了兩種鏈接 : 硬鏈接 (hard link) 與軟鏈接(又稱符號鏈接,即 soft link 或 symbolic link)。鏈接爲 Linux 系統解決了文件的共享使用,還帶來了隱藏文件路徑、增加權限安全及節省存儲等好處。
2.1 硬鏈接
若一個 inode 號對應多個文件名,則稱這些文件爲硬鏈接 . 換言之,硬鏈接就是同一個文件使用了多個別名 . 硬鏈接可由命令 link 或 ln 創建。如下是對文件 oldfile 創建硬鏈接。
硬鏈接可由命令 link 或 ln 創建。如下是對文件 oldfile 創建硬鏈接。
#link oldfile newfile
#ln oldfile newfile
硬鏈接是有着相同inode號僅文件名不同的文件, 因此硬鏈接具有下述幾個特點:
- 文件有相同的inode與data block;
- 只能對已存在的文件進行創建;
- 不能交叉文件系統進行硬鏈接的創建;
- 不能對目錄進行創建, 只可對文件創建;
- 刪除一個硬鏈接文件並不影響其他有相同inode號的文件.
下面是特點展示:
# 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 號重複的現象(如 下面所示,文件 t3.jpg、sync 及 123.txt 並無關聯,卻有着相同的 inode 號),因此硬鏈接創建時不可跨文件系統。設備文件目錄 /dev 使用的文件系統是 devtmpfs,而 /root(與根目錄 / 一致)使用的是磁盤文件系統 ext4。
下面展示了使用命令 df 查看當前系統中掛載的文件系統類型、各文件系統 inode 使用情況及文件系統掛載點。
# 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
硬鏈接不能對目錄創建是受限於文件系統的設計(見 清單 4.對目錄創建硬鏈接將失敗)。現 Linux 文件系統中的目錄均隱藏了兩個個特殊的目錄:當前目錄(.)與父目錄(…)。查看這兩個特殊目錄的 inode 號可知其實這兩目錄就是兩個硬鏈接(注意目錄 /mnt/lost+found/ 的 inode 號)。若系統允許對目錄創建硬鏈接,則會產生目錄環。
2.2 軟鏈接
軟鏈接與硬鏈接不同,若文件用戶數據塊中存放的內容是另一文件的路徑名的指向,則該文件就是軟連接。軟鏈接就是一個普通文件,只是數據塊內容有點特殊。軟鏈接有着自己的 inode 號以及用戶數據塊。因此軟鏈接的創建與使用沒有類似硬鏈接的諸多限制:
- 軟鏈接有自己的文件屬性及權限等;
- 可對不存在的文件或目錄創建軟鏈接;
- 軟鏈接可交叉文件系統;
- 軟鏈接可對文件或目錄創建;
- 創建軟鏈接時,鏈接計數 i_nlink 不會增加;
- 刪除軟鏈接並不影響被指向的文件,但若被指向的原文件被刪除,則相關軟連接被稱爲死鏈接(即 dangling link,若被指向路徑文件被重新創建,死鏈接可恢復爲正常的軟鏈接)。
下面是軟鏈接的特性
# 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 使用了相對路徑,因此不宜被移動),因爲鏈接數據塊中記錄的亦是相對路徑指向。
$ 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