![这是一个 Linux NFS 客户端缓冲区膨胀吗?](https://linux22.com/image/631717/%E8%BF%99%E6%98%AF%E4%B8%80%E4%B8%AA%20Linux%20NFS%20%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%BC%93%E5%86%B2%E5%8C%BA%E8%86%A8%E8%83%80%E5%90%97%EF%BC%9F.png)
在 NFS 挂载上(RedHat 5.6 上的标准选项,使用古老的 2.6.18 内核),我认为大型和多个写入操作会延迟较小的读取操作。例如,如果同时运行或,则ls
在目录中执行简单操作将需要几秒钟(或几分钟)。由于 Linux 会将元数据缓存几秒钟,因此该问题有所缓解,但是当有大量数据要写入时,NFS 挂载将变得不可用。cp
dd
起初我以为这只是一个 NFS 服务器问题,但运行如下命令:
for((i=0; i<60; i++)) do
strace -f -t -o strace.$i.log time stat /mnt/nfs/data > out.$i.log 2>&1
sleep 1
if ((i == 30)); then
dd if=/dev/zero of=/mnt/nfs/data bs=1M count=1000 &
fi
done
wait
并且并行的 tcpdump 告诉我以下内容:
1)每当dd
启动时,下一个stat
发生缓存未命中的事件需要 15 秒
23261 16:41:24 munmap(0x2ad024d0e000, 4096) = 0
23261 16:41:24 lstat("/mnt/fermat_emctest/data", {st_mode=S_IFREG|0600, st_size=1048576000, ...}) = 0
23261 16:41:40 open("/proc/filesystems", O_RDONLY) = 3
2) tcpdump 显示,虽然 正在dd
运行并且WRITE
发出了调用,但未GETATTR
发送任何调用。鉴于 RPC 是异步的,我本以为会看到GETATTR
调用与 进行多路复用WRITE
,但事实并非如此。并不是 很GETATTR
慢(提交时需要几微秒),而是内核将其排在所有WRITE
s 之后。
这就是为什么stat
要花很长时间,因为它要等待内核提交调用GETATTR
。
我说得对吗?这看起来像是缓冲区膨胀问题,内核正在挨饿,stat
因为此挂载(服务器?)的客户端操作队列已满。
我认为这与我的另一个问题有某种联系如何实现与同一服务器的多个 NFS/TCP 连接?。
有没有办法调整内核 NFS 操作队列?
答案1
好的,这是我的答案。
相关https://bugzilla.redhat.com/show_bug.cgi?id=688232使用 RedHat 附带的内核 2.6.18 和 2.6.32(我还没有时间使用 vanilla 较新的内核重新验证这一点),在 NFS 客户端(v3 / tcp / 默认挂载选项)上,当有人写入文件时,内核还需要更新此文件的时间戳。在写入文件时,如果另一个进程需要此文件的元数据(例如在stat
对此文件或ls -l
其父目录中执行操作时),内核会延迟此读取器进程,直到写入完成。
在 NFS 级别,我可以看到内核GETATTR
只会在之后发出调用(我对此不确定,但在我对 5GiB 的测试中,时间stat
似乎与dd
时间相匹配)WRITE
。写入越大,等待的时间越长。
对于速度较慢的 NFS 服务器或具有大量 RAM 的服务器,延迟可能长达数分钟。当 stat(2)
进入休眠状态时,可以监视/proc/meminfo
或NFS_Unstable
显示Writeback
有多少数据传输。
我不确定内核为什么会这样做,但至少现在我理解了这种行为。因此不存在缓冲区膨胀,但某些操作是序列化的。