我有一个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数据包吗?
FWIW,这是在CentOS 4上,内核为2.6.9-89.0.25.ELlargesmp。
答案 0 :(得分:7)
如果你有比核心更多的线程和它们之间相等的线程优先级,那么很可能接收线程缺乏时间来刷新传入的缓冲区。考虑以比其他线程更高的优先级运行该线程。
类似地,尽管通常效率较低的是将线程绑定到一个核心,这样您就不会在核心和相关的缓存刷新之间进行切换。
答案 1 :(得分:3)
我的程序遇到了类似的问题。它的任务是在一个线程中接收udp数据包,并使用阻塞队列,用另一个线程将它们写入数据库。
我注意到(使用vmstat 1
)当系统遇到繁重的I / O等待操作(读取)时,我的应用程序没有收到数据包,但系统正在接收它们。
问题是 - 当发生繁重的I / O等待时,写入数据库的线程在持有队列互斥锁时正在I / O缺乏。这样udp缓冲区就会被传入的数据包溢出,因为接收它们的主线程挂在pthred_mutex_lock()
上。
我通过使用我的进程的ioniceness(ionice
命令)和数据库进程来解决它。将I / O sched类更改为Best Effort有帮助。令人惊讶的是,即使使用默认的I / O优点,我也无法重现此问题。
我的内核是2.6.32-71.el6.x86_64。
我还在开发这个应用程序,所以一旦我了解更多,我会尝试更新我的帖子。
答案 2 :(得分:1)
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等程序时,接口上的全局传输速率将会下降。
答案 3 :(得分:0)
我没有足够的声誉来评论,但与@racic类似,我有一个程序,我有一个接收线程,一个处理线程,它们之间有阻塞队列。我注意到丢弃数据包的问题同样是因为接收线程正在等待阻塞队列的锁定。
为了解决这个问题,我在接收线程中添加了一个较小的本地缓冲区,并且只将数据推送到缓冲区然后它没有被锁定(使用std :: mutex :: try_lock)。