套接字选择中死锁的可能原因
Possible causes of a deadlock in socket select
我有一个jabber服务器应用程序,另一个jabber客户端应用程序在C++。
当客户端接收和发送大量消息(每秒超过 20 条)时,选择只会冻结并且永远不会返回。
使用netstat,套接字仍然在 linux 上连接,使用 tcpdump,消息仍然发送到客户端,但选择永远不会返回。
下面是选择的代码:
bool ConnectionTCPBase::dataAvailable( int timeout )
{
if( m_socket < 0 )
return true; // let recv() catch the closed fd
fd_set fds;
struct timeval tv;
FD_ZERO( &fds );
// the following causes a C4127 warning in VC++ Express 2008 and possibly other versions.
// however, the reason for the warning can't be fixed in gloox.
FD_SET( m_socket, &fds );
tv.tv_sec = timeout / 1000000;
tv.tv_usec = timeout % 1000000;
return ( ( select( m_socket + 1, &fds, 0, 0, timeout == -1 ? 0 : &tv ) > 0 )
&& FD_ISSET( m_socket, &fds ) != 0 );
}
僵局是 gdb:
Thread 2 (Thread 0x7fe226ac2700 (LWP 10774)):
#0 0x00007fe224711ff3 in select () at ../sysdeps/unix/syscall-template.S:82
#1 0x00000000004706a9 in gloox::ConnectionTCPBase::dataAvailable (this=0xcaeb60, timeout=<value optimized out>) at connectiontcpbase.cpp:103
#2 0x000000000046c4cb in gloox::ConnectionTCPClient::recv (this=0xcaeb60, timeout=10) at connectiontcpclient.cpp:131
#3 0x0000000000471476 in gloox::ConnectionTLS::recv (this=0xd1a950, timeout=648813712) at connectiontls.cpp:89
#4 0x00000000004324cc in glooxd::C2S::recv (this=0xc5d120, timeout=10) at c2s.cpp:124
#5 0x0000000000435ced in glooxd::C2S::run (this=0xc5d120) at c2s.cpp:75
#6 0x000000000042d789 in CNetwork::run (this=0xc56df0) at src/Network.cpp:343
#7 0x000000000043115f in threading::ThreadManager::threadWorker (data=0xc56e10) at src/ThreadManager.cpp:15
#8 0x00007fe2249bc9ca in start_thread (arg=<value optimized out>) at pthread_create.c:300
#9 0x00007fe22471970d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#10 0x0000000000000000 in ?? ()
你知道什么会导致选择停止接收消息,即使我们仍在向他发送消息。通过套接字接收和发送大量消息时,linux 是否有任何缓冲区限制?
谢谢
有几种可能性。
超FD_SETSIZE
您的代码正在检查负文件描述符,但不会超过FD_SETSIZE
上限(通常为 1024)。 无论何时发生这种情况,您的代码都是
- 损坏自己的堆栈
- 向
select
显示一个空fd_set
,这将导致挂起
假设您不需要那么多并发打开的文件描述符,解决方案可能包括查找删除文件描述符泄漏,尤其是处理废弃描述符关闭的堆栈中的代码。
代码中有一个可疑注释,指示可能存在泄漏:
// let recv() catch the closed fd
如果这个评论意味着有人将m_socket
设置为 -1 并希望recv
能够捕获关闭的套接字并关闭它,谁知道呢,也许我们正在关闭 -1 而不是真正的闭合套接字。 (请注意在网络级别关闭和在文件描述符级别关闭之间的区别,后者需要单独的close
调用。
这也可以通过移动到poll
来解决,但操作系统施加了一些其他限制,使这条路线非常具有挑战性。
带外数据
您说服务器正在"发送"数据。 如果这意味着数据是使用 send
调用(而不是write
调用)发送的,请使用 strace
来确定 send flags 参数。 如果使用MSG_OOB
标志,则数据将作为带外数据到达 - select
调用不会注意到这些数据,直到您将fds
的副本作为另一个参数传递。
fd_set fds_copy = fds;
select( m_socket + 1, &fds, 0, &fds_copy, timeout == -1 ? 0 : &tv )
进程匮乏
如果盒子严重过载,服务器在没有任何阻塞调用的情况下执行,并且具有实时优先级(使用 top
检查这一点) - 而客户端不是 - 客户端可能会匮乏。
暂停的进程
从理论上讲,客户端可能会被SIGSTOP
停止。 您可能会知道是否是这种情况,在某处按 ctrl-Z 或让某些特定进程对客户端行使控制权,而不是您自己启动它。
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何在没有死锁和/或争用的情况下正确使用 std::mutex C++?
- 用C++中的std::condition_variable将线程置于死锁中会有风险吗
- 使用 std::async 时死锁,将来作为成员
- 如何调试读写器锁的死锁?
- 为什么在Visual Studio 2013上的std::this_thread::sleep_for上死锁
- localtime() 函数正在调用 ___lll_lock_wait_private(),这会使线程陷入死锁
- 如何重现 Boost 进程文档提示的死锁?
- 多线程Windows GUI应用程序中的死锁
- 为什么printf会导致与future.get的死锁,而cout则不会?
- C++中具有阻塞队列和障碍的死锁
- 死锁使用 std::mutex 来保护多个线程中的 cout
- 避免并发等待对象中的死锁
- 在VC++中从DLLMAIN内部调用D3D的CREATEDEVICE时,它会创建一个死锁(loaderlock?)。有没有办法克服这个问题?最终目标内
- 当用2个螺纹锁定时,将recursive_mutex死锁
- 程序在 C++11 中使用条件变量进入死锁
- 一个线程提升的死锁
- 单个生产者/多个消费者死锁
- 套接字选择中死锁的可能原因
- 我应该选择什么样的同步方案来避免在实时,c++ /MFC应用程序上的死锁