NFS协议

NFS协议及解析概要

本文是对RFC1094的翻译,它是NFS的第2版,之所以翻译这个版本是因为它比较简单。后面会陆续完成第3版和第4版的翻译,并且完成Linux操作系统下NFS文件系统和其服务端的代码解析。作者翻译本文章出于如下几个目的:1)练习英语,提升自身英语水平;2)进一步熟悉NFS协议;3)传播存储相关知识。另外,协议本身是纯文本格式,没有配图,因此理解起来可能有一定的难度,因此译者在翻译的时候加入了一些译者注和插图,以方便大家理解。不妥之处还请大家多批评指正。预警:文章比较长

本备忘录的状态

本RFC描述了一个在太阳微系统公司(译者注:这里是指SUN,当前已经被Oracle收购)和其它正在使用的协议。新版本的协议正在开发中,但其他人仍然可以从当前协议的描述中及设计问题的讨论中受益。本备忘录的分发不受任何限制。

1. INTRODUCTION(引言)

Sun网络文件系统(NFS)协议提供了透明的通过网络远程访问共享文件的方式。NFS协议被设计于可以很轻松的跨不同的机器、操作系统、网络架构和传输协议。这种可移植性是通过使用建立在外部数据表示(XDR)之上的远程过程调用(RPC)原语实现的。实现已经存在于从个人计算机到超级计算机的各种机器上。

支持的mount协议允许服务器向受限制的一组客户端分配远程访问权限。它执行特定于操作系统的功能,例如,允许将远程目录树附加到某个本地文件系统。

译者注:NFS实现了一个跨越网络的文件访问功能,如图1可以简要说明其原理。其整个架构为Client-Server架构,客户端和服务端通过RPC协议进行通信,RPC协议可以简单的理解为一个基于TCP的应用层协议,它简化命令和数据的传输。NFS最大的特点是将服务端的文件系统目录树映射到客户端,而在客户端访问该目录树与访问本地文件系统没有任何差别,客户端并不知道这个文件系统目录树是本地的还是远在另外一台服务器。

图1 客户端服务端示意

1.1 远程过程调用

Sun的远程过程调用规范为远程服务提供了一个面向过程的接口。每台服务器都提供一个“程序”,这是一组过程。NFS就是这样一个程序。主机地址、程序号和过程号的组合指定一个远程过程。NFS的目标是不需要低级别的任何特定可靠性级别,因此它可能用于许多底层传输协议,甚至其他远程过程调用实现。为了便于讨论,本文的其余部分将假定NFS是在Sun RPC之上实现的,如RFC 1057《RPC:远程过程调用协议规范》中所述。

译者注:从字面意义上可以看出,RPC是一个跨越不同计算节点的过程调用,可以理解为跨越不同计算机的函数调用。也就是说通过RPC,你可以在一台计算机上调用另外一台计算机上进程中的函数实现。如图2是一个简答的调用示意图,客户端发起RPC调用,经过网络传输给服务端,服务端进行处理后将结果返回给客户端。虽然经过了复杂的网络传输等过程,但在客户端看来,就是进行了一次函数调用。

图2 RPC调用过程示意

1.2 外部数据表示

外部数据表示(XDR)标准提供了一种在网络上表示一组数据类型的通用方法。NFS协议规范是使用RPC数据描述语言编写的。有关更多信息,请参阅RFC 1014,《XDR:外部数据表示标准》。虽然存在自动的rpc/xdr编译器来生成服务器和客户机的“存根”,但是NFS不需要它们的使用。可以使用提供等效功能的任何软件,如果编码完全相同,则可以与其他NFS实现进行互操作。

1.3 无状态服务

NFS协议旨在尽可能无状态。也就是说,为了正常工作,服务器不需要维护任何客户机的协议状态信息。无状态服务器在发生故障时比有状态服务器有明显的优势。

对于无状态服务器,客户机只需要重试一个请求,直到服务器响应;它甚至不需要知道服务器已经崩溃,或者网络暂时中断。另一方面,有状态服务器的客户机需要检测服务器故障并在服务器恢复时重建服务器状态,或者导致客户机操作失败。

这听起来可能不是一个重要的问题,但它以一些意想不到的方式影响了协议。我们认为,在协议中,能够编写不需要特殊崩溃恢复的非常简单的服务器可能需要一些额外的复杂性。请注意,即使使用了所谓的“可靠”传输协议(如TCP),客户端仍必须能够通过在连接超时时重新打开连接来处理服务中断。因此,无状态协议实际上可以简化实现。

