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的内容,或者如何复制有问题的配置。
答案 0 :(得分: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]
因此,当它说'内核可能无法提供文件时'#39;我想我们可能在这里指的是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下载文件的问题,请尝试使用" UseSendfile off"在proftpd.conf中。
很多"这里是龙"警告。其中一些是因为文件系统根本不支持sendfile(例如until 2.4.22-pre3 Linux's tmpfs didn't support sendfile)。基于FUSE的文件系统(例如NTFS-3g)过去也会因FUSE和sendfile错误而出现问题(因为已经解决了)。虽然虚拟化文件系统列表是一个有趣的补充...
然而,OrangeFS FAQ似乎有最合理的解释:
5.16我们可以运行Apache网络服务器来提供关于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缓存。
[...]
此外,我认为类似的长期问题报告为票证#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
实现的功能,并假设文件系统可以使用{{1}除了臭虫,但需要警告!
答案 1 :(得分:0)
我认为我认为Nginx可能会引起一些混乱。
Nginx文档在https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/处说明以下内容:
“默认情况下,NGINX会自行处理文件传输,并在发送文件之前将文件复制到缓冲区中。启用sendfile指令消除了将数据复制到缓冲区中的步骤,并使数据可以直接从一个文件描述符复制到另一个文件描述符。”
这听起来好像Nginx默认不使用sendfile。
但是,如该答案所述,Nginx的默认配置文件显式打开了对开箱即用HTTP服务器的发送文件支持,如https://github.com/nginx/nginx/blob/master/conf/nginx.conf所示。