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

时间:2020-03-06 14:22:37  来源:igfitidea点击:

假设我们有一个从套接字读取的程序。如何将下载速率保持在特定阈值以下?

解决方案

假设网络传输是基于TCP / IP的数据包,则发送数据包以响应ACK / NACK数据包以另一种方式发送。

通过限制确认收到传入数据包的数据包速率,我们将依次降低发送新数据包的速率。

它可能有点不精确,因此监视下行速率并自适应地调整响应速率直到它落入舒适阈值之内可能是最佳的。 (这将很快发生,但是,我们每秒发送一定剂量的ack)

如果我们正在从套接字读取数据,则无法控制正在读取该套接字的操作系统缓冲区使用的带宽,并且我们所说的任何内容都不会使写入套接字的人写入更少的数据(当然,除非,我们已经为此制定了协议)。

缓慢阅读所能做的就是装满缓冲区,并最终导致网络端停顿,但是我们无法控制这种情况的发生时间。

如果我们真的想一次只读取这么多数据,则可以执行以下操作:

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

wget似乎使用--limit-rate选项进行管理。来自手册页:

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.  Eventually this strategy causes
  the TCP transfer to slow down to
  approximately the specified rate. 
  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.

在应用程序层(使用Berkeley套接字样式API),我们只需观察时钟,并以我们想要限制的速率读取或者写入数据。

如果我们平均仅读取10kbps,但源发送的数据超过该值,那么最终所有之间的缓冲区将被填满。 TCP / IP允许这样做,并且协议将安排发送方减慢速度(在应用程序层,我们可能需要知道的是,在另一端,阻止写调用将被阻止,非阻止写操作将失败,并且异步在我们读取足够的数据以允许写入之前,写入操作不会完成)。

在应用程序层,我们只能近似估计,不能保证严格的限制,例如"在任何一秒钟内,不超过10 kb的空间将通过网络中的给定点"。但是,如果我们跟踪收到的信息,从长远来看就可以得出平均值。

就像其他人所说的那样,OS内核正在管理流量,而我们只是从内核内存中读取数据的副本。为了大致限制一个应用程序的速率,我们需要延迟读取数据并允许传入的数据包在内核中缓冲,这最终会减慢传入数据包的确认速度并降低该一个套接字上的速率。

如果要减慢到计算机的所有流量,则需要调整传入TCP缓冲区的大小。在Linux中,我们可以通过更改/ proc / sys / net / ipv4 / tcp_rmem(读取内存缓冲区大小)和其他tcp_ *文件中的值来影响此更改。

就像将游戏限制为一定数量的FPS一样。

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

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

这样,我们可以确保游戏刷新率最高为FPS。
以相同的方式,DrawScene可以用作将字节泵送到套接字流中的函数。

为了增加布拉南的答案:

如果我们自愿限制接收器端的读取速度,最终队列将在两端都填满。然后,发送方将阻塞其send()调用,或者从send()调用返回,其send_length小于传递给send()调用的预期长度。

如果发件人还没有准备好通过睡眠并尝试重新发送OS缓冲区中无法容纳的内容来处理这种情况,那么我们将面临连接问题(发件人可能将其检测为错误)或者丢失数据(发件人可能在不知不觉中丢弃不适合OS缓冲区的数据)。