另一方面,NFS处理一些对象,比如文件和目录,这些对象本身就具有状态——如果一个文件不保持其内容完整,它会有什么好处?目标是不在协议本身中引入任何额外的状态。文件或记录锁定以及远程执行等固有的状态操作是作为单独的服务实现的,在本文档中没有描述。

简化恢复的基本方法是使操作尽可能“等量”(以便可以重复)。此版本协议中的某些操作没有达到此目标;幸运的是,大多数操作(如读和写)都是等幂的。此外,大多数服务器故障发生在操作之间,而不是在接收操作和响应之间。最后,尽管实际的服务器故障可能很少,但在复杂的网络中,任何网络、路由器或网桥的故障都可能与服务器故障不可区分。

NFS协议的定义

服务器会随着时间的推移而改变,它们使用的协议也会随之改变。rpc为每个rpc请求提供一个版本号。此RFC描述了第二版的NFS协议。即使在第二个版本中,也有一些过时的过程和参数,将在以后的版本中删除。目前正在为第三版NFS协议准备一个RFC。

2.1 文件系统模型

NFS假定文件系统是基于目录进行分层的,目录为除底层文件之外的所有内容。目录(文件、目录、设备等)中的每个条目都有一个字符串名称。不同的操作系统可能对树的深度或使用的名称有限制,并且使用不同的语法来表示“路径名”,这是名称中所有“组件”(目录和文件名)的串联。“文件系统”是具有指定“根”的单个服务器(通常是单个磁盘或物理分区)上的树。一些操作系统提供了一个“挂载”操作,使所有文件系统显示为一棵树,而其他操作系统则维护一个文件系统的“森林”。文件是未解释字节的非结构化流。NFS的版本3使用了稍微更通用的文件系统模型。

NFS一次查找路径名的一个组件。这可能并不明显,为什么它不只是获取整个路径名,沿着目录移动,并在完成后返回一个文件句柄。有几个好理由不这么做。首先,路径名需要在目录组件之间使用分隔符,不同的操作系统使用不同的分隔符。我们可以定义一个网络标准的路径名表示,但是每个路径名都必须在每一端进行解析和转换。其他问题在第3节,NFS实现问题中讨论。

虽然文件和目录在许多方面都是相似的对象,但读取目录和文件时使用了不同的过程。这为表示目录提供了一种网络标准格式。可以使用与上面相同的参数来对每个调用只返回一个目录条目的过程进行证明。问题在于效率。目录可以包含多个条目,远程调用返回每个条目的速度太慢。

2.2 服务例程

协议定义通过一组例程进行定义,这些例程的参数和结果是使用RPC语言定义的(使用程序、版本和过程声明扩展的XDR语言)。对每个例程功能的简要描述应提供足够的信息,以便于实施。第2.3节更详细地描述了基本数据类型。

假定NFS协议中的所有过程都是同步的。当一个过程返回到客户机时,客户机可以假定操作已经完成,并且与请求相关联的任何数据现在都在稳定存储中。例如,客户机写请求可能会导致服务器更新数据块、文件系统信息块(如间接块)和文件属性信息(大小和修改时间)。当写操作返回到客户机时,它可以假定写操作是安全的,即使在服务器崩溃的情况下,它也可以丢弃所写的数据。这是服务器无状态的一个非常重要的部分。如果服务器等待从远程请求中刷新数据,客户机必须保存这些请求,以便在服务器崩溃时重新发送它们。

/* Remote file service routines */program NFS_PROGRAM {version NFS_VERSION {voidNFSPROC_NULL(void) = 0;attrstatNFSPROC_GETATTR(fhandle) = 1;attrstatNFSPROC_SETATTR(sattrargs) = 2;voidNFSPROC_ROOT(void) = 3;diropresNFSPROC_LOOKUP(diropargs) = 4;readlinkresNFSPROC_READLINK(fhandle) = 5;readresNFSPROC_READ(readargs) = 6;voidNFSPROC_WRITECACHE(void) = 7;attrstatNFSPROC_WRITE(writeargs) = 8;diropresNFSPROC_CREATE(createargs) = 9;statNFSPROC_REMOVE(diropargs) = 10;statNFSPROC_RENAME(renameargs) = 11;statNFSPROC_LINK(linkargs) = 12;statNFSPROC_SYMLINK(symlinkargs) = 13;diropresNFSPROC_MKDIR(createargs) = 14;statNFSPROC_RMDIR(diropargs) = 15;readdirresNFSPROC_READDIR(readdirargs) = 16;statfsresNFSPROC_STATFS(fhandle) = 17;} = 2;} = 100003;

