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);
}