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

时间:2017-09-22 14:29:37

标签: linux apache nginx nfs sendfile

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的内容,或者如何复制有问题的配置。

2 个答案:

答案 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所示。