2.2.1 什么都不做

voidNFSPROC_NULL(void) = 0;

这个例程什么都不做,它在所有的RPC服务中都可用,以允许服务响应测试和计时。

2.2.2 获取文件属性

attrstatNFSPROC_GETATTR (fhandle) = 1;

如果响应状态为NFS_OK,这时响应的属性中包含通过输入fhandle所指定的文件的属性信息。

2.2.3 设置文件属性

struct sattrargs {fhandle file;sattr attributes;};attrstatNFSPROC_SETATTR (sattrargs) = 2;

参数"attributes" 包含着或者为-1,或者为“file”文件属性新值的域。如果其回复的状态为 NFS_OK,这时应答的属性中具有了"SETATTR"操作完成后文件的属性。

注意:在下一个版本的协议中,使用-1表示"attributes"中一个未使用的域发生了变化。

2.2.4 获取文件系统的根

voidNFSPROC_ROOT(void) = 3;

过时的。由于查找一个文件系统的根文件句柄需要在客户端和服务端移动路径名称,因此本例程不在使用。要正确执行此操作,我们必须定义路径名的网络标准表示。我们用MNTPROC_MNT 例程代替了查找根文件句柄的功能。(细节请参考附录A,“挂载协议的定义”)

2.2.5 查找文件名

diropresNFSPROC_LOOKUP(diropargs) = 4;

如果回复的状态为NFS_OK,这时应答的"file"和"attributes"分别是参数中给定目录"dir"的文件句柄和文件“name”的属性。

2.2.6 从符号链接读取数据

union readlinkres switch (stat status) {case NFS_OK:path data;default:void;};readlinkresNFSPROC_READLINK(fhandle) = 5;

如果回复的状态为NFS_OK,这时响应中的“data”是通过fhandle参数指定的文件的数据。

注意: 由于NFS仅仅在客户端解析路径名,符号链接的路径名在不同的客户端意义是不同的(或者是无意义的)。

2.2.7 从文件读

struct readargs {fhandle file;unsigned offset;unsigned count;unsigned totalcount;};union readres switch (stat status) {case NFS_OK:fattr attributes;nfsdata data;default:void;};readresNFSPROC_READ(readargs) = 6;

从给定的“file”文件返回“count”大小的数据给“data”,数据以文件的“offset”字节文件偏移为起始位置。文件的第一个字节在0偏移的位置。当读完成后,文件属性通过“attributes”返回。

注意:参数 "totalcount"没有用,协议的下一个版本将被移除。

2.2.8 写缓存

voidNFSPROC_WRITECACHE(void) = 7;

下一版本将被采用。

2.2.9 写文件

struct writeargs {fhandle file;unsigned beginoffset;unsigned offset;unsigned totalcount;nfsdata data;};attrstatNFSPROC_WRITE(writeargs) = 8;

从文件“file”开始偏移“offset”的位置开始写数据“data”。文件的第一个字节在偏移为0的位置。如果回复的“status”是NFS_OK,这时写完后“attributes”中包含文件的属性。写操作是原子操作。本客户端"WRITE"写的数据不会和其它客户端的"WRITE"操作冲突。

注意:参数 "beginoffset"和"totalcount"将在下一个版本被移除。

2.2.10 创建文件

struct createargs {diropargs where;sattr attributes;};diropresNFSPROC_CREATE(createargs) = 9;

在给定的文件夹“dir”中创建一个名为“name”的文件。新文件的初始属性由“attributes”确定。回复“status”如果是NFS_OK则表示文件创建成功,并通过“file”和“attributes”返回文件句柄和属性。其它任何“status”意味着操作失败,没有文件创建成功。

注意: 该例程应该创建一个互斥创建旗标,意味着“仅仅不在该文件时创建”。

2.2.11 移除文件

statNFSPROC_REMOVE(diropargs) = 10;

从给定的目录“dir”中移除文件“name”。回复NFS_OK 表示文件被移除。

注意:可能是非幂等操作

2.2.12 重命名文件

