首页 > 解决方案 > 在 UDP 套接字上即时更改 SOL_SOCKET、SO_RCVBUF 的可预测行为是什么?

问题描述

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

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

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

标签: socketsnetworkinglinux-kerneludpsetsockopt

解决方案


首先也是最重要的:术语“缓冲区”可能会造成混淆:内核实际上并没有将数据包保存在固定大小的缓冲区中,而是保存在称为“积压”的队列中(请参阅 参考资料include/net/sock.h:400)。大小设置通过SO_RCVBUF并且SO_SNDBUF仅限制积压的最大大小。

如果我缩小到缓冲区中的当前值以下,这会简单地丢弃最旧/最新的吗?

不,已经收到的会被保留。没有数据报被丢弃。当您这样做时,唯一发生的事情setsockopt(SO_RECVBUF)套接字字段的值发生了sk_rcvbuf变化。不执行其他操作。

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

缩小缓冲区甚至会节省内存还是阻止内存被系统重用?

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

有两种情况:

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

行为是可预测的还是有时会随机表现?

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


推荐阅读