Linux UDP丢包的原因
Posted
技术标签:
【中文标题】Linux UDP丢包的原因【英文标题】:Causes of Linux UDP packet drops 【发布时间】:2011-08-20 06:37:08 【问题描述】:我有一个接收有序 UDP 数据包的 Linux C++ 应用程序。由于排序,我可以很容易地确定数据包何时丢失或重新排序,即何时遇到“间隙”。系统具有处理间隙的恢复机制,但是,最好首先避免间隙。使用一个简单的基于 libpcap 的数据包嗅探器,我确定硬件级别的数据没有间隙。但是,我在我的申请中看到了很多空白。这表明内核正在丢弃数据包;通过查看 /proc/net/snmp 文件可以确认。当我的应用程序遇到间隙时,Udp InErrors 计数器会增加。
在系统层面,我们增加了最大接收缓冲区:
# sysctl net.core.rmem_max
net.core.rmem_max = 33554432
在应用程序级别,我们增加了接收缓冲区大小:
int sockbufsize = 33554432
int ret = setsockopt(my_socket_fd, SOL_SOCKET, SO_RCVBUF,
(char *)&sockbufsize, (int)sizeof(sockbufsize));
// check return code
sockbufsize = 0;
ret = getsockopt(my_socket_fd, SOL_SOCKET, SO_RCVBUF,
(char*)&sockbufsize, &size);
// print sockbufsize
在调用 getsockopt() 之后,打印的值始终是设置值的 2 倍(上例中为 67108864),但我相信这是意料之中的。
我知道未能足够快地使用数据会导致数据包丢失。然而,这个应用程序所做的只是检查排序,然后将数据推送到队列中;实际处理在另一个线程中完成。此外,这台机器很现代(双 Xeon X5560,8 GB RAM)并且负载非常轻。我们实际上有几十个相同的应用程序以更高的速率接收数据,它们没有遇到这个问题。
除了消耗太慢的应用程序之外,还有其他原因会导致 Linux 内核丢弃 UDP 数据包吗?
FWIW,这是在 CentOS 4 上,内核为 2.6.9-89.0.25.ELlargesmp。
【问题讨论】:
检查您的电缆,会发生位错误,尤其是当您将电缆缠绕在电噪声源上时。可能是一个有问题的驱动程序/网卡,看看你是否可以关闭/打开校验和卸载。请记住,交换机等网络元素可能会丢弃数据包。用wireshark监控你的流量并寻找可疑的东西。您可能需要对您的应用进行测试,以验证您在任何地方都没有出现过多的延迟(例如,在互斥体上等待的时间过长),因此您确定自己的阅读速度足够快。 pcap 应用程序确认 NIC 正在接收所有数据包,因此这不是接线或交换机问题。至于程序本身,我使用的代码与许多其他应用程序共享的完全相同。此连接是唯一有问题的连接。 入站校验和是否不正确? 【参考方案1】:如果您的线程数多于内核数并且它们之间的线程优先级相同,则接收线程很可能需要时间来刷新传入缓冲区。考虑以比其他线程更高的优先级运行该线程。
类似地,虽然通常效率较低,但将接收线程绑定到一个内核,这样您就不会承受在内核之间切换和相关缓存刷新的开销。
【讨论】:
这听起来确实像是读取线程在这里被饿死了。不过,可能是系统中的其他进程导致了饥饿。 澄清一下:“刷新传入缓冲区”是指跟上传入消息的速度,还是有更微妙的含义? @aix: 是的,基本上读取套接字上所有可用的内容。 如果您运行top
并键入1
以启用每CPU 显示,是否有任何CPU/内核显示0% 空闲时间?如果是这样,那么接收线程饥饿很可能是问题所在。【参考方案2】:
我的程序也有类似的问题。它的任务是在一个线程中接收 udp 数据包,并使用阻塞队列,通过另一个线程将它们写入数据库。
我注意到(使用vmstat 1
)当系统遇到繁重的 I/O 等待操作(读取)时,我的应用程序没有收到数据包,但系统正在接收它们。
问题是 - 当大量 I/O 等待发生时,正在写入数据库的线程在持有队列互斥锁时 I/O 不足。这样 udp 缓冲区就会被传入的数据包溢出,因为接收它们的主线程挂在 pthred_mutex_lock()
上。
我通过使用我的进程和数据库进程的 ioniceness(ionice
命令)来解决它。将 I/O 调度类更改为 Best Effort 有所帮助。令人惊讶的是,即使使用默认的 I/O,我现在也无法重现此问题。
我的内核是 2.6.32-71.el6.x86_64。
我仍在开发这个应用程序,所以我会在了解更多信息后尝试更新我的帖子。
【讨论】:
【参考方案3】:int ret = setsockopt(my_socket_fd, SOL_SOCKET, SO_RCVBUF, (char *)&sockbufsize, (int)sizeof(sockbufsize));
首先,setsockopt
采用 (int, int, int, void *, socklen_t),因此不需要强制转换。
使用简单的基于 libpcap 的数据包嗅探器,我确定硬件级别的数据没有间隙。但是,我在我的申请中看到了很多空白。这表明内核正在丢弃数据包;
这表明您的环境不够快。众所周知,数据包捕获是处理密集型的,当您开始在一个接口上捕获诸如 iptraf-ng 或 tcpdump 之类的程序时,您会观察到接口上的全局传输速率将下降。
【讨论】:
不过,他可能是从另一台机器上捕获的。 我在与应用程序相同的机器上运行 pcap 程序。但是,无论嗅探器是否正在运行,都会发生下降。换句话说,即使没有运行嗅探器,我的应用程序也会记录间隙,并且 /proc/net/snmp Udp InErrors 计数器会增加。 (在我使用嗅探器之前,我不知道 /proc/net/snmp 文件。)【参考方案4】:我没有足够的声誉来发表评论,但与@racic 类似,我有一个程序,其中我有一个接收线程和一个处理线程,它们之间有一个阻塞队列。我注意到丢弃数据包的同样问题,因为接收线程正在等待阻塞队列上的锁。
为了解决这个问题,我向接收线程添加了一个较小的本地缓冲区,并让它只将数据推送到缓冲区然后它没有被锁定(使用 std::mutex::try_lock)。
【讨论】:
以上是关于Linux UDP丢包的原因的主要内容,如果未能解决你的问题,请参考以下文章