第二十九章: 網絡文件系統

29.1 引言

本章中我們要討論另一個常用的應用程序:NFS(網絡文件系統),它為客戶程序提供透明的文件訪問。NFS的基礎是Sun RPC:遠程過程調用。我們首先必須描述一下RPC。

客戶程序使用NFS不需要做什么特別的工作,當NFS內核檢測到被訪問的文件位于一個NFS服務器時,就會自動產生一個訪問該文件的RPC調用。

我們對NFS如何訪問文件的細節并不感興趣,只對它如何使用Internet的協議,尤其是UDP協議,感興趣。

29.2 Sun遠程過程調用

大多數的網絡程序設計都是編寫一些調用系統提供的函數來完成特定的網絡操作的應用程序。例如,一個函數完成TCP的主動打開,另一個完成TCP的被動打開,一個函數在一個TCP連接上發送數據,另一個設置特定的協議選項(如激活TCP的keepalive定時器)。在1.15節我們提到過兩個常用的用于網絡編程的函數集(API):插口(socket)和TLI。正像客戶端和服務器端運行的操作系統可能會不相同一樣,雙方使用的API也可能會不相同。由通信協議和應用協議決定一對客戶和服務器是否可以彼此通信。如果兩臺主機連接在一個網絡上,并且都有一個TCP/IP的實現,那么一臺主機上的一個使用C語言編寫的、使用插口和TCP的Unix客戶程序可以和另一臺主機上的一個使用COBOL語言編寫的、使用其他API和TCP的大型機服務器進行通信。

一般來說,客戶發送命令給服務器,服務器向客戶發送應答。目前為止,我們討論過的所有應用程序—Ping,Traceroute,選路守護程序、以及DNS、TFTP、BOOTP、SNMP、Telnet、FTP和SMTP的客戶和服務器—都是采用這種方式實現的。

遠程過程調用RPC(Remote Procedure Call)是一種不同的網絡程序設計方法。客戶程序編寫時只是調用了服務器程序提供的函數。這只是程序員所感覺到的,實際上發生了下面一些動作。

1:當客戶程序調用遠程的過程時,它實際上只是調用了一個位于本機上的、由RPC程序包生成的函數。這個函數被稱為客戶殘樁(stub)??蛻魵垬秾⑦^程的參數封裝成一個網絡報文,并且將這個報文發送給服務器程序。

2:服務器主機上的一個服務器殘樁負責接收這個網絡報文。它從網絡報文中提取參數,然后調用應用程序員編寫的服務器過程。

3:當服務器函數返回時,它返回到服務器殘樁。服務器殘樁提取返回值,把返回值封裝成一個網絡報文,然后將報文發送給客戶殘樁。

4:客戶殘樁從接收到的網絡報文中取出返回值,將其返回給客戶程序。

網絡程序設計是通過殘樁和使用諸如插口或TLI的某個API的RPC庫例程來實現的,但是用戶程序—客戶程序和被客戶程序調用的服務器過程—不會和這個API打交道。客戶應用程序只是調用服務器的過程,所有網絡程序設計的細節都被RPC程序包、客戶殘樁和服務器殘樁所隱藏。

一個RPC程序包提供了很多好處。

1:程序設計更加容易,因為很少或幾乎沒有涉及網絡編程。應用程序設計員只需要編寫一個客戶程序和客戶程序調用的服務器過程。

2:如果使用了一個不可靠的協議,如UDP,像超時和重傳等細節就由RPC程序包來處理。這就簡化了用戶應用程序。

3:RPC庫為參數和返回值的傳輸提供任何需要的數據轉換。例如,如果參數是由整數和浮點數組成的,RPC程序包處理整數和浮點數在客戶機和服務器主機上存儲的不同形式。這個功能簡化了在異構環境中的客戶和服務器的編碼問題。

RPC程序設計的細節可以參看參考文獻[Stevens 1990]的第18章。兩個常用的RPC程序包是Sun RPC和開放軟件基金(OSF)分布式計算環境(DCE)的RPC程序包。我們對于RPC的興趣在于想了解Sun RPC中過程調用和過程返回報文的形式,因為本章中討論的網絡文件系統使用了它們。Sun RPC的第2版定義在RFC 1057 [Sun Microsystems 1988a]中。

Sun RPC

SunRPC有兩個版本。一個版本建立在插口API基礎上,和TCP和UDP打交道。另一個稱為TI-RPC的(獨立于運輸層),建立在TLIAPI基礎上,可以和內核提供的任何運輸層協議打交道。盡管本章中我們只討論TCP和UDP,從討論的觀點來看,兩者是一樣的。

圖29-1顯示的是使用UDP時,一個RPC過程調用報文的格式。IP首部和UDP首部是標準的首部,我們已經在圖3-1和圖11-2中顯示過。UDP首部以下是RPC程序包定義的部分。

圖29-1 RPC過程調用報文作為一個UDP數據報的格式

