Skip to content
Wireshark Wiki 中文翻译整理专题首页原始页面

Network Lock Manager 协议(NLM)

NLM 协议的目的是为 NFS version 2 和 3 提供类似 POSIX 建议性文件锁语义的功能。该协议与 NFS 协议本身紧密相关,因为它与 NFS 共享文件句柄数据结构;它也与 NSM 协议相关,锁管理器使用 NSM 协议从对等端重启中恢复;在某些平台上,它还与 KLM 协议相关,KLM 用于内核中的 NFS 客户端代码与用户空间锁管理器之间通信。

锁管理器通常完全在用户空间中的锁管理器守护进程内实现;该守护进程会在请求锁时从 NFS 客户端接收消息,并向 NFS 服务器机器上的 NLM 服务器发送 NLM 请求;它还会从运行该守护进程的机器上的 NFS 客户端接收 NLM 请求,并代表这些客户端执行本地加锁调用。你需要在客户端和服务器两端都运行此锁管理器守护进程,锁管理才能工作。内核中的 NFS 客户端代码会使用不同的协议与锁管理器守护进程通信;例如,它可能通过 loopback 接口使用 Sun 的 KLM 协议,也可能像某些 BSD 上那样,通过本地命名管道使用另一种协议。

历史

NLM 协议是在最初发布 NFS 之后出现的,当时 SunOS 中增加了字节范围锁支持,因为加锁显然更需要有状态协议。该协议的目的是为 NFS 服务实现 POSIX 风格的文件锁。

NLM 协议曾有 4 个不同版本,版本 1 到 3 实际上几乎完全相同,区别只是在版本 2 和 3 中增加了一些额外函数,以适配非 UNIX(指 DOS 和 Windows 的 PC-NFS)客户端。这些版本都用于 NFS version 2。

当 NFS version 3 发布时,NFS 协议的文件句柄结构改变了其在线路上的格式。由于 NLM 协议与 NFS 协议共享该结构,因此需要指定一个新的 NLM 版本。NLM version 4 与 NFS version 3 一起使用。

在 NFS version 4 中,NLM 协议已被移除,加锁功能在那里由 NFS 协议本身实现。

由于该协议文档匮乏,并且协议中存在固有的竞争问题,从历史上看,要以健壮且可靠的方式实现该协议一直非常非常困难。该协议的问题经常会导致

  • 文件永久保持锁定状态(客户端忘记解锁文件)
  • 客户端挂起
  • 文件损坏(两个进程同时持有同一文件的独占锁)

上述情况通常源于网络上的重传或数据包重排序这类正常且简单的事情。 因此,许多应用程序并不依赖使用简单文件的文件锁 fcntl 调用,而是实现自己的应用程序风格文件锁,通常称为 .LOCK 文件。

协议依赖

  • ONC-RPC:NLM 协议在 ONC-RPC 之上实现,程序号为 100021。NLM 通常在 UDP 之上实现,但也存在使用 TCP 的客户端。

  • Portmap:客户端通常需要 Portmap 服务,以发现 NSM 服务在哪个端口上可用。

  • NSM:锁管理器对等端依赖 NSM 协议来相互通知服务重启/重新启动,以便在重新启动后重新同步锁。

  • KLM:在某些平台上,这是通过 loopback 接口提供的接口,内核中的 NFS 客户端代码通过它与用户空间锁管理器通信。

函数

