作者LoganChien (簡子翔)
看板b97902HW
標題[系程] 教學: 簡介 link, stat, chdir, opendir
時間Sun Apr 11 02:36:29 2010
簡介 link, stat, chdir, opendir
在開始介紹檔案操作的函式之前,我先簡單的介紹一下檔案系統(File
System),與一些和檔案系統有關的概念。
簡介檔案系統 (File System)
不知道大家有沒有自己安裝作業系統(例如:Windows 或者 Ubuntu 之
類的) 的經驗。在很多時候,我們必須使用 fdisk/spfdisk/chfdisk
之類的軟體把硬碟分割成若干個 partition(分割區)。然後,分別將
每一個分割區格式化為特定的檔案系統,例如 ext4, ext3, ufs2,
ntfs, vfat32 等等。例如我們可以把硬碟分割成以下的樣子:
sda1 sda2 sda3 <- 我的作業系統是使用 sd[a-d]
┌──┬───┬───┬───┐ 當作磁碟機代號,可能有一些
│MBR │ext4 │ext4 │fat32 │ 同學的作業系統是使用
└──┴───┴───┴───┘ hd[a-d] 作為磁碟機代號。
/ \
┌──────┬───────┬───────┬───
│Boot Sector │Block Group 1 │Block Group 2 │...
└──────┴───────┴───────┴───
/ \
┌───┬───┬──────┬─────
┬───┬───┐
│Super │fs │inode used │block used
│inode │block │
│Block │desc │bitmask │bitmask
│table │table │
└───┴───┴──────┴─────
┴───┴───┘
ref. http://web.mit.edu/tytso/www/linux/ext2intro.html#section:ext2fs
上面是一個硬碟大概的樣子,如果看不懂,也沒有關係,我想要讓大
家知道的是黃色的部分:inode table 與 block table。在 Unix-like
作業系統當中,大部分的檔案系統都是由 inode 與 block 組成。
我們可以把檔案系統類比成圖書館:inode table 就像是圖書館的索
引,每一個 inode 就會儲存和檔案有關的資料,如檔案的擁有者、
有效群組、更改時間、檔案大小、檔案的「所在地」等等。而 block
table 就像是一排一排的書櫃,會有很多的書本。
另外,每一個檔案都會有一個對應的 inode。
不過檔案系統和圖書館還是有些微的不同:
(1)
inode 和一般的書目索引不同,大多數的 Unix-like 作業系統
是
不會儲存檔案名稱的。取而代之的是一個
獨一無二的書籍編
號:st_ino。我們如果要查尋一個檔案的相關資料,實際上作業
系統是用 st_ino 來從 inode table 找出對應的 inode。
事實上如果各位真得去看 Linux 的程式碼,你會發現 ext4 檔案
系統的 inode struct 也是沒有 st_ino 這個欄位的!(當然也沒
有 filename 之類的欄位) 因為 st_ino 在 ext4 檔案系統當中,
就是該筆 inode 在 inode table 當中的 index (inode table 長
得有一點像是陣列)。
ref. http://lxr.linux.no/linux+*/fs/ext4/ext4.h#L441
至於為什麼不用「檔案名稱」當作索引,或者是為什麼要使用 st_ino
來當索引。現在我可以給大家第一個理由:用數字從陣列找出第 n 個
元素,只需要 O(1) 的時間,而用字串搜尋,很難達到 O(1),及便做
到 O(1) 也一定比陣列計算還要慢。另外,事實上還有一些設計上的
考量,我們稍候介紹 Link (檔案連結) 的時候會再提到。
還有一點要注意:先前我說到 st_ino 是一個獨一無二的檔案編號。
這一句話可能要進一步說明一下。這裡的獨一無二,是對同一個
partition 而言。理由很簡單:既然 st_ino 是 inode 在 inode
table 當中的 index,所以二個不同的 partition 當然有可能有相
同的 index。
所以 st_ino 只有在同一個 partition 之中,是獨一
無二的!
(2) 在大多數的檔案系統當中,
大的檔案不一定會連續地儲存在 block table
裡面。大的檔案可能會被分成若干個 block 然後被儲存到不同的的地
方。所以 inode 儲存的「檔案位置」更準確地說,是「檔案碎片的位
置」。例如一個 11kb 的檔案,可能 0-4kb 是在一個 block,4-8kb
是在一個 block,8-11kb 是儲存在另一個 block。
而如果
一個檔案佔用一個 block,這一個 block 就完全歸他所有,其
他的檔案不能和他共享同一個 block。而且根據統計,在大多數的情
況,Unix-like 作業系統會有很多的小檔案。所以一般一個 block 的
大小都不大,大約是 1-4kb。
也因為多數的檔案都不大,所以 inode 本身的 struct 就不會儲存
很多 block number。例如:在 ext4 的 inode struct 裡面,就只
有 15 個 block number slot。
ref. http://lxr.linux.no/#linux+v2.6.33/fs/ext4/ext4.h#L464
不過這 15 個 block number slot 只有前 12 個真得是用來儲存
檔案的 block number,我們稱之為 direct block。而剩下的三個
我們分別稱之為 indirect block, double indirect block,
third indirect block,這三個欄位會指向一個「儲存 block number
的 block」,用以處理比較大的檔案。示意圖如下:
struct inode {
...
int32_t i_block[15] =
{
blockno1, blockno2, blockno3, ....
blockno11, blockno12,
IND_BLOCK ---->
指向一個「儲有 block number 的 block」
blockno_ind_1, blockno_ind_2, ....
blockno_ind_1024
DIND_BLOCK --->
指向一個「儲存有「儲存有 block number 的 block」的 block
blockno_dind_1 =====> blockno_dind_1_ind_1
blockno_dind_1_ind_2
blockno_dind_1_ind_3
...
blockno_dind_1_ind_1024
blockno_dind_2 =====> blockno_dind_2_ind_1
blockno_dind_2_ind_2
blockno_dind_2_ind_3
...
blockno_dind_2_ind_1024
...
blockno_dind_1024 ===> blockno_dind_1024_ind_1
....
TIND_BLOCK ---> ... (以此類推)
};
...
};
ref. http://lxr.linux.no/#linux+v2.6.33/fs/ext4/ext4.h#L464
http://lxr.linux.no/#linux+v2.6.33/fs/ext4/ext4.h#L464
所有東西都是檔案
在 Unix 之下,所有的東西都是檔案!一般的檔案如:文件、網頁、
可執行檔都是一般檔案;目錄是一種檔案;Symbolic Link 是檔案;
裝置是一種「特殊」的檔案;甚至網路的 Socket 也是檔案。
這種世界萬物都是檔案是一個很重要的概念!
而檔案的種類主要有七種,我們之後談到 stat 之後,就有辦法查詢
一個檔案是屬於哪一類,我先把六個種類列下來:
Regular File:一般的檔案,如:純文字檔、可執行檔、圖片...等等。
Directory:資料夾。
Link:軟的檔案連結,
Symbolic link 又稱 Soft link。
Block Special File:裝置檔案,如:硬碟 (dev/sd*, dev/hd*, ...)
Character Special File:裝置檔案,如:Terminal (/dev/tty*)
Socket:網路連線。
FIFO:Named Pipe (IPC)
不過本篇的重點只會放在前三者。
Hard Link 與 Symbolic Link
檔案連結主要分成 Hard link 與 Symbolic Link (Soft link)
二種。Hard link 在檔案系統當中,一定要存在著對應的 inode,而
且目標 inode 會被若干個 Hard Link 共用。而 Symbolic Link 有
一點像是 Windows 作業系統下的「捷徑」,他本身是一個檔案,其
內容是一個路徑。
不過 Symbolic Link 比 Windows 捷徑稍微強大一點,因為 Unix
大部分的系統呼叫(例如:open/opendir/chdir 等等) 都會自動追縱到
Symbolic Link 的目標,所以在不少情況下,存取 Symbolic Link 就
像是存取目標檔案。
那 Hard Link 和 Symbolic Link 有什麼不同呢?
1. Hard Link 一定要指向一個 inode,所以
Hard Link 一定會是有
效的!而 Symbolic Link 則不一定有效,他有可能指向不存在的
檔案。
2.
Hard Link 因為和 inode 綁在一起,所以
不能夠跨越 partition,
也不能跨越 file system;不過 Symbolic 是記錄路徑,所以可以
誇越 partition,也可以跨越 file system。
3.
Hard Link 會共用一個 inode,而 Symbolic Link 有自己的 inode,
有自己的 block (用來儲存路徑)。
4. 使用者自己沒有辦法使用 link() 系統呼叫建立指向目錄的 Hard
Link。而 Symbolic Link 可以指向目錄。
(ps. 老師的投影片上說
root 可以建位指向目錄的 Hard Link,不過我測試的時候,都是
沒有辦法的。)
再談 Hard Link
我想,看到這裡你一定越看越覺得奇怪,什麼 st_ino,那我們常見的
檔案路徑存到那裡去了呢?Unix 的系統呼叫清一色都是要我們傳入檔
案路徑呀?那作業系統要怎麼把檔案路徑轉成 st_ino 呢?
另一個問題是:我們怎麼檔案系統談到一半就跳到 Link 了呢?在回答
這一個問題之前,先讓我問一個問題:
目錄是什麼?他是怎麼實作出來
的?
我們先前有把圖書館拿出來和檔案系統類比,現在我們再把「目錄」的
概念對應到「字典」。目錄的功用就是把檔名對應到 inode。而目錄本
身是一個檔案
(還記得吧?),所以他自己也有自己的 inode。所以 inode
與「目錄」的關係圖如下:
inode table
┌────┐
│... │ block number: 15678
├────┤ ┌───┬──────────┐
2 │15678 ●--------------------> │st_ino│filename │
├────┤ ├───┼──────────┤
│... │ │2 │. │
│2 │.. │
│54321 │boot │
│86327 │home │
│56312 │opt │
│9875 │var │
│4587 │tmp │
│3426 │opt │
└───┴──────────┘
而
所謂的 Hard Link 就是在「目錄檔案」或者該稱之為目錄的「檔
名-inode 對照表」加上一筆記錄,指向同一個 inode。例如:
+---------------------------------+
| inode table |
| ┌────┐ |
| │... │ | block number: 15678
\├────┤ | ┌────┬────────┐
3142│15678 ●----------------------+->│ st_ino│filename │
├────┤ ┌──────┐ | ├────┼────────┤
3143│16543 ●---->│block 16543 │ +----●3142 │. │
// ├────┤ └──────┘ │ ?? │.. │
|| │... │ +----●3143 │testfile │
|| |+---●3143 │hardlink │
|| || └────┴────────┘
|+----------------------------------+|
+------------------------------------+
另外,為了維持 Hard Link 的正確性,並且避免一個 inode 在還有
Hard Link 的時候就被刪掉,所以
inode 會儲存一個 st_nlink 值,
用來計算有多少 Hard Link 連結到這個 inode。在 ext4 檔案系統
當中,inode struct 存有一個 i_links_count 就是 st_nlink。
ref. http://lxr.linux.no/#linux+v2.6.33/fs/ext4/ext4.h#L450
對了,既然提到了目錄,我們就順便說明一個目錄的 st_nlink 值。
一個目錄裡面,至少有 " . " 與 " .. " 這二個檔名。
" . " 是目
錄本身的 inode; " .. " 是其父目錄的 inode。
另外,對於非根目錄,必定存在一個父目錄擁有指向自己的 hard
link;而根目錄的 " .. " 被定義成自己的 inode。所以每一個
資料夾的 st_nlink 值一定會大於或等於 2。以下是 ls 指令的執行
結果:
$ ls -al 桌面
drwxr-xr-x
4 logan logan 4096 2010-04-11 00:06 .
drwxr-xr-x 93 logan logan 4096 2010-04-11 00:05 ..
drwxr-xr-x 2 logan logan 4096 2010-04-02 21:44 DE-hw2
drwxr-xr-x 2 logan logan 4096 2010-04-08 21:18 SP-hw2-testsuite
-rw-r--r-- 1 logan logan 3439 2010-04-10 11:46 untitled.txt
我的「桌面」資料夾下,有二個資料夾:DE-hw2 與 SP-hw2-testsuite,
還有一個檔案 untitled.txt。另外你看到了我把一個 "4" 用淡藍色
標起來,這一個數字就是 st_nlink,這裡為什麼我的「桌面」資料夾
會有 4 個 hard link 指向他呢?答案是因為他有二個子資料夾,分
別都有 " .. " 指向他,加上原有的二個就有四個 hard link 了!
而 DE-hw2 與 SP-hw2-testsuite 就只有二個 hard link。
當然,
對於看得到的 Regular File 而言,st_nlink 就一定會大於
或等於 1,不然你絕對無法用 ls 之類的命令看到他。上面例子當中
的 untitled.txt 就只有一個從「桌面」指向它的 Hard Link。
我們再回頭想一想:為什麼 inode 不儲存檔案名稱?在知道 Hard
link 之後,我們知道一個檔案他是可以有不同的檔名的!我可以
有二個 Hard link 指向同一個 inode,所以 inode 儲存哪一個檔
名都不對,因此 inode 不應該儲存檔名。
等等!雞生蛋蛋生雞?
有些很細心的同學可能會注意到:我們要靠 inode 才能找到 block,
並讀取其檔案內容,然而我們又要透過 block 來找出某個檔案的
inode,這豈不是雞生蛋蛋生雞?誰來告訴我根目錄的 inode 或者
是 block 在哪裡呢?
同樣的問題,我們可以以圖書館來類比。假設今天我們的書目索引
可能在任何書櫃上,我該如何找到第一本書目索引呢?當然,這個
問題並不困難,因為書目索引的位置是固定的。
同樣的,對於檔案系統根目錄的 inode 而言也是這樣,例如在
ext4 檔案系統之中,根目錄的 st_ino 一定是 2,所以我們可
以透過這一個 inode 找到根目錄的「檔案內容」。
ref. http://lxr.linux.no/#linux+v2.6.33/fs/ext4/ext4.h#L147
http://uranus.chrysocome.net/explore2fs/es2fs.htm
回頭介紹 Symbolic Link
我們剛才有提到 Symbolic Link 本身只是一個比較「特別」的檔案,
檔案的內容只有一個檔案路徑,用來表示目標「檔案」。既然 Symbolic
Link 是一個檔案,所以他有自己的 inode,有自己的 block (用來
儲存檔案路徑)。所以他的結構大概長這樣:
inode table
┌────┐
│... │+-------------------+ block number: 98468
├────┤| | ┌────┬────────┐
1142│98468 ●--+ block: 76747 +->│ st_ino│filename │
├────┤ ┌──────┐ ├────┼────────┤
1143│76747 ●---->│/home │ │ 1142 │. │
/ ├────┤ └──────┘ │ ?? │.. │
| │... │ +-------●1143 │testfile │
| | └────┴────────┘
+----------------------------------+
不過因為 Symbolic Link 只儲存一個路徑,我們當然可以創造出各式
各樣可能產生問題的 Symbolic Link:例如把自己的檔案路徑存為目
標路徑,這樣就可以創造一個無限迴圈。或者,做一個 Symbolic Link
到父目錄也可以產生類似的效果。
為了避免讓作業系統當掉,大部分的
作業系統對 Symbolic Link 的
跳躍次數都會加以設限。
(大部分的系統不是做已走訪檢查,因為這
可能很浪費時間) 所以下面的 Symbolic Link 雖然正確,但是如果
你呼叫 stat("l0", &sb) 你會發現系統會回傳 ELOOP。
l0 -> l1 -> l2 -> l3 -> l4 -> l5 -> l6 -> l7 -> l8 -> l9 -> some.txt
ps. 跳躍次數限制的次數不一定是 8,不過我們可以透過 SYMLOOP_MAX 得知
跳路次數的限制。
未完.待續。
--
LoganChien ----- from PTT2 個板 logan -----
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 61.224.97.11
1F:推 Hseuler:推推~ 04/11 14:47
2F:推 jimmy319:太用心 太專業了 推 04/14 23:59
3F:推 averangeall:有看有推 真的是太精美了 04/18 18:49
4F:→ Bingojkt:教學文全消推3@w< 04/19 18:15