【问题标题】:Why does Apache recommend against using sendfile() with NFS on Linux为什么 Apache 建议不要在 Linux 上将 sendfile() 与 NFS 一起使用
【发布时间】:2018-03-04 04:12:51
【问题描述】:

Apache 文档包括 EnableSendfile 的以下语句:

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

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

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

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

【问题讨论】:

    标签: linux apache nginx nfs sendfile


    【解决方案1】:

    我想我知道 Nginx 可能会在哪里引起一些混乱。

    Nginx 文档在https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/ 上说如下:

    “默认情况下,NGINX 自己处理文件传输,并在发送之前将文件复制到缓冲区。启用 sendfile 指令消除了将数据复制到缓冲区的步骤,并允许直接将数据从一个文件描述符复制到另一个文件描述符。”

    这听起来像是 Nginx 默认不使用 sendfile。

    但是,如本答案所述,Nginx 的默认配置文件显式打开了对开箱即用 HTTP 服务器的 sendfile 支持,如 https://github.com/nginx/nginx/blob/master/conf/nginx.conf 所示。

    【讨论】:

      【解决方案2】:

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

      早在 2000 年代初期,您就可以看到 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 网络服务器来提供来自 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 成员和 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 实现的工具,并且假设文件系统可以与sendfile 一起使用,除非出现错误,但警告购买者!

      【讨论】:

        猜你喜欢
        • 2020-01-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多