Linux网络管理员手册(11) 第十一章 网络文件系统 NFS
Linux网络管理员手册(11) 第十一章 网络文件系统 NFS Linux网络管理员手册(11)
gohigh@shtdu.edu.cn
第十一章 网络文件系统
NFS,网络文件系统(network filesystem),或许是使用RPC最突出的网络服务了。它允许你以访问任何本地文件一样的方法来访问远程主机上的文件。这是通过将客户端的内核功能(它使用远程文件系统)与服务器端的NFS服务器功能(它提供文件数据)相混合而成为可能的。这种文件访问对客户来说是完全透明的,并且可在各种服务器和各种主机结构上工作。
NFS提供的许多优点:
被所有用户访问的数据可以存放在一台中央主机上,由客户在引导启动时加载这个目录。例如,你可以将所有用户的帐目存放在一台主机上,让你的网络上的所有用户从那台主机上加载/home目录。如果也安装了NIS的话,用户就可以登录进任何系统上,而始终在一组文件上工作。
需要耗费大量磁盘空间的数据可以被保存在一台主机上。例如,所有有关LaTeX和METAFONT的文件和程序可以在一个地方保存和维护。
管理用的数据可以存放在单个主机上。不再需要使用rcp将相同的文件安装到20个不同的机器上。
NFS在很大程度上是由Rick Sladkey做出的,[1] 他编制了NFS的内核代码和NFS服务器的大部分。后者源自于由Mark Shand编制的unfsd user-space NFS服务器和Donald Becker编制的hnfs Harris NFS服务器。
现在让我们观看一下NFS是如何工作的:一个客户可以请求将一台远程主机的目录加载到一个本地目录上,正如用加载一个物理设备同样的方法。然而,用于指定远程目录的句法是不同的。例如,为了主机vlager的/home加载到vale的/users上,管理员在vale上要发出以下的命令:[2]
# mount –t nfs vlager:/home /users
此时,mount将试图通过RPC联系vlager上的mountd mount后台程序。服务器将检查vale是否允许加载所考虑的目录,如果行的话,就返回一个文件句柄(file handle)。这个文件句柄将被用于所有随后对/users下的文件的请求。
当某人通过NFS访问一个文件时,内核就将一个RPC调用送至服务器机器上的nfsd(NFS后台程序)。该调用将文件句柄、要访问的文件名以及用户的user和group id作为参数。这些用于确定对指定文件的访问权限。为了避免非授权用户读取或修改文件,user和group id在两台主机上必须相同。
在许多UN*X实现中,NFS的客户和服务器功能是作为内核层的后台程序实现的,是在系统引导时从用户空间启动的。NFS后台程序(nfsd)在服务器主机上,Block I/O后台程序(biod)在客户主机上。为了提高吞吐率,biod使用预读(read-ahead)和后写(迟写)(write-behind)来执行同步I/O;同样,几个nfsd后台程序通常是并发运行的。
NFS的Linux实现稍微有些不同,客户代码被紧密地集成到内核的虚拟文件系统中(VFS)并且不需要通过biod进行另外的控制。另一方面,服务器代码完全在用户空间运行,所以同时运行该服务器的几个拷贝几乎是不可能的—因为这将涉及到同步问题。目前Linux的NFS还缺乏预读和后写机制,但Rick Sladkey计划今后将添加进去。[3]
Linux的NFS代码的最大问题是Linux的1.0版内核不能分配大于4K的内存块;其结果是,网络代码不能处理除去头大小等数据后大于3500左右字节的数据报。这意味着对于使用默认的大的UDP数据报的系统(例如,在SunOS上是8K),从其上运行的NFS后台程序上传回的数据,需要人工地切成小块。在某些环境下这会严重地损害系统性能。[4] 这个限制在最近的Linux-1.1内核中已不复存在,并且客户代码也已进行了修改以克服这个问题。
11.1 准备NFS
在你能够使用NFS之前,不管是作为服务器还是客户,你必须确信你的内核已将NFS支持编译进去了。对此,较新的内核在proc文件系统上有一个简单的接口,即/proc/filesystems文件,你可以使用cat来显示它:
$ cat /proc/filesystems
minix
ext2
msdos
nodev proc
nodev nfs
如果这个列表中没有nfs,那么你必须编译你自己的启用了NFS的内核。配置内核网络选项在第三章的“内核配置”一节中作过解释。
对于Linux 1.1以前的内核,要找出你的内核是否启用了NFS支持的最容易的方法是实际地试着加载一个NFS文件系统。对于此,你可以在/tmp下建立一个目录,并试着往上加载一个本地目录:
# mkdir /tmp/test
# mount localhost:/etc/ tmp/test
如果这个加载尝试失败了,并有一段出错信息指出“fs type nfs no supported by kernel”,那么你就必须制作一个启用了NFS的新内核。任何其它的出错信息都完全无关紧要,因为你还没有在你的主机上配置NFS后台程序。
11.2 加载一个NFS卷
NFS卷[5]的加载方法与普通文件系统的加载方式是一样的。你使用下面的句法调用mount:
# mount –t nfs nfs_volume local_dir options
nfs_volume是以remote_host:remote_dir形式给出。由于这个表示法对NFS文件系统来说是唯一的,你可以省略-t nfs选项。
在加载一个NFS卷时,你可以指定许多别的选项。这些选项可以在命令行上在-o开关后面给出,或者在/etc/fstab该卷条目的选项字段内给出。在这两种情况下,多个选项是用逗号彼此分开的。在命令行上的选项总是覆盖fstab文件中给出的选项。
在/etc/fstab中的一个样本条目可以是
# volume mount point type options
news:/usr/spool/news /usr/spool/news nfs timeo=14,intr
然后,这个卷可以使用下面命令被加载
# mount news:/usr/spool/news
如果fstab中没有相应的条目,那么NFS的mount调用看上去就有点乱。例如,假如你从一台称为moonshot的机器上加载你的用户主目录,moonshot机器使用一个默认的4K大小的块来进行读/写操作。你可以发出如下命令将块的大小减至2K以适应Linux的数据报大小的限制
# mount moonshot:/home /home -o rsize=2048,wsize=2048
在随Rick Sladkey的基于NFS的mount工具(可以在Rik Faith的util-linux软件包中找到)而来的nfs(5)手册页中对所有有效选项的列表进行了完整的描述。下面是你可能要用到的这些选项的一个不完整的列表:
rsize=n and wsize=n
这分别指出了用于NFS客户读写请求的数据报的大小。由于上述的UDP数据报大小的限制,它们目前的缺省值是1024字节。
timeo=n 这用于设置NFS客户将等待一个请求完成的等待时间(以十分之一秒计)。缺省值是0.7秒。
hard 明确地标记出这个卷是硬加载的(hard-mounted)。缺省值是on。
soft 软加载(Soft-mount)驱动程序(与硬加载相反)。
intr 允许通知中断一个NFS调用。对于服务器没有响应时的异常中止很有用。
除了rsize和wsize以外,所有这些选项应用于当服务器临时变得不可访问时客户的相应动作。它们一起以如下的方式行事:每当一个客户想NFS服务器发送了一个请求,它就期望操作在一给定的时间间隔内(由timeout选项指定)完成。如果在该时间内没有收到确认,就会发生一个所谓的次超时(minor timeout),操作被重试并且超时间隔时间翻倍。在到达60秒钟的最大超时时间时,就发生一个主超时(major timeout)。
缺省地,一个主超时将导致客户程序在控制台上打印出一条消息并且再次重新开始,这一次,时间值再次翻倍。潜在地,这将会一直继续下去。顽固地重复一个操作直到服务器再次可用时的卷称为硬加载(hard-mounted)。相反地,每当一个主超时发生时,软加载(soft-mounted)的卷就为调用进程生成一个I/O出错信息。由于缓冲引入了后写,所以在进程下一次调用write(2)函数之前,这个出错情形并没有传播到进程本身,因此一个程序就完全不能确知对一个软加载卷的写操作是否已经完成了。
硬加载还是软加载一个卷不仅只是一个感觉(习惯)问题,而且也是与从该卷上你要访问什么信息有关。例如,如果你通过NFS加载你的X程序,你当然不希望你的X会话仅仅因为有人同时开启7个xv致使网络几乎中断而抓狂,或者由于片刻插拔下以太网插头而使得X会话瞬时停止。通过硬加载这个卷,你可以确信你的计算机将会等待,直到能够重新与你的NFS服务器建立连接。另一方面,非关键数据比如NFS加载的news分区或FTP文档程序也可以软加载,那么在远程机器暂时连接不上或关闭时就不会挂起你的会话过程。如果到服务器的网络连接很脆弱或者要经过一个重负荷的路由器的话,那么你或者使用timeo选项增大初始的超时值,或者硬加载这个卷,但是要允许信号中断NFS调用,这样你仍然可以终止任何挂起的文件访问。
通常,mountd后台程序以某种方法保持哪个目录已被什么主机加载的轨迹。这个信息能够使用showmount程序显示出来,该程序也包括在NFS服务器软件包中。然而现在Linux mountd还不能做到这件事。
11.3 NFS后台程序(Daemons)
如果你想为其它主机提供NFS服务,那么必须在你的机器上运行nfsd和mountd后台程序。作为基于RPC的程序,它们不是由inetd管理的,而是在引导期间启动的,并且用portmapper注册自身的。因此你必须确信只在rpc.portmap运行之后才开始运行它们。通常,在你的rc.inet2脚本中要包括下面两行:
if [ -x /usr/sbin/rpc.mountd ]; then
/usr/sbin/rpc.mountd; echo -n " mountd"
fi
if [ -x /usr/sbin/rpc.nfsd ]; then
/usr/sbin/rpc.nfsd; echo -n " nfsd"
fi
NFS后台程序为它的客户所提供的文件的属主信息通常仅包含数值的user和group id。如果客户和服务器两者所用的user和group的名字与这些数值id的相关联,那么就称它们共享相同的uid/gid空间。例如,当你在你的LAN上对所有的主机使用NIS分发passwd信息就是这种情况。
然而,在某些情况下它们并不匹配。你可以不用更新客户的uid和gid以与服务器上的匹配,你可以使用ugidd映射后台程序来处理这事。使用下面描述的map_daemon选项,在客户ugidd的协助下你可以告知nfsd将服务器的uid/gid空间映射到客户的uid/gid空间上。
ugidd是一个基于RPC的服务器,并且如果nfsd和mountd一样是从rc.inet2中启动的。
if [ -x /usr/sbin/rpc.ugidd ]; then
/usr/sbin/rpc.ugidd; echo -n " ugidd"
fi
11.4 exports文件
上面的各个选项是用于客户的NFS配置的,在服务器一端有不同的选项集用于设置每一客户的行为。这些选项必须在/etc/exports中设置。
缺省地,mountd不允许任何人从本地主机加载目录,这是一种非常敏感的态度。为了许可一台或多台主机用NFS加载一个目录,这个目录必须exported(输出),也即,它必须在exports文件中被指定。一个样本文件看上去象这样:
# exports file for vlager
/home vale(rw) vstout(rw) vlight(rw)
/usr/X386 vale(ro) vstout(ro) vlight(ro)
/usr/TeX vale(ro) vstout(ro) vlight(ro)
/ vale(rw,no root squash)
/home/ftp (ro)
每一行定义了一个目录,以及允许加载该目录的主机。主机名通常是一个全资域名,但又可以含有*和?通配符,它们的作用与在Bourne shell中的一样。例如,lab*.foo.com与lab01.foo.com匹配,也与laber.foo.com匹配。如果主机名没有给出,就象上列中的/home/ftp目录,那么任何主机都允许加载这个目录。
当使用exports文件检查一台客户主机时,mountd将使用gethostbyaddr(2)调用查找客户的主机名,所以你必须确信没有在exports中使用别名。当不使用DNS时,那么返回的名字是在hosts文件中找到的与客户地址匹配的第一个主机名。
主机名字后跟一个可选的、用逗号分开的标志的列表,这些标志是用括号括住的。这些标志可以使用下面这些值:
insecure 允许从这台机器过来的非授权访问。
unix-rpc 要求对这台机器进行UNIX域RPC认证。这简单的要求请求要从一个保留的internet端口生成(也即,端口号必须小于1024)。缺省地,这个选项是启用的(on)。
secure-rpc 要求对这台机器进行安全的RCP认证。这个选项还没有实现。参见Sun的有关安全RPC的文档。
kerberos 要求对这台机器来的访问进行Kerberos认证。这个选项还没有实现。参见MIT有关Kerberos认证系统的文档。
root-squash 这是一个安全特性,它通过将客户的uid 0请求映射到服务器的uid 65534(-2)来拒绝指定主机上的超级用户的任何特殊访问权限。这个uid应该与user nobody相关联。
no_root_squash
不映射来自uid 0的请求。这个选项的缺省值为on。
ro 将文件结构加载为只读。这个选项缺省值为on。
rw 将文件结构加载为读-写。
link-relative 通过装扮所需的几个../来从服务器上含有连接到根的目录上将绝对符号连接(以一个斜杠开始的连接形式)转换成为相对连接。只有当主机的整个文件系统被加载时,这个选项才有意义,否则的话,某些连接将不指向任何地方,或者更糟的是,指向它们根本就没有想指的文件上。
这个选项缺省的为on。
link-absolute 保留所有符号连接为原样(这是Sun提供的NFS服务器的常规行为)。
map_daemon 这个选项告知NFS服务器假设客户和服务器不共享相同的uid/gid空间。此时,nfsd将通过查询客户的ugidd后台程序来建立一个在客户和服务器之间映射id的列表。
在nfsd或mountd启动时,解析exports文件所得的出错信息在notice层上被报告到syslogd的后台程序设施中。
注意,主机名字是通过逆向映射从客户的IP地址获取的,所以你必须正确地配置好解析器。如果你使用BIND并且特别注重安全性,你应该在你的host.conf文件中激活spoof检查。
11.5 Linux自动加载器(Automounter)
有时候,加载用户可能要访问的所有NFS卷是很浪费时间的;一方面是由于要被加载的卷数量,另一方面是在启动时要占用很多时间。对此一个可选择的方案是所谓的自动加载器(automounter)。这是一个daemon,它能自动地和透明地加载任何需要的NFS卷,并且在一定时间没有用到时自动卸载它们。automounter的一个聪明之处是它可以从另外一个地方加载某个卷。例如,你可能在两到三台主机上保存有你的X程序和支持文件的拷贝,并且所有其它主机通过NFS加载它们。使用automounter,你可以指定加载所有这三个到/usr/X386上;此时automounter将尝试加载其中任何一个,直到有一个加载尝试成功。
Linux常用的automounter称为amd。它最初是由Jan-Simon Pendry编制的并且由Rick Sladkey移植到Linux上。当前的版本是amd-5.3。
对amd作讨论已超出本书的范围;要想有一本好手册,请参阅源程序;其中含有一个有详细信息的文本文件。
注释
[1] 可以通过jrs@world.std.com联系到Rick。
[2] 注意,你可以省略-t nfs参数,因为mount从冒号上看出这里指定了一个NFS卷。
[3] 后写的问题是内核高速缓冲是用device/inode对索引的,因此,不能用于NFS加载的文件系统上。
[4] 正如Alan Cox向我解释的:NFS规范要求服务器在返回应答之前将每次写入的数据刷新到磁盘上。因为,BSD内核只能进行页大小的写入(4K),因此写入4块1K的数据将导致基于BSD的NFS服务器执行4次每块4K的写操作。
[5] 我们没有说文件系统,因为这些不是适当的文件系统。