事務標識符(XID)由客戶程序設置,由服務器程序返回。當客戶收到一個應答,它將服務器返回的XID與它發送的請求的XID相比較。如果不匹配,客戶就放棄這個報文,等待從服務器返回的下一個報文。每次客戶發出一個新的RPC,它就會改變報文的XID。但是如果客戶重傳一個以前發送過的RPC(因為它沒有收到服務器的一個應答),重傳報文的XID不會修改。

調用(call)變量在過程調用報文中設置為0,在應答報文中設置為1。當前的RPC版本是2。接下來三個變量:程序號、版本號和過程號,標識了服務器上被調用的特定過程。

證書(credential)字段標識了客戶。有些情況下,證書字段設置為空值;另外一些情況下,證書字段設置為數字形式的客戶的用戶號和組號。服務器可以查看證書字段以決定是否執行請求的過程。驗證(verifier)字段用于使用了DES加密的安全RPC。盡管證書字段和驗證字段是可變長度的字段,它們的長度也作為字段的一部分被編碼。

接下來是過程參數(procedure parameter)字段。參數的格式依賴于遠程過程的定義。接收者(服務器殘樁)如何知道參數字段的大小呢?既然使用的是UDP協議,UDP數據報的大小減去驗證字段以上所有字段的長度就是參數的大小。如果使用的不是UDP而是TCP,因為TCP是一個字節流協議,沒有記錄邊界,所以沒有固定的長度。為了解決這個問題,在TCP首部和XID之間增加了一個4字節的長度字段,告訴接收者這個RPC調用由多少字節組成。這也使得一個RPC調用報文在必要時可以用多個TCP段來傳輸(DNS使用了類似的技術,參見習題14-4)。

圖29-2顯示了一個RPC應答報文的格式。當遠程過程返回時,服務器殘樁將這個報文發送給客戶殘樁。

圖29-2 RPC應答報文作為一個UDP數據報的格式

應答報文中的XID字段是從調用報文的XID字段復制而來。應答字段設置為1,以區別于調用報文。如果調用報文被接受,狀態字段設置為0(如果RPC的版本號不為2,或者服務器不能鑒別客戶的身份,調用報文可能被拒絕)。安全的RPC使用驗證字段來標識服務器。

如果遠程過程調用成功,接受狀態字段置為0。一個非零的值可能表示一個不合法的版本號或者一個不合法的過程號。如果使用的不是UDP而是TCP,如同RPC調用報文一樣,在TCP首部和XID字段之間插入一個4字節的長度字段。

29.3 XDR:外部數據表示

外部數據表示XDR(eXternal Data Representation)是一個標準,用來對RPC調用報文和應答報文中的值進行編碼。這些值包括RPC首部字段(XID、程序號、接受狀態等)、過程參數和過程結果。采用標準化的方法對這些值進行編碼使得一個系統中的客戶可以調用另一個不同架構的系統中的一個過程。XDR在RFC 1014中定義[Sun Microsystems 1987]。

XDR定義了很多數據類型以及它們如何在一個RPC報文中傳輸的具體形式(如比特順序,字節順序等)。發送者必須采用XDR格式構造一個RPC報文,然后接收者將XDR格式的報文轉換為本機的表示形式。例如,在圖29-1和圖29-2中,我們顯示的所有整數值(XID、調用字段、程序號等)都是4字節的整數。在XDR中,所有的整數的確占據4個字節。XDR支持的其他數據類型包括無符號整數、布爾類型、浮點數、定長數組、可變長數組和結構。

29.4 端口映射器

包含遠程過程的RPC服務器程序使用的是臨時端口,而不是知名端口。這就需要某種形式的“注冊”程序來跟蹤哪一個RPC程序使用了哪一個臨時端口。在Sun RPC中,這個注冊程序被稱為端口映射器(port mapper)。

“端口”這個詞作為Internet協議族的一個特征,來自于TCP和UDP端口號。既然TIRPC可以工作在任何運輸層協議之上,而不僅僅是TCP和UDP,所以使用TI-RPC的系統中(如SVR4和Solaris 2.2),端口映射器的名字變成了rpcbind。下面我們繼續使用更為常見的端口映射器的名字。

很自然地,端口映射器本身必須有一個知名端口:UDP端口111和TCP端口111。端口映射器也就是一個RPC服務器程序。它有一個程序號(100000)、一個版本號(2)、一個TCP端口111和一個UDP端口111。服務器程序使用RPC調用向端口映射器注冊自身,客戶程序使用RPC調用向端口映射器查詢。端口映射器提供四個服務過程:

1:PMAPPROC_SET。一個RPC服務器啟動時調用這個過程,注冊一個程序號、版本號和帶有一個端口號的協議。

2:PMAPPROC_UNSET。RPC服務器調用此過程來刪除一個已經注冊的映射。

3:PMAPPROC_GETPORT。一個RPC客戶啟動時調用此過程。根據一個給定的程序號、版本號和協議來獲得注冊的端口號。

4:PMAPPROC_DUMP。返回端口映射器數據庫中所有的記錄(每個記錄包括程序號、版本號、協議和端口號)。

