简体   繁体   English

如何对IO操作进行速率限制?

[英]How do you rate-limit an IO operation?

Suppose you have a program which reads from a socket. 假设您有一个从套接字读取的程序。 How do you keep the download rate below a certain given threshold? 如何将下载速率保持在某个给定阈值以下?

At the application layer (using a Berkeley socket style API) you just watch the clock, and read or write data at the rate you want to limit at. 在应用程序层(使用Berkeley套接字样式API),您只需观察时钟,以您想要限制的速率读取或写入数据。

If you only read 10kbps on average, but the source is sending more than that, then eventually all the buffers between it and you will fill up. 如果你平均只读取10kbps,但是源发送的数量超过了这个,那么最终它和你之间的所有缓冲区都将填满。 TCP/IP allows for this, and the protocol will arrange for the sender to slow down (at the application layer, probably all you need to know is that at the other end, blocking write calls will block, nonblocking writes will fail, and asynchronous writes won't complete, until you've read enough data to allow it). TCP / IP允许这样做,并且协议将安排发送器减速(在应用层,可能你需要知道的是,在另一端,阻塞写入调用将阻塞,非阻塞写入将失败,并且异步写入将无法完成,直到您已阅读足够的数据才能允许它。

At the application layer you can only be approximate - you can't guarantee hard limits such as "no more than 10 kb will pass a given point in the network in any one second". 在应用层,您只能是近似值 - 您不能保证硬限制,例如“不超过10 kb将在任何一秒内通过网络中的给定点”。 But if you keep track of what you've received, you can get the average right in the long run. 但是如果你跟踪你收到的内容,从长远来看你可以获得平均水平。

Assuming a network transport, a TCP/IP based one, Packets are sent in response to ACK/NACK packets going the other way. 假设网络传输是基于TCP / IP的网络传输,则响应于以另一种方式进行的ACK / NACK分组发送分组。

By limiting the rate of packets acknowledging receipt of the incoming packets, you will in turn reduce the rate at which new packets are sent. 通过限制确认收到传入数据包的数据包速率,您将反过来降低新数据包的发送速率。

It can be a bit imprecise, so its possibly optimal to monitor the downstream rate and adjust the response rate adaptively untill it falls inside a comfortable threshold. 它可能有点不精确,因此它可能是最佳的,以监控下游速率并自适应地调整响应速率,直到它落入舒适的阈值内。 ( This will happen really quick however, you send dosens of acks a second ) (这会很快发生,但你会发送一秒钟的剂量)

If you're reading from a socket, you have no control over the bandwidth used - you're reading the operating system's buffer of that socket, and nothing you say will make the person writing to the socket write less data (unless, of course, you've worked out a protocol for that). 如果您正在从套接字读取,则无法控制所使用的带宽 - 您正在读取该套接字的操作系统缓冲区,而您所说的任何内容都不会使写入套接字的人写入更少的数据(当然,除非,你已经为此制定了一个协议。

All that reading slowly would do is fill up the buffer, and cause an eventual stall on the network end - but you have no control of how or when this happens. 慢慢阅读的所有内容都是填充缓冲区,并导致网络端最终失速 - 但您无法控制这种情况发生的方式或时间。

If you really want to read only so much data at a time, you can do something like this: 如果你真的想一次只读取这么多数据,你可以这样做:

ReadFixedRate() {
  while(Data_Exists()) {
    t = GetTime();
    ReadBlock();
    while(t + delay > GetTime()) {
      Delay()'
    }
  }
}

It is like when limiting a game to a certain number of FPS. 就像将游戏限制在一定数量的FPS时一样。

extern int FPS;
....    
timePerFrameinMS = 1000/FPS;

while(1) {
time = getMilliseconds();
DrawScene();
time = getMilliseconds()-time;
if (time < timePerFrameinMS) {
   sleep(timePerFrameinMS - time);
}
}

This way you make sure that the game refresh rate will be at most FPS. 这样您就可以确保游戏刷新率最多为FPS。 In the same manner DrawScene can be the function used to pump bytes into the socket stream. 以相同的方式,DrawScene可以是用于将字节泵入套接字流的函数。

Set small socket send and receive buffers, say 1k or 2k, such that the bandwidth*delay product = the buffer size. 设置小套接字发送和接收缓冲区,比如1​​k或2k,这样带宽*延迟乘积=缓冲区大小。 You may not be able to get it small enough over fast links. 您可能无法通过快速链接获得足够小的内容。

wget seems to manage it with the --limit-rate option. wget似乎使用--limit-rate选项来管理它。 Here's from the man page: 这是手册页:

Note that Wget implements the limiting by sleeping the appropriate amount of time after a network read that took less time than specified by the rate. 请注意,Wget通过在网络读取后休眠适当的时间来实现限制,该时间花费的时间少于速率指定的时间。 Eventually this strategy causes the TCP transfer to slow down to approximately the specified rate. 最终,此策略会导致TCP传输速度降至大约指定的速率。 However, it may take some time for this balance to be achieved, so don't be surprised if limiting the rate doesn't work well with very small files. 但是,可能需要一些时间来实现这种平衡,因此如果限制速率对于非常小的文件不能很好地工作,也不要感到惊讶。

As other have said, the OS kernel is managing the traffic and you are simply reading a copy of the data out of kernel memory. 正如其他人所说,操作系统内核正在管理流量,而您只是从内核内存中读取数据的副本。 To roughly limit the rate of just one application, you need to delay your reads of the data and allow incoming packets to buffer up in the kernel, which will eventually slow the acknowledgment of incoming packets and reduce the rate on that one socket. 要大致限制一个应用程序的速率,您需要延迟读取数据并允许传入的数据包在内核中缓冲,这最终会减慢传入数据包的确认速度并降低该套接字的速率。

If you want to slow all traffic to the machine, you need to go and adjust the sizes of your incoming TCP buffers. 如果要减慢计算机的所有流量,则需要调整传入TCP缓冲区的大小。 In Linux, you would affect this change by altering the values in /proc/sys/net/ipv4/tcp_rmem (read memory buffer sizes) and other tcp_* files. 在Linux中,您可以通过更改/ proc / sys / net / ipv4 / tcp_rmem(读取内存缓冲区大小)和其他tcp_ *文件中的值来影响此更改。

To add to Branan's answer: 添加到Branan的答案:

If you voluntarily limit the read speed at the receiver end, eventually queues will fill up at both end. 如果你自愿限制接收端的读取速度,最终队列将在两端填满。 Then the sender will either block in its send() call or return from the send() call with a sent_length less than the expected length passed on to the send() call. 然后发送方将阻塞其send()调用或从send()调用返回,其sent_length小于传递给send()调用的预期长度。

If the sender is not ready to deal with this case by sleeping and trying to resend what has not fit into OS buffers, you will ending up have connection issues (the sender may detect this as an error) or losing data (the sender may unknowingly discard data the did not fit into OS buffers). 如果发件人还没准备好通过休眠处理这种情况并尝试重新发送不适合OS缓冲区的内容,那么最终会出现连接问题(发件人可能会将此检测为错误)或丢失数据(发件人可能在不知不觉中)丢弃不适合OS缓冲区的数据)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM