命名在分散式檔案系統中扮演重要角色。幾乎所有系統的名稱都組織成階層式名稱空間。以下仍以 NFS 為代表,說明分散式檔案系統常見的命名處理方式。

NFS 的命名#

NFS 命名模型的核心想法:讓用戶端完全透明地存取伺服器維護的遠端檔案系統。做法是讓用戶端把遠端檔案系統**掛載(mount)進自己的本地檔案系統。NFS 不必掛載整個檔案系統,可只掛載其中一部分:伺服器把某個目錄及其項目開放給用戶端使用,稱為匯出(export)**該目錄;被匯出的目錄可掛載進用戶端的本地名稱空間。

使用者不共享名稱空間#

這個設計有一項嚴重的隱含後果:原則上使用者彼此不共享名稱空間。同一個檔案,在用戶端 A 可能叫 /remote/vu/mbox,在用戶端 B 卻叫 /work/me/mbox——檔名取決於各用戶端如何組織自己的本地名稱空間、把匯出目錄掛在哪裡。

圖 11-11:在 NFS 中掛載遠端檔案系統(的一部分)。

這使得檔案共享變得困難:Alice 沒辦法用她自己取的名稱告訴 Bob 某個檔案在哪,因為那個名稱在 Bob 的名稱空間可能意義完全不同。最常見的解法是提供部分標準化的名稱空間:例如所有用戶端都用 /usr/bin 掛載一套人人可用的標準程式集合,用 /local 掛載用戶端主機上的本地檔案系統。

巢狀掛載的限制#

NFS 伺服器自己也可以掛載其他伺服器匯出的目錄,但不允許把這些目錄再匯出給自己的用戶端;用戶端必須自行向維護該目錄的伺服器明確掛載。這項限制部分出於簡單性考量。

假設伺服器 A 的檔案系統 FS_A 匯出 /packages,其中子目錄 /draw 是伺服器 B 匯出、由 A 掛載的檔案系統 FS_B 的掛載點;再假設用戶端把 /packages 掛載到本地 /bin。若名稱解析是逐步(iterative)進行(NFSv3 即如此),解析 /bin/draw/install 時,用戶端向 A 要求 /draw 的 file handle——此時 A 必須回傳一個內含伺服器 B 識別資訊的 file handle,因為只有 B 能解析剩下的 /install。NFS 不支援這種 file handle。

圖 11-12:在 NFS 中掛載來自多台伺服器的巢狀目錄。

逐步與遞迴解析#

  • NFSv3(含之前版本)的名稱解析嚴格逐步:一次只能查找一個檔名,解析 /bin/draw/install 需要對伺服器三次個別呼叫,且路徑解析完全由用戶端負責實作。
  • NFSv4 另外支援遞迴查找(recursive lookup):用戶端可把完整路徑交給伺服器,請它一次解析。

v3 還有一個查找上的怪癖到 v4 才解決:若一台伺服器上有多個檔案系統,v3 嚴格逐步解析下,查找某個掛有其他檔案系統的目錄時,回傳的是原目錄的 file handle——接著讀該目錄會拿到原本的內容,而非掛載其上的檔案系統根目錄內容。沿用前例,假設 FS_A 與 FS_B 都在同一台伺服器:用戶端查 draw 拿到的是 FS_A 中 /packages/draw 原始儲存的目錄項目;除非用戶端自己也掛載了 FS_B,否則無法正確解析 draw/install。NFSv4 允許查找在伺服器端跨越掛載點:lookup 回傳被掛載目錄(而非原目錄)的 file handle,用戶端檢查檔案系統識別碼即可偵測跨越了掛載點,需要的話再把該檔案系統也掛到本地。

檔案控制代碼(File Handles)#

file handle 是檔案在檔案系統內的參考,與檔名無關。重點性質:

  • 由存放該檔案系統的伺服器建立,在該伺服器匯出的所有檔案系統範圍內唯一,於檔案建立時產生。
  • 對用戶端完全不透明(opaque):用戶端不知其內容。長度則不是祕密:v2 固定 32 位元組,v3 可變最長 64 位元組,v4 為 128 位元組。
  • 理想上是檔案相對於檔案系統的真識別碼:只要檔案存在,file handle 就維持不變。這種持久性讓用戶端可在查過名稱後把 handle 存在本地。

本地儲存 file handle 有兩個好處:其一是效能——多數檔案操作用的是 handle 而非名稱,可免去每次操作前重複查名;其二是用戶端可獨立於檔案(當下的)名稱來存取它。

正因用戶端會把 file handle 存在本地,伺服器刪除檔案後不得重複使用其 file handle,否則用戶端可能用舊 handle 誤存取到別的檔案。