struct renameargs {diropargs from;diropargs to;};statNFSPROC_RENAME(renameargs) = 11;

给定目录"from.dir"中的文件 "from.name"命名为"to.dir"中的文件"to.name"。如果回复NFS_OK表示命名成功。RENAME 操作是原子操作,在执行过程中不能被打断。

注意:可能是非幂等操作

2.2.13 创建文件链接

例程12, 版本 2.

struct linkargs {fhandle from;diropargs to;};statNFSPROC_LINK(linkargs) = 12;

在给定的“to.dir”目录中创建一个名为“to.name”的文件。该文件是一个当前已经存在文件“from”的硬链接。如果返回值是NFS_OK,链接将被创建。其它任何返回值将表示有错误,链接不会被创建。

一个硬链接应该具有改变任何被链接文件都应该反映在两个文件中的特性。当一个文件做了硬链接的时候,该文件的“nlink”属性应该具有一个值,而且值应该比做链接之前大。

注意:可能是非幂等操作

2.2.14 创建符号链接

struct symlinkargs {diropargs from;path to;sattr attributes;};statNFSPROC_SYMLINK(symlinkargs) = 13;

在给定的目录"from.dir"中创建类型为NFLNK的文件"from.name"。新文件包含路径名"to",并且具有通过"attibutes"指定的初始属性。如果返回值是NFS_OK,链接将被创建成功。任何其它返回值表示创建错误,链接不被创建。

符号链接是另外一个文件的指针。给定的名称"to"不会被服务端解析,仅仅存储在新建的文件中。当客户端引用一个符号链接文件时,符号链接的内容通常被透明地重新解释为要替换的路径名。READLINK操作向客户端返回解析的数据。

注意: 在UNIX系统上属性从来不会被使用,这是因为符号链接的模式总是0777.

2.2.15 创建目录diropresNFSPROC_MKDIR (createargs) = 14;

在给定的目录"where.dir"中新建一个目录"where.name"。通过给定的"attributes"设置目录的初始属性。返回NFS_OK表示新目录创建成功,此时返回的"file"和"attributes"为文件句柄和属性。其它任何返回表示操作失败,并且没有目录创建。

注意:可能是非幂等操作

2.2.16 移除文件夹

statNFSPROC_RMDIR(diropargs) = 15;

更定文件夹“dir”中的名为“name”的空文件夹被移除。如果回复是NFS_OK,则文件夹被移除。

注意:可能是费米等操作

2.2.17 从文件夹中读

struct readdirargs {fhandle dir;nfscookie cookie;unsigned count;};struct entry {unsigned fileid;filename name;nfscookie cookie;entry *nextentry;};union readdirres switch (stat status) {case NFS_OK:struct {entry *entries;bool eof;} readdirok;default:void;};readdirresNFSPROC_READDIR (readdirargs) = 16;

从给定的文件夹“dir”中返回一定数量的目录项,数量有参数“count”决定。如果返回值中“status”的值为NFS_OK,这时将有多个“entry”紧跟其后。每个“entry”包含一个“fileid”,其由一个文件系统中标识一个文件的唯一数字、文件的“name”和一个目录中指向下一个项的隐藏指针构成。

cookie 用于下一次READDIR调用中,用于在本目录中指定开始查找的位置。特定的0 cookie用于对目录进行从头查找。fileid 域应该与文件属性中的“fileid”有相同的值。eof 是一个旗标,如果目录中没有更多项目,则返回TRUE。

2.2.18 获取文件属性

union statfsres (stat status) {case NFS_OK:struct {unsigned tsize;unsigned bsize;unsigned blocks;unsigned bfree;unsigned bavail;} info;default:void;};statfsresNFSPROC_STATFS(fhandle) = 17;

如果返回的"status"是NFS_OK,回复"info"包含由输入fhandle指定文件的文件系统的属性 。属性域包含如下值:

tsize服务器端以字节为单位的最有传输大小。这个是READ和WRITE请求中服务器端期望的数据部分的字节数量。bsize 文件系统以字节为单位的块大小。

blocks文件系统上"bsiez"块的总数量。

bfree文件系统上"bsize"块的剩余数量。

bavail"bsize"块非特权用户的可用数量。

注意: 如果文件系统具有变化大小的块,本调用工作可能会有问题。由于文章太长,本号划分为2部分进行翻译。本篇文章是前半部分,后面将介绍NFS协议的数据类型相关内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值