如何监控Linux UDP缓冲区可用空间?

时间:2010-02-18 15:39:27

标签: linux networking udp

我在linux上有一个java应用程序,它打开UDP套接字并等待消息。

在重负载下经过几个小时后,就会丢包,即数据包是由内核接收的,而不是我的应用程序接收的(我们在嗅探器中看到丢失的数据包,我们看到netstat中的UDP数据包丢失,我们没有在我们的应用程序日志中查看这些数据包。)

我们尝试扩大套接字缓冲区,但这没有用 - 我们之前开始丢失数据包,但就是这样。

对于调试,我想知道OS udp缓冲区在任何给定时刻的完整程度。谷歌搜索,但没有找到任何东西。你能救我吗?

P.S。大家好,我知道UDP不可靠。但是 - 我的计算机接收所有UDP消息,而我的应用程序无法使用其中一些消息。我想优化我的应用程序,这就是问题的原因。谢谢。

4 个答案:

答案 0 :(得分:56)

UDP是一种非常可行的协议。对于正确的工作,这是正确的工具的旧案例!

如果你有一个等待UDP数据报的程序,然后在返回等待另一个程序之前去处理它们,那么你经过的处理时间总是要比数据报的最坏情况到达速度快。如果不是,则UDP套接字接收队列将开始填充。

短暂爆发可以容忍这种情况。队列完全按照预期的方式执行 - 在您准备好之前排队数据报。但如果平均到达率经常导致队列积压,那么是时候重新设计你的程序了。这里有两个主要选择:通过狡猾的编程技术减少处理时间,和/或多线程程序。也可以使用跨程序的多个实例的负载平衡。

如上所述,在Linux上,您可以检查proc文件系统以获取有关UDP的最新状态。例如,如果我cat /proc/net/udp节点,我会得到这样的结果:

$ cat /proc/net/udp   
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode ref pointer drops             
  40: 00000000:0202 00000000:0000 07 00000000:00000000 00:00000000 00000000     0        0 3466 2 ffff88013abc8340 0           
  67: 00000000:231D 00000000:0000 07 00000000:0001E4C8 00:00000000 00000000  1006        0 16940862 2 ffff88013abc9040 2237    
 122: 00000000:30D4 00000000:0000 07 00000000:00000000 00:00000000 00000000  1006        0 912865 2 ffff88013abc8d00 0         

由此可以看出,用户ID为1006的套接字正在侦听端口0x231D(8989),接收队列大约为128KB。因为128KB是我系统上的最大大小,这告诉我我的程序在跟上到达的数据报时非常弱。到目前为止已经有2237个丢弃,这意味着UDP层不能再将任何数据报放入套接字队列,并且必须删除它们。

您可以随时查看您的计划的行为,例如使用:

watch -d 'cat /proc/net/udp|grep 00000000:231D'

另请注意,netstat命令的作用大致相同:netstat -c --udp -an

我的weenie程序的解决方案将是多线程的。

干杯!

答案 1 :(得分:35)

Linux提供文件/proc/net/udp/proc/net/udp6,其中列出了所有打开的UDP套接字(分别用于IPv4和IPv6)。在这两个列中,列tx_queuerx_queue以字节为单位显示传出和传入队列。

如果一切按预期工作,您通常不会在这两列中看到任何不同于0的值:一旦您的应用程序生成数据包,它们就会通过网络发送,并且一旦这些数据包从网络到达您的应用程序将唤醒并接收它们(recv呼叫立即返回)。如果您的应用程序已打开套接字但未调用rx_queue来接收数据,或者它没有足够快地处理此类数据,您可能会看到recv上升。

答案 2 :(得分:4)

rx_queue会告诉你任何给定时刻的队列长度,但它不会告诉你队列有多长,即高水位线。无法持续监控此值,也无法以编程方式获取该值(请参阅How do I get amount of queued data for UDP socket?)。

我可以想象监控队列长度的唯一方法是将队列移动到您自己的程序中。换句话说,启动两个线程 - 一个是尽可能快地读取套接字并将数据报转储到队列中;另一个是你的程序从这个队列拉出并处理数据包。这当然假设您可以确保每个线程都在一个单独的CPU上。现在,您可以监控自己队列的长度并跟踪高水位线。

答案 3 :(得分:0)

这个过程很简单:

  1. 如果需要,请暂停申请流程。

  2. 打开UDP套接字。如有必要,您可以使用/proc/<PID>/fd从正在运行的进程中获取它。或者您可以将此代码添加到应用程序本身并向其发送信号 - 当然,它已经打开了套接字。

  3. 尽快调用recvmsg

  4. 计算你得到的包/字节数。

  5. 这将丢弃当前缓冲的任何数据报,但如果这会破坏您的应用程序,那么您的应用程序已经被破坏。