为什么Apache建议不要在Linux上使用带有NFS的sendfile()

问题描述 投票:2回答:1

Apache文档包含EnableSendfile的此语句:

使用网络安装的DocumentRoot(例如,NFS,SMB,CIFS,FUSE),内核可能无法通过自己的缓存提供网络文件。[1]

Apache 2.4和Nginx的默认配置禁用sendfile()。

我正在尝试找到一些具体的东西来描述在Linux上使用带有NFS文件系统的sendfile()时的确切问题。在内核3.10.0-327.36.3(CentOS 7)上运行最小测试程序验证sendfile()在源是否在NFS上时是否有效,并且它确实从页面缓存读取(第一次运行很慢,随后很快, drop_caches使它再次变慢,即从源重新读取)。我试过文件大小高达1G,一切似乎都运行正常。我假设必须有一些情况可以揭示错误的行为,但我想确切地知道那是什么。

为了比较,有一些关于VirtualBox卷与sendfile()[2]存在问题的文档,但是我找不到类似于Apache的内容,或者如何复制有问题的配置。

linux apache nginx nfs sendfile
1个回答
3
投票

Nginx的默认配置将sendfile打开 - https://github.com/nginx/nginx/blob/release-1.13.8/conf/nginx.conf#L27所以我对你在那里的陈述感到困惑。

回到21世纪初你可以看到一个Apache dev introducing the option to disable SendFile(这里是mailing list post for the patch)。还有old bugs that might have been related to sendfile over in the Apache bug tracker。从Apache bug #12893我们了解到,其中一个失败是因为Linux内核NTFS实现根本不支持sendfile系统调用:

[...]显然你的NTFS文件系统有一些特性阻止sendfile()工作。

sendfile(8, 9, [0], 9804)               = -1 EINVAL (Invalid argument)

引用您正在阅读的stackoverflow问题的blog post titled "The Mysterious Case of Sendfile and Apache"提出了以下理论:

sendfile()最多传输0x7ffff000(2,147,479,552)个字节,返回实际传输的字节数。 (在32位和64位系统上都是如此。)

有2GB的限制。现在这是假设,apache文档说:

使用网络安装的DocumentRoot(例如,NFS,SMB,CIFS,FUSE),内核可能无法通过自己的缓存提供网络文件[2]

因此,当它说'内核可能无法提供文件'时,我想我们可能会在这里指的是sendfile具有的文件大小的固有限制。

有趣的理论,但我怀疑这是答案,因为你可以简单地选择不对太大的文件使用sendfile代码路径。更新:在挖掘时我发现该帖子的作者创建了一个名为That Time I Was Wrong About Sendfile() and Apache的后续内容,其中提到了您正在阅读的答案!

还有warnings about sendfile problems in the ProFTPD documentation

有些情况下,文件系统而不是内核似乎是sendfile(2)问题的罪魁祸首:

  • 网络文件系统(例如NFS,SMBFS / Samba,CIFS)
  • 虚拟化文件系统(OpenVZ,VMware,甚至Veritas)
  • 其他文件系统(例如Linux上的NTFS和tmpfs)

同样,如果在这些文件驻留在网络或虚拟化文件系统上时遇到从ProFTPD下载文件的问题,请尝试在proftpd.conf中使用“UseSendfile off”。

很多“这里是龙”的警告。其中一些是因为文件系统根本不支持sendfile(例如until 2.4.22-pre3 Linux's tmpfs didn't support sendfile)。基于FUSE的文件系统(例如NTFS-3g)过去也会因FUSE和sendfile错误而出现问题(因为已经解决了)。虽然虚拟化文件系统列表是一个有趣的补充...

然而,OrangeFS FAQ似乎有最合理的解释:

5.16我们可以运行Apache Web服务器来提供orangefs卷上的文件吗?

你当然可以!但是,我们建议您在启动Web服务器之前关闭httpd.conf中的EnableSendfile选项。或者,您可以使用选项-enable-kernel-sendfile配置orangefs。传递此选项以在支持sendfile回调的orangefs内核模块中配置结果。但我们建议,除非提供的文件足够大,否则在性能方面可能不是一个好主意。 Apache 2.x +使用sendfile系统调用,该调用通常通过页面缓存对文件数据进行分级。在最近的2.6内核中,可以通过在文件系统中提供sendfile回调例程来避免这种情况。因此,这可以确保我们不会在这些内核上结束陈旧或不一致的缓存数据。但是,在较旧的2.4内核上,sendfile系统调用通过页面缓存对数据进行流式传输,因此数据的实际可能性是陈旧的。因此,警告sendfile系统调用的用户要小心这个细节。

类似的解释可以在Linux guest readv system call returns stale (cached) shared folder file data Virtualbox bug中阅读:

我发现使用read系统调用读取文件的程序会返回正确的数据,但那些使用readv系统调用的程序(例如我的gas版本)会读取过时的缓存数据。

[...]

使用内核函数generic_file_read_iter作为file_operations结构的.read_iter成员(在执行readv系统调用时使用.read_iter)。此函数将写入文件缓存并从中读取。但是,用于通用.read成员和读取系统调用的vbox函数sf_reg_read似乎总是绕过Linux的FS缓存。

[...]

此外,我认为类似的长期问题被报告为ticket #819,仅用于sendfile系统调用。似乎所有这些generic_file_ *函数都期望主机控制对驱动器的所有访问。

以上可能也解释了ProFTPD的问题虚拟化文件系统列表。

Summary (best guess)

Apache建议不要将sendfile()与Linux NFS一起使用,因为他们的软件很受欢迎,并且在使用较旧的Linux NFS客户端调试与sendfile相关的错误时会引发很多痛苦。警告是旧的,它可能更容易保持原样,而不是更新它与所有警告。

如果你有一个Linux文件系统可以更改底层数据而不会使Linux页面缓存失效,那么在旧的Linux内核上使用sendfile是不明智的(这解释了旧的Linux NFS客户端问题)。对于较新的内核,如果上述文件系统没有实现自己的sendfile挂钩又使用sendfile是不明智的(Virtualbox共享文件夹问题证明了这一点)。

最近(2.6.31及以上)Linux内核为可能面临此失效问题的文件系统提供了使用自己的sendfile实现的设施,并假设文件系统可以使用sendfile禁止错误,但需要警告!

© www.soinside.com 2019 - 2024. All rights reserved.