在一個RPC服務器程序啟動,接著被一個RPC客戶程序調用的過程中,進行了以下一些步驟:

1:一般情況下,當系統引導時,端口映射器必須首先啟動。它創建一個TCP端點,并且被動打開TCP端口111。它也創建一個UDP端點,并且在UDP端口111等待著UDP數據報的到來。

2:當RPC服務器程序啟動時,它為它所支持的程序的每一個版本創建一個TCP端點和一個UDP端點(一個給定的RPC程序可以支持多個版本??蛻粽{用一個服務器過程時,說明它想要哪一個版本)。兩個端點各自綁定一個臨時端口(TCP端口號和UDP端口號是否一致無關緊要)。服務器通過RPC調用端口映射器的PMAPPROC_SET過程,注冊每一個程序、版本、協議和端口號。

3:當RPC客戶程序啟動時,它調用端口映射器的PMAPPROC_GETPORT過程來獲得一個指定程序、版本和協議的臨時端口號。

4:客戶發送一個RPC調用報文給第3步返回的端口號。如果使用的是UDP,客戶只是發送一個包含RPC調用報文(見圖29-1)的UDP數據報到服務器相應的UDP端口。服務器發送一個包含RPC應答報文(見圖29-2)的UDP數據報到客戶作為響應。

如果使用的是TCP,客戶對服務器的TCP端口號做一個主動打開,然后在建立的TCP連接上發送一個RPC調用報文。服務器作為響應,在連接上發送一個RPC應答報文。

程序rpcinfo(8)打印了端口映射器中當前的映射記錄(它調用了端口映射器的PMAPPROC_DUMP過程)。這里給出的是典型的輸出:


可以看出一些程序確實支持多個版本。在端口映射器中,每一個程序號、版本號和協議的組合都有自己的端口號映射。

安裝守護程序(mount daemon)的兩個版本可以通過同樣的TCP端口號(702)和同樣的UDP端口號(699)來訪問,而加鎖管理程序(lock manager)的每個版本都有各自不同的端口號。

29.5 NFS協議

使用NFS,客戶可以透明地訪問服務器上的文件和文件系統。這不同于提供文件傳輸的FTP(第27章)。FTP會產生文件一個完整的副本。NFS只訪問一個進程引用文件的那一部分,并且NFS的一個目的就是使得這種訪問透明。這就意味著任何能夠訪問一個本地文件的客戶程序不需要做任何修改,就應該能夠訪問一個NFS文件。

NFS是一個使用Sun RPC構造的客戶服務器應用程序。NFS客戶通過向一個NFS服務器發送RPC請求來訪問其上的文件。盡管這一工作可以使用一般的用戶進程來實現—即NFS客戶可以是一個用戶進程,對服務器進行顯式調用。而服務器也可以是一個用戶進程—因為兩個理由,NFS一般不這樣實現。首先,訪問一個NFS文件必須對客戶透明。因此,NFS的客戶調用是由客戶操作系統代表用戶進程來完成的。第二,出于效率的考慮,NFS服務器在服務器操作系統中實現。如果NFS服務器是一個用戶進程,每個客戶請求和服務器應答(包括讀和寫的數據)將不得不在內核和用戶進程之間進行切換,這個代價太大。

本節中,我們考察在RFC1094中說明的第2版的NFS [Sun Microsystems 1988b]。[X/Open1991]中給出了Sun RPC、XDR和NFS的一個更好的描述。[Stern 1991]給出了使用和管理NFS的細節。第3版的NFS協議在1993年發布,我們在29.7節中對它做一個簡單的描述。

圖29-3顯示了一個NFS客戶和一個NFS服務器的典型配置,圖中有很多地方需要注意。

圖29-3 NFS客戶和NFS服務器的典型配置

1:訪問的是一個本地文件還是一個NFS文件對于客戶來說是透明的。當文件被打開時,由內核決定這一點。文件被打開之后,內核將本地文件的所有引用傳遞給名為“本地文件訪問”的框中,而將一個NFS文件的所有引用傳遞給名為“NFS客戶”的框中。

2:NFS客戶通過它的TCP/IP模塊向NFS服務器發送RPC請求。NFS主要使用UDP,最新的實現也可以使用TCP。

3:NFS服務器在端口2049接收作為UDP數據報的客戶請求。盡管NFS可以被實現成使用端口映射器,允許服務器使用一個臨時端口,但是大多數的實現都是直接指定UDP端口2049。

4:當NFS服務器收到一個客戶請求時,它將這個請求傳遞給本地文件訪問例程,后者訪問服務器主機上的一個本地的磁盤文件。

5:NFS服務器需要花一定的時間來處理一個客戶的請求。訪問本地文件系統一般也需要一部分時間。在這段時間間隔內,服務器不應該阻止其他的客戶請求得到服務。為了實現這一功能,大多數的NFS服務器都是多線程的—即服務器的內核中實際上有多個NFS服務器在運行。具體怎么實現依賴于不同的操作系統。既然大多數的Unix內核不是多線程的,一個共同的技術就是啟動一個用戶進程(常被稱為nfsd)的多個實例。這個實例執行一個系統調用,使自己作為一個內核進程保留在操作系統的內核中。

6:同樣,在客戶主機上,NFS客戶需要花一定的時間來處理一個用戶進程的請求。NFS客戶向服務器主機發出一個RPC調用,然后等待服務器的應答。為了給使用NFS的客戶主機上的用戶進程提供更多的并發性,在客戶內核中一般運行著多個NFS客戶。同樣,具體實現也依賴于操作系統。Unix系統經常使用類似于NFS服務器的技術:一個叫作biod的用戶進程執行一個系統調用,作為一個內核進程保留在操作系統的內核中。

大多數的Unix主機可以作為一個NFS客戶,一個NFS服務器,或者兩者都是。大多數PC機的實現(MS-DOS)只提供了NFS客戶實現。大多數的IBM大型機只提供了NFS服務器功能。

NFS實際上不僅僅由NFS協議組成。圖29-4顯示了NFS使用的不同RPC程序。

圖29-4 NFS使用的不同RPC程序
在這個圖中,程序的版本是在SunOS 4.1.3中使用的。更新的實現提供了其中一些程序更新的版本。例如,Solaris 2.2還支持端口映射器的第3版和第4版,以及安裝守護程序的第2版。SVR4支持第3版的端口映射器。

在客戶能夠訪問服務器上的文件系統之前,NFS客戶主機必須調用安裝守護程序。我們在下面討論安裝守護程序。

加鎖管理程序和狀態監視器允許客戶鎖定一個NFS服務器上文件的部分區域。這兩個程序獨立于NFS協議,因為加鎖需要知道客戶和服務器的狀態,而NFS本身在服務器上是無狀態的(下面我們對NFS的無狀態會介紹得更多)。[X/Open 1991]的第9,10和11章說明了使用加鎖管理程序和狀態監視器進行NFS文件鎖定的過程。

29.5.1 文件句柄

NFS中一個基本概念是文件句柄(file handle)。它是一個不透明(opaque)的對象,用來引用服務器上的一個文件或目錄。不透明指的是服務器創建文件句柄,把它傳遞給客戶,然后客戶訪問文件時,使用對應的文件句柄??蛻舨粫榭次募浔膬热荨膬热葜粚Ψ掌饔幸饬x。

每次一個客戶進程打開一個實際上位于一個NFS服務器上的文件時,NFS客戶就會從NFS服務器那里獲得該文件的一個文件句柄。每次NFS客戶為用戶進程讀或寫文件時,文件句柄就會傳給服務器以指定被訪問的文件。

一般情況下,用戶進程不會和文件句柄打交道—只有NFS客戶和NFS服務器將文件句柄傳來傳去。在第2版的NFS中,一個文件句柄占據32個字節,第3版中增加為64個字節。

  Unix服務器一般在文件句柄中存儲下面的信息:文件系統標識符(文件系統最大和最小的設備號),i-node號(在一個文件系統中唯一的數值)和一個i-node的生成碼(每當一個i-node被一個不同的文件重用時就改變的數值)。
29.5.2 安裝協議

客戶必須在訪問服務器上一個文件系統中的文件之前,使用安裝協議安裝那個文件系統。一般情況下,這是在客戶主機引導時完成的。最后的結果就是客戶獲得服務器文件系統的一個文件句柄。

圖29-5顯示了一個Unix客戶發出mount(8)命令所發生的情況,它說明一個NFS的安裝過程。

圖29-5 使用Unixmount命令的安裝協議

依次發生了下面的動作。

1:服務器上的端口映射器一般在服務器主機引導時被啟動。

2:安裝守護程序(mountd)在端口映射器之后被啟動。它創建了一個TCP端點和一個UDP端點,并分別賦予一個臨時的端口號。然后它在端口映射器中注冊這些端口號。

3:在客戶機上執行mount命令,它向服務器上的端口映射器發出一個RPC調用來獲得服務器上安裝守護程序的端口號??蛻艉投丝谟成淦鹘换ゼ瓤梢允褂肨CP也可以使用UDP,但一般使用UDP。

4:端口映射器應答以安裝守護程序的端口號。

5:mount命令向安裝守護程序發出一個RPC調用來安裝服務器上的一個文件系統。同樣,既可以使用TCP也可以使用UDP,但一般使用UDP。服務器現在可以驗證客戶,使用客戶的IP地址和端口號來判別是否允許客戶安裝指定的文件系統。

6:安裝守護程序應答以指定文件系統的文件句柄。

7:客戶機上的mount命令發出mount系統調用將第5步返回的文件句柄與客戶機上的一個本地安裝點聯系起來。文件句柄被存儲在NFS客戶代碼中,從現在開始,用戶進程對于那個服務器文件系統的任何引用都將從使用這個文件句柄開始。

上述實現技術將所有的安裝處理,除了客戶機上的mount系統調用,都放在用戶進程中,而不是放在內核中。我們顯示的三個程序—mount命令、端口映射器和安裝守護程序—都是用戶進程。

作為一個例子,在我們的主機sun(一個NFS客戶機)上執行:

sun # mount -t nfs bsdi:/usr /nfs/bsdi/usr

這個命令將主機bsdi(一個NFS服務器)上的/usr目錄安裝成為本地文件系統/nfs/bsdi/usr。圖29-6顯示了結果。

圖29-6 將bsdi:/usr 目錄安裝成主機 sun 上的/nfs/bsdi/usr 目錄

當我們引用客戶機sun上的/nfs/bsdi/usr/rstevens/hello.c文件時,實際上引用的是服務器bsdi上的文件/usr/rstevens/hello.c。

29.5.3 NFS過程

現在我們描述NFS服務器提供的15個過程(使用的個數與NFS過程的實際個數不一樣,因為我們把它們按照功能分了組)。盡管NFS被設計成可以在不同的操作系統上工作,而不僅僅是Unix系統,但是一些提供Unix功能的過程可能不被其他操作系統支持(例如硬鏈接、符號鏈接、組的屬主和執行權等)。[Stevens 1992]的第4章包含了Unix文件系統其他的一些信息,其中有些被NFS采用。

1:GETATTR。返回一個文件的屬性:文件類型(一般文件,目錄等)、訪問權限、文件大小、文件的屬主者及上次訪問時間等信息。

2:SETATTR。設置一個文件的屬性。只允許設置文件屬性的一個子集:訪問權限、文件的屬主、組的屬主、文件大小、上次訪問時間和上次修改時間。

3:STATFS。返回一個文件系統的狀態:可用空間的大小、最佳傳送大小等。例如Unix的df命令使用此過程。

4:LOOKUP。查找一個文件。每當一個用戶進程打開一個NFS服務器上的一個文件時,NFS客戶調用此過程。

5:READ。從一個文件中讀數據??蛻粽f明文件的句柄、讀操作的開始位置和讀數據的最大字節數(最多8192個字節)。

6:WRITE。對一個文件進行寫操作??蛻粽f明文件的句柄、開始位置、寫數據的字節數和要寫的數據。

7:CREATE。創建一個文件。

8:REMOVE。刪除一個文件。

9:RENAME。重命名一個文件。

10:LINK。為一個文件構造一個硬鏈接。硬鏈接是一個Unix的概念,指的是磁盤中的一個文件可以有任意多個目錄項(即名字,也叫作硬鏈接)指向它。

11:SYMLINK。為一個文件創建一個符號鏈接。符號鏈接是一個包含另一個文件名字的文件。大多數引用符號鏈接的操作(例如,打開)實際上引用的是符號鏈接所指的文件。

12:READLINK。讀一個符號鏈接。即返回符號鏈接所指的文件的名字。

13:MKDIR。創建一個目錄。

14:RMDIR。刪除一個目錄。

15:READDIR。讀一個目錄。例如,Unix的ls命令使用此過程。

這些過程實際上有一個前綴NFSPROC_,我們把它省略了。

29.5.4 UDP還是TCP

NFS最初是用UDP寫的,所有的廠商都提供了這種實現。最新的一些實現也支持TCP。TCP支持主要用于廣域網,它可以使文件操作更快。NFS已經不再局限于局域網的使用。

當從LAN轉換到W N時,網絡的動態特征變化得非常大。往返時間(round-trip time)變動范圍大,擁塞經常發生。WAN的這些特征使得我們考慮使用具有TCP屬性的算法——慢啟動,但是可以避免擁塞。既然UDP沒有提供任何類似的東西,那么在NFS客戶和服務器上加進同樣的算法或者使用TCP。

29.5.5 TCP上的NFS

伯克利實現的Net/2NFS支持UDP或者TCP。[Macklem 1991]描述了這個實現。讓我們看一下使用TCP有什么不同。

1:當服務器主機進行引導時,它啟動一個NFS服務器,后者被動打開TCP端口2049,等待著客戶的連接請求。這通常是另一個NFS服務器,正常的NFS UDP服務器在UDP端口2049等待著進入的UDP數據報。

2:當客戶使用TCP安裝服務器上的文件系統時,它對服務器上的TCP端口2049做一個主動打開。這樣就為這個文件系統在客戶和服務器之間形成了一個TCP連接。如果同樣的客戶安裝同樣服務器上的另一個文件系統,就會創建另一個TCP連接。
3:客戶和服務器在它們連接的兩端都要設置TCP的keepalive選項,這樣雙方都能檢測到對方主機崩潰,或者崩潰然后重啟動。

4:客戶方所有使用這個服務器文件系統的應用程序共享這個TCP連接。例如,在圖29-6中,如果在bsdi的/usr目錄下還有另一個目錄smith,那么對兩個目錄/nfs/bsdi/usr/rstevens和/nfs/bsdi/usr/smith下所有文件的引用將共享同樣的TCP連接。

5:如果客戶檢測到服務器已經崩潰,或者崩潰然后重啟動(通過收到一個TCP差錯“連接超時”或者“對方復位連接”),它嘗試與服務器重新建立連接??蛻糇隽硪粋€主動打開,為同一個文件系統請求重新建立TCP連接。在以前連接上超時的所有客戶請求在新的連接上都會重新發出。

6:如果客戶機崩潰,那么當它崩潰時正在運行的應用程序也要崩潰。當客戶機重新啟動時,它很可能使用TCP重新安裝服務器的文件系統,這將導致和服務器的另一個連接??蛻艉头掌髦g針對同一個文件系統的前一個連接現在打開了一半(服務器方認為它還開著),但是既然服務器設置了keepalive選項,當服務器發出下一個keepalive探查報文時,這個半開著的TCP連接就會被中止。

隨著時間的流逝,另外一些廠商也計劃支持TCP上的NFS。

29.6 NFS實例

我們使用tcpdump來看一下在典型的文件操作中,客戶調用了哪些NFS過程。當tcpdump檢測到一個包含RPC調用(在圖29-1中調用字段等于0)、目的端口是2049的UDP數據報時,它把數據報按照一個NFS請求進行解碼。類似地,如果一個UDP數據報是一個RPC應答(在圖29-2中應答字段為1),源端口是2049,tcpdump就把此數據報作為一個NFS應答來解碼。

29.6.1 簡單的例子:讀一個文件

第一個例子是使用cat(1)命令將位于一個NFS服務器上的一個文件復制到終端上:

如同圖29-6所示,主機sun(NFS客戶機)上的文件系統/nfs/bsdi/usr實際上是主機bsdi(NFS服務器)上的/usr文件系統。當cat打開這個文件時,sun上的內核檢測到這一點,然后使用NFS去訪問文件。圖29-7顯示了tcpdump的輸出。

當tcpdump解析一個NFS請求或應答報文時,它打印客戶的XID字段,而不是端口號。第1行和第2行中的XID字段值是0x7aa6。

客戶內核中的打開函數一次處理文件名/nfs/bsdi/usr/rstevens/hello.c中的一個成員。當處理到/nfs/bsdi/usr時,它發現這是指向一個已安裝的NFS文件系統的一個安裝點。

在第1行中,客戶調用GETAT TR過程取得客戶已經安裝的服務器目錄的屬性(/usr)。這個RPC請求,除IP首部和UDP首部之外,包含104個字節的數據。第2行中的應答返回了一個OK值,除了IP首部和UDP首部之外,包含了96個字節的數據。在這個圖中,我們可以看出最小的NFS報文包含大約100個字節的數據。

圖29-7 讀一個文件的NFS操作

在第3行中,客戶調用LOOKUP過程來查看rstevens文件。在第4行中收到一個OK應答。LOOKUP過程說明了文件名rstevens和遠程文件系統被安裝時由內核保存的文件句柄。應答中包含了下一步要使用的一個新的文件句柄。

在第5行中,客戶使用第4行中返回的文件句柄對hello.c調用LOOKUP過程。在第6行返回了另一個文件句柄。新的文件句柄就是客戶在第7行和第9行中引用文件/nfs/bsdi/usr/rstevens/hello.c所使用的文件句柄。我們看到客戶對于正在打開的路徑名的每個成員都調用了一次LOOKUP過程。

在第7行中,客戶又調用了一次GETAT TR過程,接著在第9行中調用了READ過程。客戶請求從偏移0開始的1024個字節,但是接收到的沒有這么多(減去RPC字段和其他由READ過程返回的值的大小,在第10行中返回了38個字節的數據。這是文件hello.c的實際大小)。

在這個例子中,應用進程對于內核所做的這些RPC請求和應答一點兒也不知道。應用進程只是調用了內核的open函數,后者引起了3個RPC請求和3個應答(16行),然后應用進程又調用了內核的read函數,它引起了兩個請求和兩個應答(710行)。該文件位于一個NFS文件服務器,這一點對客戶應用進程來說是透明的。

29.6.2 簡單的例子:創建一個目錄

作為另一個簡單的例子,我們將當前工作目錄改變為一個后創建一個新的目錄:


圖29-8顯示了tcpdump的輸出。

圖29-8 NFS的操作:cd到NFS目錄,然后mkdir

改變目錄引起客戶調用了兩次GETATTR過程(1~4行)。當我們創建新的目錄時,客戶調用了GETAT TR過程(56行),接著調用LOOKUP過程(78行,用來驗證將創建的目錄不存在),跟著調用了MKDIR過程來創建目錄(9-10行)。在第8行中,應答OK并不表示目錄存在。它只是表示過程返回了。tcpdump并不理解NFS過程的返回值。它一般打印OK和應答報文中數據的字節數。

29.6.3 無狀態

NFS的一個特征(NFS的批評者稱之為NFS的一個瑕疵,而不是一個特征)是NFS服務器是無狀態的(stateless)。服務器并不記錄哪個客戶正在訪問哪個文件。請注意一下在前面給出的NFS過程中,沒有一個open操作和一個close操作。LOOKUP過程的功能與open操作有些類似,但是服務器永遠也不會知道客戶對一個文件調用了LOOKUP過程之后是否會引用該文件。
無狀態設計的理由是為了在服務器崩潰并且重啟動時,簡化服務器的崩潰恢復操作。

29.6.4 例子:服務器崩潰

在下面的例子中我們從一個崩潰然后重啟動的NFS服務器上讀一個文件。這個例子演示了無狀態的服務器是如何使得客戶不知道服務器的崩潰。除了在服務器崩潰然后重啟動時一個時間上的暫停外,客戶并不知道發生的問題,客戶應用進程沒有受到影響。

在客戶機sun上,我們對一個長文件(NFS服務器主機svr4上的文件/usr/share/lib/termcap)執行cat命令。在傳送過程中把以太網的網線拔掉,關閉然后重啟動服務器主機,再重新將網線連上。客戶被配置成每個NFS read過程讀1024個字節。圖29-9顯示了tcpdump的輸出。

1~10行對應于客戶打開文件,操作類似于圖29-7所示。在第11行我們看到對文件的第一個READ操作,在12行返回了1024個字節的數據。這個操作一直繼續到129行(讀1024個字節的數據,跟著一個OK應答)。

在第130行和第131行我們看到兩個請求超時,并且分別在132行和133行重傳。第一個問題是這里為什么會有兩個讀請求,一個從偏移65536開始讀,另一個從偏移73728開始讀?答案是客戶內核檢測到客戶應用進程正在進行順序地讀操作,所以試圖預先取得數據塊(大多數的Unix內核都采用了這種預讀技術)??蛻魞群艘舱谶\行多個NFS塊I/O守護程序,后者試圖代表客戶產生多個RPC請求。一個守護程序正在從偏移65536處讀8192個字節(以1024字節為一組數據塊),而另一個正在從73728處預讀8192個字節。

客戶重傳發生在130~168行。在第169行我們看到服務器已經重啟動,在它對第168行的客戶NFS請求做出應答之前,它發送了一個ARP請求。對168行的響應被發送在171行??蛻舻腞EAD操作繼續進行下去。

除了從129行到171行5分鐘的暫停,客戶應用進程并不知道服務器崩潰然后又重啟動了。這個服務器的崩潰對于客戶是透明的。

為了研究這個例子中的超時和重傳時間間隔,首先要意識到這兒有兩個客戶守護程序,分別有它們各自的超時。第1個守護程序(在偏移65536處開始讀)的間隔,四舍五入到兩個十進制小數點,為0.68,0.87,1.74,3.48,6.96,13.92,20.0,20.0,20.0等等。第2個守護程序(在偏移73728處開始讀)的間隔也是一樣的(精確到兩個小數點)??梢钥闯鲞@些NFS客戶使用了一個這樣的超時定時器:間隔為0.875秒的倍數,上限為20秒。每次超時后,重傳間隔翻倍:0.875,1.75,3.5,7.0和14.0。

圖29-9 當一個NFS服務器崩潰然后重啟動時,客戶正在讀一個文件的過程

客戶要重傳多久呢?客戶有兩個與此有關的選項。首先,如果服務器文件系統是“硬”安裝的,客戶就會永遠重傳下去。但是如果服務器文件系統是“軟”安裝的,客戶重傳了固定數目的次數之后就會放棄。在“硬”安裝的情況下,客戶還有一個選項決定是否允許用戶中斷無限制的重傳。如果客戶主機安裝服務器文件系統時說明了中斷能力,并且如果我們不想在服務器崩潰之后等5分鐘,等著服務器重啟動,就可以鍵入一個中斷鍵以終止客戶應用程序。

29.6.5 等冪過程

如果一個RPC過程被服務器執行多次仍然返回同樣的結果,那么就把它叫作等冪過程(Idempotent Procedure)。例如,NFS的讀過程是等冪的。正像我們在圖29-9中看到的,客戶只是重發一個特定的READ調用直到它得到一個響應。在我們的例子中,重傳的原因是服務器崩潰了。如果服務器沒有崩潰,而是RPC應答報文丟失了(既然UDP是不可靠的),客戶只是重傳請求,服務器再一次執行同樣的READ過程。同一個文件的同一部分被重讀一次,發送給客戶。
這種方法行得通的原因在于每個READ請求指出了讀操作開始的偏移位置。如果有一個NFS過程要求服務器讀一個文件的下N個字節,這種方法就不行了。除非服務器被做成是有狀態的(與無狀態相反),如果一個應答丟失了,客戶重發讀下N個字節的READ請求,結果將是不一樣的。這就是為什么NFS的READ和WRITE過程要求客戶說明開始的偏移位置的原因??蛻艟S護著狀態(每個文件當前的偏移位置),而不是服務器。

不幸的是并不是所有的文件系統操作都是等冪的。例如,考慮下面的動作:客戶NFS發出REMOVE請求來刪除一個文件;服務器NFS刪除了文件,并回答OK;服務器的回答丟失了;客戶NFS超時,然后重傳請求;服務器NFS找不到指定的文件,回答指出一個錯誤;客戶應用程序接收到一個錯誤表示文件不存在。這個返回給客戶應用程序的錯誤是不對的—該文件的確存在并且被刪除了。

等冪的NFS過程是:GETATTR、STATES、LOOKUP、READ、WRITE、READLINK和READDIR。不是等冪的過程是:CREATE、REMOVE、RENAME、LINK、SYMLINK、MKDIR和RMDIR。SETATTR過程如果不用來截斷文件,一般是等冪的。

既然使用UDP總會發生響應報文丟失的現象,NFS服務器需要一種方法來處理非等冪的操作。大多數的服務器實現了一個最近應答的高速緩存,用于存放非等冪操作最近的應答。每當服務器收到一個請求,它首先檢查這個高速緩存,如果找到了一個匹配,就返回以前的應答而不再調用相應的NFS過程。[Juszczak 1989]提供了這種高速緩存的實現細節。

等冪服務器過程的概念可以應用于任何基于UDP的應用程序,而不僅僅是NFS。例如,DNS也提供了一個等冪服務。一個DNS的服務器可以任意多次地執行一個解析者的請求而沒有任何不良的后果(如果不考慮網絡資源浪費的話)。

29.7 第3版的NFS

1993年發布了第3版的NFS協議規范[Sun Microsystem 1994]。其實現有望在1994年成為可能。

我們總結一下第2版和第3版的主要區別。下面把兩者分別稱為V2和V3。

1:V2中的文件句柄是32字節的固定大小的數組。在V3中,它變成了一個最多為64個字節的可變長度的數組。在XDR中,一個可變長度的數組被編碼為一個4字節的數組成員個數跟著實際的數組成員字節。這樣在實現時減少了文件句柄的長度,例如Unix只需要12個字節,但又允許非Unix實現維護另外的信息。

2:V2將每個READ和WRITE RPC過程可以讀寫的數據限制為8192個字節。這個限制在V3中取消了,這就意味著一個UDP上的實現只受到IP數據報大小的限制(65535字節)。這樣允許在更快的網絡上讀寫更大的分組。

3:文件大小以及READ和WRITE過程開始偏移的字節從32字節擴充到64字節,允許讀寫更大的文件。

4:每個影響文件屬性值的調用都返回文件的屬性。這樣減少了客戶調用GETAT TR過程的次數。

5:WRITE過程可以是異步的,而在V2中要求同步的WRITE過程。這樣可以提高WRITE過程的性能。

6:V3中刪去了一個過程(STAT FS),增加了七個過程:ACCESS(檢查文件訪問權限)、MKNOD(創建一個Unix特殊文件)、READDIRPLUS(返回一個目錄中的文件名字和它們的屬性)、FSINFO(返回一個文件系統的靜態信息)、FSSTAT(返回一個文件系統的動態信息)、PAT HCONF(返回一個文件的POSIX.1信息)和COMMIT(將以前的異步寫操作提交到外存中)。

29.8 小結

RPC是構造客戶-服務器應用程序的一種方式,使得看起來客戶只是調用了服務器的過程。所有的網絡操作細節都被隱藏在RPC程序包為一個應用程序生成的客戶和服務器殘樁以及RPC庫的例程中。我們顯示了RPC調用和應答報文的格式,并且提到了使用XDR對傳輸的值進行編碼,使得RPC客戶和服務器可以運行在不同架構的機器上。

最廣泛使用的RPC應用之一就是Sun的NFS,一個在各種大小的主機上廣泛實現的異構的文件訪問協議。我們瀏覽了NFS和它使用UDP和TCP的方式。第2版的NFS協議定義了15個過程。

一個客戶對一個NFS服務器的訪問開始于安裝協議,返回給客戶一個文件句柄??蛻艚又梢允褂媚莻€文件句柄來訪問服務器文件系統中的文件。在服務器上,一次檢查文件名的一個成員,返回每個成員的一個新的文件句柄。最后的結果就是要引用的文件的一個文件句柄,它可以在隨后的讀寫操作中被使用。

NFS試圖把它的所用過程都做成等冪的,使得如果響應報文丟失了,客戶只需要重發一個請求。我們看到了服務器崩潰然后又重啟動時,一個客戶讀服務器上的一個文件的例子。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 名詞延伸 通俗的說,域名就相當于一個家庭的門牌號碼,別人通過這個號碼可以很容易的找到你。如果把IP地址比作一間房子...
    楊大蝦閱讀 20,617評論 2 56
  • 簡介 用簡單的話來定義tcpdump,就是:dump the traffic on a network,根據使用者...
    保川閱讀 5,976評論 1 13
  • 文/墨爾本.晴上 追風 來自四面八方的風 帶著黃昏遺留下來的恍惚 看滿世界的星星 樹葉兒和發絲兒沒有告訴我答案 唯...
    桃戚閱讀 230評論 2 9
  • 劉小雨,劉小雨,劉小雨… 不知看到的同行們有沒有熟悉此人的呢 劉小雨,95后妹子,15年從事房地產銷售工作,從業兩...
    sl孫麗閱讀 400評論 0 0