NFS のメンタルモデルを 5分で手に入れる

こんにちは、上原です。

Linux ではファイルシステムは VFS (Virtual File System) で仮想化されており、個別のファイルシステムの実装を気にせず使えるということになっています。

しかし果たして本当にそうでしょうか？ :thinking: 

現実には、NFS (Network File System) の挙動は直感的なファイルシステムの挙動と異なることがあります。この記事では、なぜ NFS では他のファイルシステムと異なる挙動をすることがあるのか、NFS をどういうものとしてとらえればいいのかを解説します。

## シンプルなファイルシステムのメンタルモデル
シンプルなファイルシステムのイメージは以下のようになるのではないでしょうか。ファイル I/O はユーザ空間、カーネル空間でそれぞれバッファリングされます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4086953/dcec70ef-acdc-46c1-afcf-0dccb3b77359.png" width="60%">

しかし、このようなモデルは NFS を理解するのに役立ちません。
write (2) で書き込み、fsync (2) を呼んで同期しても、異なるマシンから読み出した時に最新の情報が取れないのです。

これを理解するには、ファイルシステムをより詳細に理解する必要があります。

## I/O スタック
ファイルシステムを細かく分けると、VFS インターフェース、その下の実ファイルシステム、ボリュームマネージャ、ディスクデバイスのように分けられます。
![Screenshot-2025-05-20_12.53.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4086953/e85c6793-a1b2-485d-bf23-3a270d77338f.png)

このうち、実ファイルシステムはキャッシュを持っています。fsync (2) のようなディスクへの同期を行うシステムコールも、実際にはファイルシステムキャッシュへの書き出しまでしか保証されません。どうしてもディスクデバイスへの書き出しの同期を保証したい場合は Raw I/O を使うことができますが、通常の用途であれば Raw I/O を使う必要はないでしょう。

書き込むユーザと読み込むユーザが同一マシン上の場合、ファイルシステムキャッシュを通じて書き出し内容をリアルタイムに読み出すことができます。このため、書き込み内容が実際にはディスクデバイスに同期されていなかったとしても影響がありません。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4086953/21939311-7f94-4b0b-afdf-6eb40b71fb59.png" width="70%">

しかし NFS で書き込むユーザと読み込むユーザが異なるマシン上にいる場合、キャッシュを共有していないので、書き込んだ内容をリアルタイムに読み出すことはできません。キャッシュの期限が切れればキャッシュが更新されますが、キャッシュの期限がいつ切れるのか、それを我々ユーザが知ることはできません。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4086953/4da2c034-cff6-4649-986f-76afb632b4fc.png" width="70%">


## NFS プロトコルが保証するのは Close-To-Open Cache Consistency だけ

ではどうしたら NFS キャッシュを強制的に更新できるのでしょうか？この答えは NFS Version 4 Protocol を説明した [RFC 7530](https://datatracker.ietf.org/doc/html/rfc7530) に記述があります。

```

1.4.6.  Client Caching and Delegation

   The file, attribute, and directory caching for the NFSv4 protocol is
   similar to previous versions.  Attributes and directory information
   are cached for a duration determined by the client.  At the end of a
   predefined timeout, the client will query the server to see if the
   related file system object has been updated.

   For file data, the client checks its cache validity when the file is
   opened.  A query is sent to the server to determine if the file has
   been changed.  Based on this information, the client determines if
   the data cache for the file should be kept or released.  Also, when
   the file is closed, any modified data is written to the server.
```
ファイルを開くときと閉じるとき、それが NFS キャッシュの更新されるタイミングです。NFS が保証するこの一貫性を Close-To-Open Cache Consistency と呼ぶようです。 

write (2) や fsync(2) はファイルシステムまでの書き出しまでしか保証されません。
![Screenshot-2025-05-20_21.33.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4086953/2894df90-3b18-44e9-bb1c-5cdbe9d04dc0.png)

NFS キャッシュが同期されるタイミングは NFS プロトコルに従います。それは open (2) 及び close (2) が行われたタイミングです。
![Screenshot-2025-05-20_21.35.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4086953/22f9dbd2-9b8a-47b1-a5e9-a4d7d1c1920a.png)

## 最後に
最後までお読みいただきありがとうございます。
私たちは 408 Request Timeout というシステム会社の文芸サークルです。ゆるく技術ネタを書いたり、文学フリマに出店したりと二足の草鞋でやっていますので良ければフォローお願いします。