本页目录

NFS (Network File System)

覆盖: NFSv3/v4 协议 → SUNRPC 层 → client/server 架构 → caching (attribute/delegation) → pNFS → Kerberos 安全 → nfsd 内核版本: 2.6 ~ 6.x

概述

NFS 是 Unix/Linux 最传统的网络文件系统。NFSv4 (2003) 是对 v3 的重大重写:引入了状态 (locking + delegation)、复合操作 (compound RPC)、伪文件系统 (pseudo-fs) 和更好的安全模型。

Linux NFS 实现分为两个独立模块:

  • nfsd (server, fs/nfsd/): 导出本地文件系统到网络
  • nfs (client, fs/nfs/): 挂载远程文件系统

SUNRPC 层

NFS 使用 SUNRPC (ONC RPC) 作为传输层:

// net/sunrpc/
// RPC 客户端: 发送请求 → 等待应答
struct rpc_clnt {
    struct rpc_program *cl_program;    // NFS (100003) 或其他程序
    struct rpc_version *cl_versions;   // NFSv3 (3) or NFSv4 (4)
    struct rpc_xprt    *cl_xprt;       // 传输: TCP/UDP/RDMA
};

// RPC 传输:
//   TCP (默认): 可靠, 内建流量控制
//   UDP:       轻量但有丢包风险 (v3 only)
//   RDMA:      高性能 (InfiniBand/RoCE)

NFS Client

挂载与伪文件系统

NFSv3 mount:
  mount -t nfs server:/export/path /mnt
    → MOUNT protocol (portmapper) 获取初始 filehandle
    → 后续操作用 NFS protocol (port 2049)

NFSv4 mount:
  mount -t nfs4 server:/ /mnt
    → 伪文件系统: 服务端导出根, 客户端通过遍历到达子导出
    → 不需要辅助的 MOUNT 协议
    → /etc/exports 的 fsid=0 标记 root export

Attribute Cache

// NFS 客户端缓存文件属性 (stat, permission 等)
// 属性缓存时间由 acregmin/acregmax/acdirmin/acdirmax 控制

// close-to-open consistency:
//   这是 NFS 的默认一致性模型:
//   1. open() → 强制重新验证属性 (GETATTR)
//   2. 未打开的文件: 属性在缓存期内复用
//   3. close() → 回写所有修改
//   4. 另一个客户端 open 同一个文件 → 看到完整的修改

// NFSv4 delegation (委托):
//   服务端可以把文件的读写权"委托"给客户端
//   客户端独占读/写 → 本地缓存属性 + 数据 → 零 RPC!
//   另一个客户端想访问 → 服务端先 revoke 委托 → 第一个客户端刷新回服务端
//   类似分布式系统中的 lease 机制

Page Cache 与回写

NFS 客户端的 page cache:
  读: page cache 命中 → 不发起 RPC → 本地服务
  写: 先写 page cache → 异步 WRITE RPC → 后台回写

NFS COMMIT:
  fsync → COMMIT RPC → 服务端确保写入稳定存储
  → 服务端可能用 NFS write delegation 加速

NFSv4.1+ pNFS (Parallel NFS):
  元数据走 MDS (Metadata Server)
  数据直接走 DS (Data Server)
  → 绕过 MDS 的带宽瓶颈

NFS Server (nfsd)

// fs/nfsd/
// 内核 NFS server: nfsd 内核线程池

// 导出文件系统:
//   /etc/exports:
//     /export *(rw,sync,no_subtree_check)
//   exportfs -a → 通知内核

// nfsd 线程:
//   每个 CPU 有一个 nfsd 内核线程
//   nfsd 循环: 取 RPC 请求 → 执行 NFS 操作 → 返回应答
//   线程数: /proc/fs/nfsd/threads

// NFSv4 state:
//   服务端维护 client 状态 (open files, locks, delegations)
//   状态存储: 在 /var/lib/nfs/nfsdcltrack (或 v4.2+ 基于文件系统)
//   客户端崩溃 → state 在 lease 时间后自动回收

安全模型

NFSv3: 
  基于 AUTH_SYS (UNIX credential: uid/gid/aux gids)
  → 信任客户端提供的 uid/gid (不安全!)
  → root_squash 缓解 (把 root 映射为 nobody)
  → sec=sys (默认), sec=krb5 (可选)

NFSv4:
  支持 Kerberos 5 (RPCSEC_GSS)
    sec=krb5:    仅认证
    sec=krb5i:   认证 + 完整性 (防止篡改, 签名)
    sec=krb5p:   认证 + 完整性 + 隐私 (加密数据)

  强制要求: NFSv4.2+ 服务器支持 sec=krb5

TASK_KILLABLE 与 NFS

// NFS 是 TASK_KILLABLE 引入的主要动机之一
// 经典问题: 挂载了 NFS 但服务器不可达
//   → 进程访问 NFS 挂载点 → 进入 TASK_UNINTERRUPTIBLE (D 状态)
//   → SIGKILL 无效 (D 状态不响应信号!)
//   → 只能重启
//
// TASK_KILLABLE 的解决 (5.x+):
//   某些等待路径使用 wait_event_killable()
//   → 等待时检查 fatal_signal_pending()
//   → SIGKILL 可以打断
//   但不是所有 NFS 路径都改成了 KILLABLE

调试

# NFS client 统计
cat /proc/self/mountstats  # 详细 RPC 统计
nfsstat -c                  # 客户端

# NFS server 统计
nfsstat -s                  # 服务端

# 查看 NFS 挂载选项
mount | grep nfs
cat /proc/mounts | grep nfs

# 追踪 NFS RPC
rpcdebug -m nfs -s all      # 开启 NFS client 调试
rpcdebug -m nfsd -s all     # 开启 NFS server 调试

参考与延伸

  • 内核文档⁠: Documentation/filesystems/nfs/
  • RFC: RFC 7530 (NFSv4), RFC 5661 (NFSv4.1/pNFS), RFC 7862 (NFSv4.2)
  • 源码⁠:
    • fs/nfs/ — client 实现
    • fs/nfsd/ — server 实现
    • net/sunrpc/ — RPC 层
    • include/linux/sunrpc/ — RPC 类型定义

关键词: NFSv4, SUNRPC, delegation, close-to-open, pNFS, nfsd, TASK_KILLABLE, Kerberos, RPCSEC_GSS