另外,「逐步查找」加上「lookup 不能跨掛載點」還帶來初始 file handle 的問題:用戶端要存取遠端檔案系統,必須先給伺服器一個「查找起點目錄」的 handle。NFSv3 靠獨立的**掛載協定(mount protocol)**解決:掛載完成後用戶端拿回被掛載檔案系統的根 file handle,作為後續查名的起點。NFSv4 則提供 putrootfh 操作,告訴伺服器所有檔名都相對於其管理的檔案系統根 handle 來解析;如此不再需要獨立的掛載協定,掛載可整合進一般的檔案查找協定。

自動掛載(Automounting)#

NFS 命名模型還有一個問題:遠端檔案系統該在何時掛載?假設大型系統中每個使用者都有本地目錄 /home 用來掛載各使用者的家目錄——Alice 登入時自動掛載她自己的 /home/alice 沒有問題,但 Bob 的家目錄也要在 Alice 登入時一併掛載嗎?好處是掛載對 Alice 完全透明,但若對每個使用者都這麼做,登入會引發大量通訊與管理負擔,還要求事先知道所有使用者。更好的做法是用到時才透明地掛載(on demand)

隨需掛載由**自動掛載器(automounter)**處理,它是用戶端機器上的獨立行程。以實作成使用者層級 NFS 伺服器的簡單 automounter 為例:

  • 用戶端開機時,automounter 先掛載 /home。此後程式一存取 /home,UNIX 核心就把 lookup 轉給 NFS client,NFS client 再把請求轉給扮演 NFS 伺服器角色的 automounter。
  • Alice 登入時,登入程式讀取 /home/alice;automounter 收到查找請求後,先在 /home 建立子目錄 /alice,再查出匯出 Alice 家目錄的 NFS 伺服器,把該目錄掛載到 /home/alice,登入程式即可繼續。

圖 11-13:一個簡單的 NFS 自動掛載器。

這種做法的問題是:為了保證透明性,automounter 必須介入所有檔案操作——包括已掛載檔案系統的每次讀寫,否則無從得知被參考的檔案是否因尚未掛載而不在本地。這可能造成很大的效能問題。較好的方式是讓 automounter 只負責掛載/卸載,其餘時候完全置身事外。

簡單的解法是讓 automounter 把目錄掛在特殊子目錄下,再對每個已掛載目錄安裝符號連結。例如把使用者家目錄掛在 /tmp_mnt 之下:Alice 登入時,automounter 把她的家目錄掛到 /tmp_mnt/home/alice,並建立指向它的符號連結 /home/alice。此後 Alice 執行 ls -l /home/alice 之類的命令時,直接聯絡匯出其家目錄的 NFS 伺服器,automounter 不再介入。

圖 11-14:在自動掛載中使用符號連結。

建構全域名稱空間#

大型分散式系統常由多個既有(legacy)系統黏合而成;要提供檔案共享,全域名稱空間差不多是最起碼想要的黏合劑。現況是檔案系統多半用 FTP 這類原始手段開放共享(Grid 計算普遍如此);真正的廣域分散式檔案系統做法更精緻,卻常需修改作業系統核心才能被採用。因此研究者尋求只用使用者層級的解法把既有檔案系統整合進單一全域名稱空間,**Global Name Space Service(GNS)**即為一例。

GNS 不提供檔案存取介面,只提供把多個既有名稱空間合併成一個全域名稱空間的手段:

  • GNS 用戶端維護一棵虛擬樹,每個節點是目錄或接合點(junction)。junction 是特殊節點,表示名稱解析要交由另一個行程接手,性質類似傳統檔案系統的掛載點。
  • junction 共五種類型:GNS junction 指向另一個 GNS 實例(另一棵可能由別的行程承載的虛擬樹);兩種邏輯(logical)junction 內含聯絡定位服務(location service)所需的資訊,由定位服務分別提供存取某檔案系統或某檔案的聯絡位址;實體檔案系統名稱(physical file-system name)指向另一伺服器上的檔案系統,大致等同邏輯 junction 需要的聯絡位址——例如 ftp://ftp.cs.vu.nl/pub 這種 URL 就含有存取該 FTP 伺服器檔案的全部資訊;同理,指向單一檔案的 URL(如某網頁)是實體檔案名稱(physical file name)的典型例子。
  • junction 必須內含繼續名稱解析所需的一切資訊。檔案系統種類繁多,每種 junction 都需要自己的實作;所幸存取遠端檔案的常見方式也不少,包括與 NFS 伺服器、FTP 伺服器及 Windows 機器(特別是 CIFS)溝通的協定。

圖 11-15:GNS 中的接合點(junction)。

GNS 的優點是把檔案命名與實際位置解耦:虛擬樹與檔案、目錄的實體擺放位置毫無關聯。此外透過定位服務,檔案搬移後名稱依然可解析——只需把新的實體位置註冊到定位服務即可。