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的内容,或者如何复制有问题的配置。
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的问题虚拟化文件系统列表。
Apache建议不要将sendfile()
与Linux NFS一起使用,因为他们的软件很受欢迎,并且在使用较旧的Linux NFS客户端调试与sendfile
相关的错误时会引发很多痛苦。警告是旧的,它可能更容易保持原样,而不是更新它与所有警告。
如果你有一个Linux文件系统可以更改底层数据而不会使Linux页面缓存失效,那么在旧的Linux内核上使用sendfile
是不明智的(这解释了旧的Linux NFS客户端问题)。对于较新的内核,如果上述文件系统没有实现自己的sendfile
挂钩又使用sendfile
是不明智的(Virtualbox共享文件夹问题证明了这一点)。
最近(2.6.31及以上)Linux内核为可能面临此失效问题的文件系统提供了使用自己的sendfile
实现的设施,并假设文件系统可以使用sendfile
禁止错误,但需要警告!