lienhua34
2014-09-15
1 文件系統數據結構
UNIX 文件系統通過 i 節點來存儲文件的信息。如圖 1 所示為一個磁盤柱面上的 i 節點和數據塊示意圖。其中 i 節點是一個固定長度的記錄項,它包含了有關文件的大部分信息。數據塊用於存儲文件的實際內容。每個文件的 i 節點會記錄該文件的內容所占用的數據塊信息。

圖 1: i 節點和數據塊
圖 1 中還有一些信息需要進行說明:
1. 每個目錄項只存儲了文件的文件名和 i 節點編號(每個文件系統各自對它們的 i 節點進行編號)。文件的其它信息則記錄在 i 節點中,例如,文件類型、文件訪問權限位、文件長度等。
2. 每個 i 節點都有一個鏈接計數,其值是指向該 i 節點的目錄項數。只有當鏈接計數減少至 0 時,才可刪除該文件(即釋放該文件所占用的數據塊)。通過 i 節點鏈接使多個目錄項指向同一個文件的這種鏈接類型稱為硬鏈接。
2 硬鏈接和符號鏈接
上一節講到了,硬鏈接是通過 i 節點編號來使多個目錄項指向同一個文件。因此,硬鏈接存在一些限制,
• 硬鏈接要求鏈接和文件位於同一個文件系統中(因為每個文件系統具有各自的 i 節點編號)。
• 只有超級用戶才能創建指向目錄的硬鏈接(避免在文件系統中存在循環)。
對於符號鏈接,該文件的實際內容(在數據塊中)包含了該符號鏈接所指向的文件的名字。對符號鏈接以及它所指向何種對象並無任何文件系統限制,任何用戶都可以創建指向目錄的符號鏈接。符號鏈接一般用於將一個文件或整個目錄結構移到文件系統中的另一個位置。
3 創建鏈接 link 函數、解除鏈接 unlink 函數
link 函數用於創建一個現有文件的硬鏈接。
#include <unistd.h>
int link(cosnt char *existingpath, const char *newpath);
返回值:若成功則返回0,若出錯則返回-1。
此函數創建一個新目錄項 newpath,它引用現有的文件 existingpath。如果 newpath 已經存在,則返回出錯。該函數只創建 newpath 中的最后一個分量,路徑中其他部分應當已經存在。
unlink 函數刪除一個現有的目錄項。
#include <unistd.h>
int unlink(const char *pathname);
返回值:若成功則返回0,若出錯則返回-1.
此函數刪除目錄項,比將由 pathname 所引用的文件的鏈接計數減 1。如果出錯,則不對該文件做任何修改。只有當鏈接計數達到 0 時,該文件的內容才會被刪除。另一個阻止刪除文件內容的條件是:有進程打開着該文件。關閉一個文件時,內核首先檢查打開該文件的進程數。如果該數達到 0,然后內核檢查其鏈接計數,如果鏈接計數也是 0,那么就刪除該文件的內容。
如果 pathname 是符號鏈接,那么 unlink 刪除該符號鏈接,而不會刪除該符號鏈接所引用的文件。
例子:
下面程序先使用 open 打開文件 tempfile,然后 unlink 刪除目錄項,接着進程進入 15 秒鍾睡眠時間。
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <fcntl.h> int main(void) { if (open("tempfile", O_RDWR) < 0) { printf("open error: %s\n", strerror(errno)); exit(-1); } if (unlink("tempfile") < 0) { printf("unlink error: %s\n", strerror(errno)); exit(-1); } printf("file unlinked\n"); sleep(15); printf("done\n"); exit(0); }
編譯該程序,生成文件 unlinkdemo,然后執行該文件,
1 lienhua34:demo$ ls -l tempfile 2 -rw-r--r-- 1 lienhua34 lienhua34 20094393 9月 15 21:06 tempfile 3 lienhua34:demo$ df /home 4 文件系統 1K-塊 已用 可用 已用% 掛載點 5 /dev/sda1 305603832 14811828 275268216 6% / 6 lienhua34:demo$ ./unlinkdemo & 7 [1] 3113 8 lienhua34:demo$ file unlinked 9 ls -l tempfile 10 ls: 無法訪問tempfile: 沒有那個文件或目錄 11 lienhua34:demo$ df /home 12 文件系統 1K-塊 已用 可用 已用% 掛載點 13 /dev/sda1 305603832 14811828 275268216 6% / 14 lienhua34:demo$ done 15 df /home 16 文件系統 1K-塊 已用 可用 已用% 掛載點 17 /dev/sda1 305603832 14792140 275287904 6% /
通過上面的執行結果,我們可以看出,只有等到執行 unlinkdemo 文件的進程結束之后(第14行),文件 tempfile 的內容才被真正刪除。unlink 的這種性質經常被程序用來確保即使是在該程序崩潰時,它所創建的臨時文件也不會遺留下來。進程用 open 或 creat 創建一個文件,然后立即調用 unlink。因為該文件被進程打開着,所以不會將其內容刪除。只有當進程關閉該文件或終止時(在這種情況下,內核關閉該進程打開的全部文件),該文件的內容才會被刪除。
4 符號鏈接的 symlink 和 readlink 函數
symlink 函數創建一個符號鏈接。
#include <unistd.h>
int symlink(const char *actualpath, const char *sympath);
返回值:若成功則返回0,若出錯則返回-1。
該函數創建了一個指向 actualpath 的新目錄項 sympath,在創建此符號鏈接時,並不要求 actualpath 已經存在。並且,actualpath 和 sympath並不需要位於同一文件系統中。
readlink 函數打開符號鏈接本身,並讀取該鏈接中的內容(不是該鏈接所引用的文件的內容)。
#include <unistd.h>
ssize_t readlink(const char *restrict pathname, char *restrict buf, size_t bufsize);
返回值:若成功則返回讀到的字節數,若出錯則返回-1。
如果此函數成功執行,則返回讀入 buf 的字節數。在 buf 中返回的符號鏈接的內容不以 null 字符終止。
例子:
下面程序創建文件 bar 的符號鏈接 barlink,然后讀入符號鏈接 barlink的內容並進行打印。
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #define BUFFER_LEN 2014 int main(void) { char buf[BUFFER_LEN]; ssize_t buflen; if (symlink("bar", "barlink") < 0) { printf("symlink error: %s\n", strerror(errno)); exit(-1); } printf("create symbol link \"barlink\".\n"); if ((buflen = readlink("barlink", buf, BUFFER_LEN)) < 0) { printf("readlink error: %s\n", strerror(errno)); exit(-1); } printf("barlink context: %s\n", buf); exit(0); }
編譯該程序,生成 symlinkdemo,然后運行該文件,
lienhua34:demo$ gcc -o symlinkdemo symlinkdemo.c lienhua34:demo$ ./symlinkdemo create symbol link "barlink". barlink context: bar lienhua34:demo$ ls -l bar barlink -rw-r--r-- 1 lienhua34 lienhua34 7 9月 15 20:02 bar lrwxrwxrwx 1 lienhua34 lienhua34 3 9月 15 20:07 barlink -> bar lienhua34:demo$ cat bar in bar
(done)
