如果我直接将文件下载到写入速度明显慢于互联网连接下载速度的设备,会怎么样呢?
等待写入的数据是否移至缓冲区或临时存储器?
这种行为是否依赖于操作系统、浏览器或者其他什么?
如果出现了问题,我能做些什么来预防呢?
答案1
某种程度上,结果取决于操作系统和应用程序。不过,我们可以做出以下预测:
首先堆的接收窗口会填满,比网络的完整数据速率略低。它会填满慢点比网络的线路速率,由于TCP慢启动算法以及 TCP/IP 堆栈行为方式的其他影响。
在我的 Linux 机器上,TCP 窗口最大可达 128 KiB(减去 1 字节)。(假设
sysctl net.core.rmem_max
要获取你的盒子的值。)但是,它通常小于这个最大值。我的盒子上的默认值是 4 KiB。(说sysctl net.ipv4.tcp_rmem
要获取该值。)您的应用程序将拥有一些自己的缓冲。它可能只有 1 个字节,但不能为零。Linux 需要一个零拷贝系统调用
recvfile()
来避免对应用程序缓冲的需求,但它缺乏这一点。缓冲区大小完全由应用程序程序员决定。在我编写的程序中,我使用过大约十几个字节到 64 KiB 的缓冲区,具体取决于应用程序的需求。通过观察其他应用程序的行为,我推断出它们使用了更大的缓冲区(大约 1 MiB)。
应用程序几乎肯定会使用某种缓冲 I/O 机制来写入文件,例如C的标准输入输出。这通常至少为 1 KiB,也可能是几 KiB。在我的盒子上,它似乎默认为 8 KiB。
应用程序可能正在使用无缓冲的 I/O,或者持续冲洗I/O 缓冲区到磁盘,但这种情况并不常见。
存储设备的设备驱动程序可能有一些缓冲。可能不多,但单个 4 KiB页缓冲区不会不合理。
存储设备本身几乎肯定有一些缓存。例如,现代硬盘驱动器的缓存大小约为几十兆字节。如果你正在写入 RAID 设备,可能会有更大的缓存回写缓存, 也。
全部五个这些缓冲区必须填满,底层存储设备的原始 I/O 性能才会产生影响。由于它们很容易加起来达到 100 MiB 或更多,因此如果您想确保不只是测试这些缓冲区的组合行为,则需要使用大于此的传输大小进行测试。
讲完这些之后,我会回答你的顶级问题:只要你使用的网络协议是流量控制机制 — 例如TCP— 您假设的场景应该不会产生任何问题。但是,如果您使用的是不可靠网络协议,例如UDP,并且构建在其之上的应用协议没有提供自己的流量控制机制,在这种情况下应用程序可能会被迫丢弃数据包。
答案2
我期望发生一到两件事:
请求数据的进程会将“多余的”数据缓冲在内存中。
或者更有可能的是:
请求数据的进程只有在能够处理数据时才会请求数据,这样下载速度就会有效地降低到设备的写入速度。
实际发生的情况取决于执行下载和写入操作的应用程序,因此,除非您心中有一个特定的应用程序,否则问题的第二部分是无法回答的。
答案3
操作系统/应用程序只会限制下载速度。只需从 1Gbit LAN 下载文件到旧的 USB1 闪存盘,您就能亲眼见证。
答案4
如果底层协议是 TCP(例如 HTTP),那么就不会有问题。您的下载程序在内存中有一个缓冲区,用于临时存储已下载的数据。它会不断将数据从该缓冲区写入磁盘。如果磁盘速度很慢,则缓冲区将变满,下载程序将不会要求操作系统从远程服务器接收更多数据。这意味着 Windows TCP 驱动程序中的类似缓冲区已填满。TCP 协议保证如果某人的缓冲区已满,您不会遇到问题:
http://en.wikipedia.org/wiki/Transmission_Control_Protocol#Flow_control
TCP 使用端到端流量控制协议,以避免发送方发送数据太快,以致 TCP 接收方无法可靠地接收和处理数据。在网络速度各异的机器进行通信的环境中,流量控制机制至关重要。例如,如果 PC 向智能手机发送数据,而智能手机正在缓慢地处理接收的数据,则智能手机必须调节数据流,以免不堪重负。
TCP 使用滑动窗口流量控制协议。在每个 TCP 段中,接收方在接收窗口字段中指定它愿意为连接缓冲的额外接收数据量(以字节为单位)。发送主机只能发送最多该数量的数据,然后必须等待接收主机的确认和窗口更新。
因此,当 TCP 驱动程序的缓冲区已满时,它不会向另一台计算机确认它已准备好接收更多数据。
如果底层协议是更特殊/专有的东西,那么所有的赌注都将失效 - 因为这是 TCP 的一个特性。