1. 程式人生 > >Unix檔案系統(一)

Unix檔案系統(一)

此文著重介紹系統是如何表示目錄的以及pwd命令的編寫。

我們都知道Unix將磁碟分為三部分:超級塊(superblock),節點表(inode table)以及資料區。超級塊中記錄檔案系統本身的結構資訊。節點表中記錄檔案的屬性,檔案系統中每個檔案在表中都至少有一個i-節點,表中每個節點的大小相同。資料區就是檔案資料真實的儲存位置。

簡要介紹磁碟知識後,我們來談談目錄。在Unix中,目錄是包含了檔名字列表的特殊檔案,其抽象模型是一個包含i-節點號和檔名的表。所以一個目錄的內容有兩個部分:一個i-節點表和檔案名錶,其相互對應。對於檔案來說,目錄是找到該檔案的一個索引(連結),本質上就是負責記錄檔案i-節點表與檔名的對應關係(一個檔案可能對應好多個i-節點)。對於open、cat、cp等命令,都是通過在目錄中尋找目的檔名,然後根據對應關係獲取i-節點號以獲取檔案屬性,最終獲取檔案內容來實現的。

假設目錄demodir中有一個檔案y,y檔案對應的i-節點號是491。從系統角度來看,目錄中有一個包含檔名y和i-節點號為491的入口,也可以說目錄demodir中有一個指向i-節點號為491的連結,這個連結所附加的檔名為y。

說來說去這麼繞,書上有一句結論概括地很好:目錄包含的是檔案的引用,每個引用被成為連結。檔案的內容儲存在資料塊,檔案的屬性被記錄在一個被稱為i-節點的結構中,i-節點的編號和檔名儲存在目錄中。就像我前面說的,目錄本質上是記錄對應關係。

瞭解了pwd的核心屬性後,我們來實現一下pwd。
此程式中使用遞迴語句實現,從當前目錄不斷迴圈深入直到”.”與”..”的i-節點號相同,即代表到達根目錄,再由內向外打印出資料夾名。
邏輯不難理解,有一點需要注意,通過stat結構體我們可以獲得”.”與”..”目錄的i-節點號,我們需要通過節點號獲取資料夾名。有點類似於一個trick,通過獲得父目錄的DIR結構體,取出父目錄中包含當前目錄在內的檔案及資料夾的dirent結構體,通過比對結構體中d_ino欄位與當前目錄的inode,找到此inode所對應的的d_name即可。因此,程式中chdir必須在num_to_name之前。

/*pwd.c          -print the directory where you in*/

#include "stdio.h"
#include "string.h"
#include "dirent.h"
#include "sys/stat.h"
#include "unistd.h"
#include "stdlib.h"

#define SIZE 100

ino_t get_inode(char *path);
void print_path(ino_t cur_inode);
void num_to_name(ino_t inode, char *name);

int
main(int argc, char const *argv[]) { ino_t cur_inode; cur_inode = get_inode("."); print_path(cur_inode); printf("\n"); return 0; } ino_t get_inode(char *path){ struct stat info; if(stat(path, &info) == -1){ perror("Get Inode"); exit(1); } return info.st_ino; } void print_path(ino_t cur_inode){ char dname[SIZE]; ino_t next_inode; if(cur_inode != get_inode("..")){ chdir(".."); num_to_name(cur_inode, dname); //必須放在chdir之後,要從父目錄中找到innode和資料夾名的對應關係 next_inode = get_inode("."); print_path(next_inode); printf("/%s", dname); } } void num_to_name(ino_t inode, char *name){ DIR *dir_ptr; struct dirent *direntp; dir_ptr = opendir("."); if(dir_ptr == NULL){ perror("opendir"); exit(1); } while((direntp = readdir(dir_ptr)) != NULL){ if(direntp->d_ino == inode) { strcpy(name, direntp->d_name); closedir(dir_ptr); return; } } perror("Can not fine with the innode"); exit(1); }