---
title: NFS (Network File System)
url: https://doc.liz6.com/linux-kernel/03-file-system/06-NFS
locale: zh
area: linux-kernel
tags:
- linux-kernel
- 文件系统
date: 2026-06-30
modified: 2026-06-30
description: '覆盖: NFSv3/v4 协议 → SUNRPC 层 → client/server 架构 → caching (attribute/delegation) → pNFS → Kerberos 安全 → nfsd 内核版本: 2.6 ~ 6.x'
---

# 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) 作为传输层：

```c
// 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

```c
// 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)

```c
// 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

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

---

## 调试

```bash
# 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*
