在 UDP 套接字上动态更改 SOL_SOCKET、SO_RCVBUF 的可预测行为是什么?

What is the predictable behavior of changing SOL_SOCKET, SO_RCVBUF on the fly on a UDP socket?

如果我们在 Linux 系统上即时调整 UDP 服务器套接字的输入缓冲区大小,应该会发生什么?

setsockopt(sock, SOL_SOCKET, SO_RCVBUF, ...)

我对这些问题特别感兴趣:

首先也是最重要的:术语“缓冲区”可能令人困惑:内核实际上并不将数据包保存在固定大小的缓冲区中,而是保存在称为“积压”的队列中(参见 include/net/sock.h:400)。通过SO_RCVBUFSO_SNDBUF设置的大小只限制了backlog的最大大小。

If I shrink below what is currently in the buffer, would this simply drop the oldest/newest?

不,已经收到的会保留。没有数据报被丢弃。当您执行 setsockopt(SO_RECVBUF) 时唯一发生的事情是 the value of the sk_rcvbuf field of the socket is changed。没有执行其他操作。

只有当接收到更多的数据包时才会看到真正的效果:所有后续接收到的数据报将被立即丢弃,并且将继续被丢弃直到队列缩小到设定大小以下(即用户空间接收到足够的数据报)。

Would shrinking the buffer even save memory or something prevents that memory from being reused by the system?

正如我之前所说,由于“缓冲区”并不是真正的缓冲区并且没有固定大小,因此更改 SO_RECVBUF 不会立即更改任何内容。

有两种情况:

  1. 如果积压大小低于(或等于)指定大小:新的最大大小将受到限制,因此它将节省内存未来 以可能丢失数据包为代价。
  2. 如果积压大小高于指定大小:当用户空间收到缓冲数据包时,内存最终将被释放,并且不会再次增长超过设定值。这将在将来再次节省内存。

Is the behavior predictable or could it behave randomly at times?

看看内核代码,我会说 100% 可以按照我上面描述的方式进行预测。但是我不完全确定这可能被记录在哪里。如果您将发送和接收“缓冲区”视为队列(它们实际上是),我会说这很直观。