Linux UDP丢包原因分析
Causes of Linux UDP packet drops
我有一个Linux c++应用程序,它接收排序的UDP数据包。由于排序,我可以很容易地确定数据包何时丢失或重新排序,即当遇到"间隙"时。系统有一个恢复机制来处理缺口,但是,最好在一开始就避免缺口。使用一个简单的基于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数据包吗?
如果线程数多于内核数,并且它们之间的线程优先级相等,那么接收线程很可能会缺乏时间来刷新传入缓冲区。考虑以比其他线程更高的优先级运行该线程。
类似地,虽然通常效率较低,但将用于接收的线程绑定到一个核心,这样您就不会承受在核心之间切换和相关缓存刷新的开销。
我的程序也有类似的问题。它的任务是在一个线程中接收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.
我还在开发这个应用程序,所以一旦我知道更多,我会尝试更新我的帖子。
int ret = setsockopt(my_socket_fd, SOL_SOCKET, SO_RCVBUF, (char *)&sockbufsize, (int)sizeof(sockbufsize));
首先,setsockopt
接受(int, int, int, void *, socklen_t),所以不需要强制类型转换。
使用一个简单的基于libpcap的数据包嗅探器,我已经确定在硬件级别的数据中没有缺口。然而,我在我的申请中看到了很多差距。这表明内核正在丢弃数据包;
提示您的环境不够快。众所周知,包捕获是处理密集型的,并且您将观察到,当您开始捕获一个接口上的iptraffic -ng或tcpdump等程序时,接口上的全局传输速率将下降。
我没有足够的声誉来评论,但是与@racic类似,我有一个程序,其中我有一个接收线程和一个处理线程,它们之间有一个阻塞队列。我注意到丢包也有同样的问题,因为接收线程正在等待阻塞队列上的锁。
为了解决这个问题,我为接收线程添加了一个较小的本地缓冲区,并让它只将数据推入缓冲区,然后不锁定(使用std::mutex::try_lock)。
- (C++)分析树以计算返回错误值的简单算术表达式
- 从udp接收帧对于人脸识别来说太慢
- 函数复杂度分析
- 如何找出GDB的SIGTRAP核心转储的根本原因
- 我正在使用嵌套的while循环来解析具有多行的文本文件,但由于某种原因,它只通过第一行,我不知道为什么
- LNK2038、MSVS2017 MAGMA的原因列表
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 为什么它不适用于Visual 2019的原因
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- 使用指针重新分析实体
- 如何处理来自核心指南检查器的关于gsl::at的静态分析警告
- 我无法缩小此分段错误的原因
- 链表中写入访问冲突的未知原因
- 是什么原因导致它无法编译?它是声明签名还是在函数本身的实现中
- 初始化或分配空字符串文字到指向 C 中的 char 的指针或指向 C++ 中 const char 的指针的原因是什么
- 如何使用 Boost Asio 在 Android 上获取我的本地 udp IP 地址?
- 覆盖范围分析一些googletest宏显示在分布在几行上时的覆盖范围不完整 - 原因
- 为什么UDP套接字不关闭原因选择要返回
- 阿克曼终止:根本原因分析
- Linux UDP丢包原因分析