NFS 文件系統(tǒng)概述
NFS(Network File System,網(wǎng)絡(luò)文件系統(tǒng))是一種基于網(wǎng)絡(luò)的文件系統(tǒng)。它可以將遠(yuǎn)端服務(wù)器文件系統(tǒng)的目錄掛載到本地文件系統(tǒng)的目錄上,允許用戶或者應(yīng)用程序像訪問本地文件系統(tǒng)的目錄結(jié)構(gòu)一樣,訪問遠(yuǎn)端服務(wù)器文件系統(tǒng)的目錄結(jié)構(gòu),而無(wú)需理會(huì)遠(yuǎn)端服務(wù)器文件系統(tǒng)和本地文件系統(tǒng)的具體類型,非常方便地實(shí)現(xiàn)了目錄和文件在不同機(jī)器上進(jìn)行共享。雖然 NFS 不是唯一實(shí)現(xiàn)這個(gè)功能的文件系統(tǒng),但它無(wú)疑是最成功一個(gè)。
NFS 的第一個(gè)版本是 SUN Microsystems 在 20 世紀(jì) 80 年代開發(fā)出來的,至今為止,NFS 經(jīng)歷了 NFS,NFSv2,NFSv3 和 NFSv4 共四個(gè)版本。現(xiàn)在,NFS 最新的版本是 4.1,也被稱為 pNFS(parallel NFS,并行網(wǎng)絡(luò)文件系統(tǒng))。
前四個(gè)版本的 NFS,作為一個(gè)文件系統(tǒng),它幾乎具備了一個(gè)傳統(tǒng)桌面文件系統(tǒng)最基本的結(jié)構(gòu)特征和訪問特征,不同之處在于它的數(shù)據(jù)存儲(chǔ)于遠(yuǎn)端服務(wù)器上,而不是本地設(shè)備上,因此不存在磁盤布局的處理。NFS 需要將本地操作轉(zhuǎn)換為網(wǎng)絡(luò)操作,并在遠(yuǎn)端服務(wù)器上實(shí)現(xiàn),最后返回操作的結(jié)果。因此,NFS 更像是遠(yuǎn)端服務(wù)器文件系統(tǒng)在本地的一個(gè)文件系統(tǒng)代理,用戶或者應(yīng)用程序通過訪問文件系統(tǒng)代理來訪問真實(shí)的文件系統(tǒng)。
眾所周知的是,NFS 的客戶端在訪問遠(yuǎn)端服務(wù)器文件系統(tǒng)時(shí),既需要通過服務(wù)器獲得文件的屬性信息,還需要通過服務(wù)器獲得文件的數(shù)據(jù)信息,這使得 NFS 天然地具備將文件的屬性信息和數(shù)據(jù)信息分離在不同服務(wù)器上進(jìn)行訪問的特性,于是最后一個(gè)版本 NFS4.1/pNFS,將 Lustre/CephFS/GFS 等集群文件系統(tǒng)的設(shè)計(jì)思想引入到自身中,成為一個(gè)具有里程碑意義的 NFS 版本。它使得 NFS 的數(shù)據(jù)吞吐的速度和規(guī)模都得到了極大提高,為 NFS 的應(yīng)用帶了更為廣闊的空間。
NFS 之所以備受矚目,除了它在文件共享領(lǐng)域上的優(yōu)異表現(xiàn)外,還有一個(gè)關(guān)鍵原因在于它在 NAS 存儲(chǔ)系統(tǒng)上應(yīng)用。NAS 與 DAS 和 SAN 在存儲(chǔ)領(lǐng)域的競(jìng)爭(zhēng)中,NFS 發(fā)揮了積極的作用,這更使得 NFS 越來越值得關(guān)注。
NFSv3 源代碼結(jié)構(gòu)
相比之前的兩個(gè)版本,NFSv3 是一個(gè)較為穩(wěn)定和成熟的 NFS 版本,而之后的 NFSv4 除了在安全和性能上有所提高外,還在網(wǎng)絡(luò)連接中加入了狀態(tài)屬性,因此顯得復(fù)雜一些。在此,本文以 NFSv3 為例來剖析 NFS 文件系統(tǒng)的源代碼結(jié)構(gòu),所用源碼來自 Linux 2.4.9 內(nèi)核。
按照?NFS 文件系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn),NFS 文件系統(tǒng)主要分為三個(gè)部分:The Protocol(網(wǎng)絡(luò)協(xié)議),Client Side(NFS 客戶端)和 Server Side(NFS 服務(wù)器)。NFS 客戶端提供了接口,保證用戶或者應(yīng)用程序能像訪問本地文件系統(tǒng)一樣訪問 NFS 文件系統(tǒng),NFS 服務(wù)器作為數(shù)據(jù)源,為 NFS 客戶端提供真實(shí)的文件系統(tǒng)服務(wù),而網(wǎng)絡(luò)協(xié)議則使得 NFS 客戶端和 NFS 服務(wù)器能夠高效和可靠地進(jìn)行通信。NFS 網(wǎng)絡(luò)協(xié)議使用的是 RPC(Remote Procedure Call,遠(yuǎn)程過程調(diào)用)/XDR(External Data Representation,外部數(shù)據(jù)表示)機(jī)制,因此本文將剖析的重點(diǎn)放在 NFS 客戶端和 NFS 服務(wù)器上。
Client Side 源代碼
Client Side 的頭文件在 include/linux/ 下面,C 文件在 fs/nfs 下面。
dir.c/file.c/inode.c/symlink.c/unlink.c:與文件操作相關(guān)的系統(tǒng)調(diào)用
read.c/write.c/flushd.c:文件讀寫
mount_clnt.c/nfs_root.c:將 NFS 文件系統(tǒng)作為 root 目錄的相關(guān)實(shí)現(xiàn)
proc.c/nfs2xdr.c/nfs3proc.c/nfs3xdr.c:網(wǎng)絡(luò)數(shù)據(jù)交換
與文件操作相關(guān)的系統(tǒng)調(diào)用都在 struct file_operations,struct inode_operations 這兩個(gè)數(shù)據(jù)結(jié)構(gòu)里面定義。文件的讀操作 nfs_file_read 和寫操作 nfs_file_write 被單獨(dú)提出來,因?yàn)槲募x寫性能將直接關(guān)系到文件系統(tǒng)的成敗,本文在后面會(huì)重點(diǎn)闡述其實(shí)現(xiàn)。
Server Side 源代碼
Server Side 的頭文件在 include/linux/nfsd 下面,C 文件在 fs/nfsd 下面。
auth.c/lockd.c/export.c/nfsctl.c/nfscache.c/nfsfh.c/stats.c:導(dǎo)出目錄的訪問管理
nfssvc.c:NFS 服務(wù) deamon 的實(shí)現(xiàn)
vfs.c:將 NFS 文件系統(tǒng)的操作轉(zhuǎn)換成具體文件系統(tǒng)的操作
nfsproc.c/nfsxdr.c/nfs3proc.c/nfs3xdr.c:網(wǎng)絡(luò)數(shù)據(jù)交換
導(dǎo)出目錄的訪問管理主要解決網(wǎng)絡(luò)文件系統(tǒng)實(shí)現(xiàn)面臨的幾個(gè)重要問題,包括目錄導(dǎo)出服務(wù),外部訪問的權(quán)限控制,多客戶端以及客戶端與服務(wù)器的文件并發(fā)操作等。
一個(gè)典型例子:rename 的調(diào)用過程
在 NFS 文件系統(tǒng)的文件操作中,除了 read 和 write 操作考慮到性能因素,專門使用了緩存機(jī)制外,其它的操作基本上都是同步完成的。本文以 rename 為例來進(jìn)行說明,如下圖所示。首先用戶或者應(yīng)用程序開始調(diào)用文件操作,經(jīng)過系統(tǒng)調(diào)用 sys_rename,到達(dá)虛擬文件系統(tǒng)層 vfs_rename,然后交給 NFS 文件系統(tǒng) nfs_rename 來處理。NFS 文件系統(tǒng)無(wú)法操作存儲(chǔ)介質(zhì),它調(diào)用 NFS 客戶端函數(shù) nfs3_proc_rename 和 NFS 服務(wù)器函數(shù) nfsd3_proc_rename 進(jìn)行通信,把文件操作轉(zhuǎn)發(fā)到 NFS 服務(wù)器的虛擬文件系統(tǒng)層 vfs_rename,最后調(diào)用具體的文件系統(tǒng)如 ext2 的函數(shù) ext2_raname,完成文件重命名。
圖 1. rename 調(diào)用過程
與傳統(tǒng)文件系統(tǒng)相同點(diǎn)
在闡述 NFS 文件系統(tǒng)與傳統(tǒng)桌面文件系統(tǒng)的相同點(diǎn)之前,我們首先簡(jiǎn)要回顧一下 Linux 操作系統(tǒng)上文件系統(tǒng)的體系結(jié)構(gòu)。按照?Linux 文件系統(tǒng)剖析的劃分,Linux 文件系統(tǒng)從上至下主要由虛擬文件系統(tǒng)層,特定文件系統(tǒng)層和頁(yè)高速緩存層三部分組成,如下圖所示。當(dāng)然,這種劃分并不是一定的,例如在執(zhí)行直接 I/O 調(diào)用時(shí),是不需要進(jìn)行頁(yè)高速緩存的,另外,對(duì)于塊設(shè)備的讀寫,進(jìn)行頁(yè)高速緩存之后還會(huì)有通用塊層和 I/O 調(diào)度層的處理。
圖 2. 文件系統(tǒng)體系結(jié)構(gòu)
用戶或者應(yīng)用程序通過統(tǒng)一的系統(tǒng)調(diào)用接口對(duì)文件系統(tǒng)進(jìn)行操作,然后系統(tǒng)調(diào)用進(jìn)入虛擬文件系統(tǒng)層,虛擬文件系統(tǒng)根據(jù)文件系統(tǒng)類型,調(diào)用特定文件系統(tǒng)的操作函數(shù)。對(duì)用戶和應(yīng)用程序來說,由于接口完全相同,因此用戶感覺不到差異,應(yīng)用程序也可以無(wú)縫地移植到 NFS 文件系統(tǒng)上。Linux 通過一組對(duì)象對(duì)文件系統(tǒng)的操作,這組對(duì)象是 superblock(超級(jí)塊對(duì)象),inode(索引節(jié)點(diǎn)對(duì)象),dentry(目錄項(xiàng)對(duì)象)和 file(文件對(duì)象),如下圖所示。所有文件系統(tǒng)都支持這些對(duì)象,正是因?yàn)樗鼈儯琕FS 層可以對(duì) NFS 和其它文件系統(tǒng)一視同仁,只管調(diào)用這些對(duì)象的數(shù)據(jù)和函數(shù)指針,把具體的文件系統(tǒng)數(shù)據(jù)布局和操作都留給特定的文件系統(tǒng)來完成。
圖 3. VFS 對(duì)象
NFS 與其它文件系統(tǒng)一樣,向內(nèi)核聲明和注冊(cè)自己的文件系統(tǒng)類型。
static DECLARE_FSTYPE(nfs_fs_type, "nfs", nfs_read_super, FS_ODD_RENAME); ... ... module_init(init_nfs_fs) module_exit(exit_nfs_fs)
同樣,NFS 也需要根據(jù)自己的文件類型設(shè)置相應(yīng)的文件操作函數(shù)。如果是正規(guī)文件,需要設(shè)置 inode 操作函數(shù),file 操作函數(shù),以及 address_space 操作函數(shù);如果是目錄文件,需要設(shè)置 inode 操作函數(shù),file 操作函數(shù);如果是鏈接,則只需設(shè)置 inode 操作函數(shù)。
static void nfs_fill_inode(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fattr) { ... ... inode->i_op = &nfs_file_inode_operations; if (S_ISREG(inode->i_mode)) { inode->i_fop = &nfs_file_operations; inode->i_data.a_ops = &nfs_file_aops; } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &nfs_dir_inode_operations; inode->i_fop = &nfs_dir_operations; } else if (S_ISLNK(inode->i_mode)) inode->i_op = &nfs_symlink_inode_operations; else init_special_inode(inode, inode->i_mode, fattr->rdev); ... ... }
與傳統(tǒng)文件系統(tǒng)不同點(diǎn)
與內(nèi)存文件系統(tǒng),閃存文件系統(tǒng)和磁盤文件系統(tǒng)這些本地文件系統(tǒng)最大的不同在于,NFS 文件系統(tǒng)的數(shù)據(jù)是基于網(wǎng)絡(luò),而不是基于存儲(chǔ)設(shè)備的,因此 NFS 文件系統(tǒng)在設(shè)計(jì)自己的 inode 和 superblock 數(shù)據(jù)結(jié)構(gòu),以及實(shí)現(xiàn)文件操作函數(shù)時(shí),無(wú)需考慮數(shù)據(jù)布局情況。同樣是因?yàn)榛诰W(wǎng)絡(luò),NFS 文件系統(tǒng)的權(quán)限控制和并發(fā)訪問的要求比本地文件系統(tǒng)更高,讀寫的緩存機(jī)制也大大有別于本地文件系統(tǒng)。
superblock 和 inode
清單 1. NFS 的 superblock 定義
struct rpc_clnt * client; /* RPC 客戶端句柄 */ struct nfs_rpc_ops * rpc_ops; /* RPC 客戶端函數(shù)向量表 */ int flags; /* 標(biāo)識(shí)信息 */ unsigned int rsize; /* 每次讀請(qǐng)求的最小數(shù)據(jù)量 */ unsigned int rpages; /* 每次讀請(qǐng)求的最小數(shù)據(jù)量(以頁(yè)為單位)*/ unsigned int wsize; /* 每次寫請(qǐng)求的最小數(shù)據(jù)量 */ unsigned int wpages; /* 每次寫請(qǐng)求的最小數(shù)據(jù)量(以頁(yè)為單位)*/ unsigned int dtsize; /* 每次讀目錄信息的最小數(shù)據(jù)量 */ unsigned int bsize; /* NFS 服務(wù)器端的塊大小 */ unsigned int acregmin; /* 正規(guī)文件在緩存中駐留的最小允許時(shí)間 */ unsigned int acregmax; /* 正規(guī)文件在緩存中駐留的最大允許時(shí)間 */ unsigned int acdirmin; /* 目錄文件在緩存中駐留的最小允許時(shí)間 */ unsigned int acdirmax; /* 目錄文件在緩存中駐留的最大允許時(shí)間 */ unsigned int namelen; /* NFS 服務(wù)器端的主機(jī)名稱最大長(zhǎng)度 */ char * hostname; /* NFS 服務(wù)器端的主機(jī)名稱 */ struct nfs_reqlist * rw_requests; /* 異步讀寫請(qǐng)求隊(duì)列信息 */
清單 2. NFS 的 inode 定義
__u64 fsid; /* 根目錄(導(dǎo)出目錄)信息 */ __u64 fileid; /* 當(dāng)前文件信息 */ struct nfs_fh fh; /* 文件句柄 */ ... ... struct list_head read; /* 讀數(shù)據(jù)頁(yè)隊(duì)列 */ struct list_head dirty; /* 臟數(shù)據(jù)頁(yè)隊(duì)列 */ struct list_head commit; /* 提交數(shù)據(jù)頁(yè)隊(duì)列 */ struct list_head writeback; /* 寫回?cái)?shù)據(jù)頁(yè)隊(duì)列 */ unsigned int nread, /* 讀數(shù)據(jù)頁(yè)數(shù)量 */ ndirty, /* 臟數(shù)據(jù)頁(yè)數(shù)量 */ ncommit, /* 提交數(shù)據(jù)頁(yè)數(shù)量 */ npages; /* 寫回?cái)?shù)據(jù)頁(yè)數(shù)量 */ ... ...
以上省略了 superblock 和 inode 定義的公共部分,列出的僅是 NFS 文件系統(tǒng) superblock 和 inode 定義的私有部分,因?yàn)橹挥羞@些私有定義才能體現(xiàn)出文件系統(tǒng)的設(shè)計(jì)原則。從這些定義可以看出,私有部分?jǐn)?shù)據(jù)結(jié)構(gòu)里面主要包含網(wǎng)絡(luò)連接和讀寫請(qǐng)求兩個(gè)方面相關(guān)的信息。superblock 里 client 定義了 RPC 協(xié)議的客戶端連接狀態(tài),rpc_ops 定義了 RPC 協(xié)議的客戶端入口函數(shù),如 nfs3_proc_read,nfs3_proc_write,nfs3_proc_create 等。inode 里 fh 是 NFS 客戶端和 NFS 服務(wù)器相互傳遞的關(guān)鍵參數(shù),4 個(gè)頁(yè)隊(duì)列用于進(jìn)行讀寫緩存,隨后兩小節(jié)將分別予以介紹。
file handle
file handle(fh 或者 fhandle)在 NFS 客戶端和 NFS 服務(wù)器之間相互傳遞,建立 NFS 客戶端的 inode 和 NFS 服務(wù)器的 inode 的關(guān)聯(lián)關(guān)系。它主要表征的是 NFS 服務(wù)器上 inode 和物理設(shè)備的信息。file handle 對(duì)于 NFS 客戶端來說是透明的,NFS 客戶端不需要知道它的具體內(nèi)容。file handle 在 NFS 客戶端的定義是 66 個(gè)字節(jié),前兩個(gè)字節(jié)組成一個(gè)無(wú)符號(hào) short 型,表示 file handle 的大小,后 64 個(gè)字節(jié)組成數(shù)據(jù)區(qū),存儲(chǔ) file handle 的內(nèi)容。
#define NFS_MAXFHSIZE 64 struct nfs_fh { unsigned short size; unsigned char data[NFS_MAXFHSIZE]; };
file handle 在 NFS 服務(wù)器的定義由 knfsd_fh 數(shù)據(jù)結(jié)構(gòu)表示,fh_size 表示 file handle 的大小,數(shù)據(jù)區(qū) fh_base 是一個(gè)聯(lián)合體,有 fh_old,fh_pad,fh_new 三種定義,最大也是 64 個(gè)字節(jié)。考慮到當(dāng)前的 NFS 版本是 v3,只看 fh_new 的定義。fh_version 表示 fh_new 定義的版本,當(dāng)前版本是 1。fh_auth_type 表示認(rèn)證方式,0 表示不認(rèn)證。fh_fsid_type 表示根目錄(即導(dǎo)出目錄)的信息存儲(chǔ)方式,如果是 0,那么從 fh_auth 開始前 2 個(gè)字節(jié)表示根目錄所在設(shè)備的 major 號(hào),后 2 個(gè)字節(jié)表示根目錄所在設(shè)備的 minor 號(hào),隨后的 4 個(gè)字節(jié)表示根目錄的 inode 索引號(hào)。fh_fileid_type 表示當(dāng)前文件的信息存儲(chǔ)方式,如果是 1,那么在表示完 fh_fsid 后,緊接著 4 個(gè)字節(jié)表示當(dāng)前文件的 inode 索引號(hào),之后 4 個(gè)字節(jié)表示當(dāng)前文件的 inode generation 號(hào)。
struct nfs_fhbase_new { __u8 fb_version; /* == 1, even => nfs_fhbase_old */ __u8 fb_auth_type; __u8 fb_fsid_type; __u8 fb_fileid_type; __u32 fb_auth[1]; };
read 和 write
前面介紹文件系統(tǒng)體系結(jié)構(gòu)的時(shí)候,將它分為了虛擬文件系統(tǒng),特定文件系統(tǒng)和頁(yè)高速緩存三個(gè)層次。NFS 文件系統(tǒng)使用了這三個(gè)層次的功能,它本身完成了特定文件系統(tǒng)的功能,同時(shí)既為虛擬文件系統(tǒng)提供了完整的調(diào)用接口,也用到了頁(yè)高速緩存來提高讀寫性能。就層次劃分而言,與傳統(tǒng)桌面文件系統(tǒng)相比,NFS 文件系統(tǒng)的讀寫操作不再需要通用塊層和 I/O 調(diào)度層,而是使用了多個(gè)列表以及相關(guān)操作來進(jìn)一步緩存數(shù)據(jù),增強(qiáng)讀寫效率。當(dāng)然 NFS 文件系統(tǒng)也不再使用存儲(chǔ)設(shè)備驅(qū)動(dòng),而是通過網(wǎng)絡(luò)協(xié)議來獲取和提交數(shù)據(jù)。
圖 4. 讀寫緩存機(jī)制
如上圖所示,NFS 文件系統(tǒng)使用 read,writeback,dirty 和 commit 四個(gè)隊(duì)列,每個(gè)隊(duì)列的單元數(shù)據(jù)結(jié)構(gòu)都是 nfs_page,每個(gè) nfs_page 都有一個(gè) page 變量指向頁(yè)高速緩存。讀方法 nfs_readpage 首先使用異步方式讀取數(shù)據(jù),如果異步方式失效,才使用同步方式,nfs_readpage_async 所讀的數(shù)據(jù)都進(jìn)入 read 隊(duì)列中。寫方法 nfs_writepage 如果寫數(shù)據(jù)超過一頁(yè)(缺省是 4096 字節(jié)),使用異步方式提交數(shù)據(jù),否則使用同步方式。nfs_writepage_async 所寫的數(shù)據(jù)首先進(jìn)入 writeback 隊(duì)列,如果數(shù)據(jù)發(fā)生更改,則進(jìn)入 dirty 隊(duì)列,如果將更改的數(shù)據(jù)提交到 NFS 服務(wù)器上,則進(jìn)入 commit 隊(duì)列。這些隊(duì)列或者因?yàn)槌瑫r(shí),或者因?yàn)閱卧獢?shù)量多于最大值,將被釋放掉。
權(quán)限認(rèn)證和并發(fā)鎖
NFSv3 版本使用 nfs_permission 做用戶權(quán)限認(rèn)證,用 nfs_revalidate 做文件合法性檢查。前者調(diào)用 access 系統(tǒng)調(diào)用同步完成,后者調(diào)用 getattr 同步完成。為了使多個(gè) NFS 客戶端或者 NFS 客戶端與 NFS 服務(wù)器對(duì)相同文件可以實(shí)現(xiàn)并發(fā)操作,NFS 使用 NLM(network lock management,網(wǎng)絡(luò)鎖管理)協(xié)議在 NFS 服務(wù)器上對(duì)文件進(jìn)行打開,讀寫和移除,使不同的訪問都有及時(shí)和同一的語(yǔ)義理解。
總結(jié)
本文分析了 NFS 文件系統(tǒng)的設(shè)計(jì),主要分為三個(gè)部分,NFS 客戶端,NFS 服務(wù)器和網(wǎng)絡(luò)協(xié)議,并闡述了三者的功能劃分,介紹了它們是如何組織起來,為用戶或者應(yīng)用程序提供文件服務(wù)。進(jìn)一步的,本文使用 Linux 2.4.9 內(nèi)核剖析了 NFSv3 的源代碼實(shí)現(xiàn),從源代碼層次說明了 NFS 文件系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié),重點(diǎn)介紹了它與傳統(tǒng)桌面文件系統(tǒng)的相同和不同之處,使讀者能夠深入理解 NFS 文件系統(tǒng)的本質(zhì)。pNFS 是 NFS 文件系統(tǒng)從桌面型文件系統(tǒng)到集群型文件系統(tǒng)的一個(gè)轉(zhuǎn)折性版本,讀者可自行閱讀 pNFS 的源代碼實(shí)現(xiàn)。在閱讀之前,推薦讀者首先閱讀 Luster/CephFS/GFS 等文件系統(tǒng)相關(guān)的論文和資料,以便對(duì)集群文件系統(tǒng)的設(shè)計(jì)架構(gòu)有個(gè)基本的認(rèn)識(shí)。
?
評(píng)論