暂且不讨论为 PC-NFS 和其他非 UNIX 客户端添加的特殊函数,该协议只实现 6 个函数:Null、Test、Lock、Unlock、Cancel 和 Granted。

  • Null:此函数是所有 ONC-RPC 服务都会使用的标准“ping”函数,它只是用于“ping”服务,以查看服务是否正常运行。

  • Test:此函数原本用于“测试”锁请求是否会成功。这在现实中并不实用,因为在 test 调用完成之后、响应到达客户端之前,服务器上的锁状态可能并且经常会发生变化。没有任何客户端实现会使用此函数。

  • Lock:此函数用于请求在服务器上取得锁。对于独占锁请求,可能会发生锁竞争(其他人已经拥有该文件的锁),并且该锁无法授予客户端。服务器不会只是报告获取锁失败并让客户端持续轮询锁,而是在这里以 BLOCKED 响应进行回复,告知客户端现在无法授予锁,但当锁可用并已分配给该客户端时,服务器会向客户端执行 Granted 回调。请注意,Granted 消息可能会在网络上丢失,因此如果客户端尚未获得被阻塞的锁,它们仍会每隔大约 10 或 20 秒重传原始请求。由于锁通常生命周期很短,许多服务器在遇到会阻塞的请求时,会将加锁请求的执行延迟几毫秒,机会性地假定稍后锁可能已经可用,从而使整个 blocked 和 granted 握手被短路。

  • Unlock:此函数会将已持有(或已阻塞)的锁释放回服务器。

  • Cancel:此函数与 Unlock 相同,许多实现中的 Cancel 只是一个简单的桩函数,无论如何都会调用 Unlock 函数。对于分析网络跟踪的人来说,这些函数实际上有所不同,并且会提供额外信息:Unlock 通常在应用程序通过 fcntl() 调用显式释放锁时使用。Cancel 通常由锁管理器在应用程序终止且未自行清理锁之后发出。也就是说,Cancel 表示持有锁的应用程序已经终止,正在由内核中的 VFS 层清理已分配资源。

  • Granted:当被阻塞的锁变为可用时,服务器使用此函数回调客户端,告知客户端它已经获得该锁。

幂等性

为了实现幂等,实现必须对所有重复请求返回相同响应,即实现最多执行一次语义。在存在重传的情况下,这会稍微影响该协议,而且并非所有实现都正确处理了这一点。这确实意味着这些函数的响应码含义会略有变化,最简单的理解方式是:响应码描述的是调用完成后服务器上的结果状态,而不是状态转换是否实际发生。如果向不存在的锁发送 Unlock 请求,就可以看到这一点:服务器仍会返回 Unlock successful,因为调用完成后锁不存在;至于该锁在调用发起之前就不存在这一点并不相关。

某些客户端实现不是幂等的,这会给服务器造成问题。特别是某些非 Solaris 的旧式 unix 实现在 Granted 调用中不是幂等的,并且会根据状态转换是否发生返回不同的响应码。服务器通过始终完全忽略 Granted 调用的响应码,并将任何以及所有响应都视为成功来规避此问题。

同步与异步 NLM

NLM 有两种风格,它们都提供相同函数:同步和异步。尽管几乎所有实现都使用同步版本,但某些较旧的传统 unixen(例如 HP-UX)确实使用异步版本。主要区别是,同步 NLM 是普通的 ONC-RPC 请求/响应协议,而异步版本是消息/消息协议。

在异步版本的 NLM 中,不会有任何 ONC-RPC 层响应;相反,NLM 响应会作为 ONC-RPC 请求消息发送回来。这意味着 ONC-RPC transaction id(XID)不能用于匹配“请求”和“响应”,也不能用于检测潜在重传。取而代之的是,每个 NLM PDU 开头的 cookie 字段用于匹配请求和响应。此 cookie 字段也存在于同步版本的 NLM 中,但在那里没有语义含义。Wireshark 有一个偏好设置,可以允许 Wireshark 基于 cookie 匹配请求和响应;该偏好设置默认关闭。

Wireshark

NLM dissector 功能完整。Wireshark 和 TShark 中的 Service Response Time 统计功能可以计算此协议的响应时间统计信息。

通过为 NLM 启用适当的偏好设置,Wireshark 甚至可以为异步 NLM 匹配 MSG 和 RES 数据包,并测量该类型 NLM 的响应时间。

偏好设置

  • NLM Preferences

  • NFS_Preferences(NLM 与 NFS 共享 filehandle 结构,因此 NFS 偏好设置也会影响 NLM)

示例捕获文件

XXX 这里需要有人捐赠一个包含 NLM 协议的示例捕获文件

显示过滤器

NLM 显示过滤器字段的完整列表可以在 display filter reference 中找到

只显示 NLM 协议:

 nlm

捕获过滤器

在捕获时无法直接按 NLM 协议过滤。由于 NLM 不使用标准端口,因此也无法按特定端口捕获。

外部链接

  • The Open Group 的 NFS 标准,其中描述了 NLM 协议的一部分

请记住,NLM 协议没有标准;该协议唯一存在的是一份描述数据包格式的接口规范。这也是该协议在历史上存在如此多问题的原因之一。

工具

  • rpcinfo 可用于“ping”服务器上的 NLM 服务。

讨论

Imported from https://wiki.wireshark.org/Network_Lock_Manager on 2020-08-11 23:17:23 UTC

相关 Wireshark Wiki 页面

网络分析技术档案