最近在學(xué)習(xí)Unix下系統(tǒng)編程,書看的還比較仔細(xì),但是合上書后總是有種霧里看花朦朦朧朧的感覺。俗話說實踐出真知,學(xué)習(xí)編程怎么能不動手呢。既然是學(xué)習(xí)系統(tǒng)編程那就寫一些系統(tǒng)命令來鞏固知識,消除朦朧的感覺吧!選中PWD命令,有如下幾個原因:
1、 可以加深對Linux文件系統(tǒng)組織結(jié)構(gòu)的理解
2、
可以加深對目錄結(jié)構(gòu)的理解
3、 可以加深對掛載點和鏈接的理解
注:關(guān)于LINUX文件系統(tǒng)的相關(guān)基礎(chǔ)知識,大家可以先看看《Linux文件系統(tǒng)詳解》:http://www.armjishu.com/bbs/viewtopic.php?id=1754
&flag=1578
Unix下一切皆文件,也就是說掌握好了文件也就掌握了Unix的一切(菜鳥說法不必當(dāng)真)。掌握了上述三個知識點也就算基本掌握了Unix文件系統(tǒng)的相關(guān)知識。有點遺憾的是文件狀態(tài)和權(quán)限相關(guān)知識在這個例子中沒有涉及。選好了命令下一步就是寫了,下面是我的寫作計劃:
1、
Unix的文件系統(tǒng)的內(nèi)部結(jié)構(gòu),主要是超級塊、inode相關(guān)知識
2、 目錄結(jié)構(gòu),當(dāng)然是目錄相關(guān)知識了
3、 編寫PWD命令
PWD命令的作用就是顯示當(dāng)前工作目錄的絕對路徑,就是從/開始到當(dāng)前目錄。用法很簡單,沒有其他參數(shù)。在命令行敲入pwd:
pwd命令.jpg
1、Unix文件系統(tǒng)的內(nèi)部結(jié)構(gòu)
文件系統(tǒng)可以用來存儲文件內(nèi)容、文件屬性(所有者、日期等)和目錄,這些不同類型的數(shù)據(jù)是如何存儲在磁盤上的呢?Linux使用了一個簡單的辦法。如下圖所示它將磁盤分成了3部分:超級塊、inode表和數(shù)據(jù)區(qū)。
文件的內(nèi)部結(jié)構(gòu)
超級塊,通常是文件系統(tǒng)的第一個塊,用來存放文件系統(tǒng)本身的信息。例如每個區(qū)域的大小、未被使用的磁盤塊的信息等;不同版本的Unix的超級塊的內(nèi)容和結(jié)構(gòu)稍有不同。
inode表,每個文件都有一些屬性,如大小、文件所有者和最近修改時間等。這些屬性被記錄在inode表中,所有的inode都有相同的大小。文件系統(tǒng)中的每個文件都對應(yīng)一個inode;
數(shù)據(jù)區(qū),文件內(nèi)容的內(nèi)容保存在這個區(qū)域。磁盤上所有塊的大小都是一樣的,如果文件包含了超過一個塊的內(nèi)容,則文件內(nèi)容會存放在多個磁盤塊中。
當(dāng)創(chuàng)建一個新文件時,內(nèi)核將文件內(nèi)容存放在數(shù)據(jù)區(qū),文件屬性存放在inode中,文件名存放在目錄中。當(dāng)對一個文件進行相關(guān)操作時,內(nèi)核先在目錄中找到
文件名,然后根據(jù)目錄中inode獲得文件的屬性,最終找到文件的內(nèi)容。從上述過程可以看出,目錄至少需要包含inode和文件名。
2、目錄結(jié)構(gòu)
用戶看到的文件系統(tǒng)是目錄和子目錄的集合,也就是目錄樹。每個目錄能夠包含文件和子目錄,每個子目錄有一個父目錄,這棵樹的結(jié)構(gòu)常用線條連接的方框圖來
表示。在文件系統(tǒng)內(nèi)部,目錄是一個包含文件名與inode節(jié)點對的列表的文件。從用戶角度看到的是一個文件名的列表,而從系統(tǒng)的角度看到的是一個被命名的
指針的列表。如下圖所示:
目錄樹的兩種不同視圖
一般我們都說文件存放在某個目錄中,但是由上面的分析可知目錄中存放的只是文件在inode表的入口,而文件內(nèi)容則存儲在數(shù)據(jù)區(qū)。'文件x在目錄a中
',意味著在目錄a中有一個指向inode402的鏈接,這個鏈接所附加的文件名為x。簡短的說,目錄包含的是文件的引用,每個引用被稱為鏈接。
鏈接有兩種,一種是硬鏈接,另一種是符號鏈接。這里需要注意的一點是Unix下文件沒有文件名,但是鏈接有名字,通常所說的文件名其實就是鏈接名。硬鏈
接就是把鏈接名和inode鏈接起來,也就是將文件名和文件本身鏈接起來。符號鏈接就是通過名字引用文件,而不是inode,也就是將名字和名字鏈接起
來。
從圖1我們可以看到,在系統(tǒng)角度,每個目錄都有兩個鏈接:'.'和'..'。這兩個鏈接是干什么用的呢?內(nèi)核在每個目錄都設(shè)置了一個指向目錄本身的inode,這個入口被稱為'.'。'..',是指向父目錄inode的入口。
3、編寫PWD命令
3.1、PWD分析
通過前面基礎(chǔ)知識的學(xué)習(xí),我們知道了Unix文件系統(tǒng)是樹形結(jié)構(gòu),節(jié)點被稱為inode,指針的集合被稱為目錄,葉子節(jié)點被稱為鏈接。那么要想取得當(dāng)前
工作目錄的絕對路徑只需追蹤鏈接,讀取目錄,一個目錄接著一個目錄沿著樹向上追蹤,每步查看'.'的inode,然后在父目錄中查找該inode的名字,
直到到達樹的根部。這就是PWD命令的工作原理,知道了原理那么就可以得出PWD的實現(xiàn)算法了:
1、得到'.'的inode,稱其為n(使用stat)
2、進入'..'(使用chdir)
3、找到節(jié)點號為n的inode鏈接的名字(使用opendir、readdir、closedir)
4、重復(fù)上述三個步驟,直到到達書的根部
這里需要注意兩個問題,第一個問題就是如何知道已經(jīng)到達樹的頂端。在Unix
文件系統(tǒng)的根目錄中,'.' 和 '..'指向同一個inode。因此當(dāng)PWD命令重復(fù)循環(huán)直到一個目錄的'.' 和
'..'的inode相同時,就可以認(rèn)為已經(jīng)到達文件樹的根部了。第二個問題就是如何以正確的順序顯示目錄名字。學(xué)過算法的同學(xué)都知道,處理這種問題用遞歸再合適不過了。通過一個遞歸的程序逐步到達樹的頂端來一個接一個地顯示目錄名,從而避免了字符串的管理。
3.2、相關(guān)數(shù)據(jù)結(jié)構(gòu)和系統(tǒng)調(diào)用
在動手寫代碼之前需要了解一下兩個數(shù)據(jù)結(jié)構(gòu):DIR和struct
dirent。DIR是一個不透明的結(jié)構(gòu),每個平臺都有自己的DIR實現(xiàn),用戶不需要明白DIR的具體結(jié)構(gòu)。也就是說,看到源碼中的DIR就只需要明白這是個目錄指針就可以了。struct
dirent是一個目錄入口的結(jié)構(gòu),在Linux下定義如下:
- struct dirent{
- long d_ino;
- off_t d_off;
- unsigned short d_reclen;
- unsigned char d_type;
- char d_name [NAME_MAX+1];
- }
與目錄操作有關(guān)的函數(shù)在dirent.h頭文件中聲明。它們以DIR結(jié)構(gòu)為目錄操作的基礎(chǔ),其使用方法與文件流(FILE *)類似。目錄數(shù)據(jù)項本身在struct
dirent結(jié)構(gòu)中返回。我們實現(xiàn)PWD命令需要用到的目錄操作有opendir、readdir和closedir,下面就介紹下這三個函數(shù):
DIR
*opendir(const char
*name)
opendir函數(shù)用來打開一個目錄并創(chuàng)建一個目錄流,如果成功則返回一個指向DIR結(jié)構(gòu)的指針,該指針用于讀取目錄數(shù)據(jù)項。
struct
dirent *readdir(DIR
*dirp)
readdir函數(shù)返回一個指針,指針指向的結(jié)構(gòu)保存著目錄流dirp中下一個目錄項的相關(guān)資料。后續(xù)的readdir調(diào)用將返回后續(xù)的目錄項。
int
closedir(DIR
*dirp)
closedir函數(shù)用來關(guān)閉一個目錄流并釋放與之相關(guān)的資源,成功返回0,失敗返回-1。
3.3、編譯運行
進入到存放spwd.c文件的目錄下,使用gcc編譯源文件,生成可執(zhí)行文件(我這里命名為spwd),然后把可執(zhí)行文件拷貝到/bin/下就可以像使用pwd命令一樣使用自己寫的pwd命令了。
112.JPG
原文: http://blog.csdn.net/humchx/archive/2009/09/04/4517828.aspx