从POSIX套接字(RHEL6 x86_64 C ++ icpc)读取时,我遇到间歇性延迟。我的代码经过精心设计,以便用户可以提供绝对的时间规格截止日期(相对于相对超时),以便在多次调用recv时使用。我调用pselect以确保在尝试调用recv之前可以读取数据。
这通常会按预期工作(将等待数据,但不会超过截止日期,如果有数据可接收,则不会引起明显的延迟)。但是,我有一个用户可以周期性地(大约50%的时间)将其应用程序置于选择阻塞状态,即使套接字上有可用数据的时间约为400-500 ms。如果我观看/ proc / net / tcp,则可以看到RX队列中有可用数据,并且可以看到应用程序正在缓慢地从队列中读取数据。如果我跳过对pselect的调用,而仅调用recv,则行为类似(但总体延迟较小,表明recv也不必要地阻塞了)。当应用程序进入此状态时,它将保持这种状态(每个pselect / recv都会经历一致的延迟)。
我花了几个小时在这里和其他网站上闲逛。这是我能找到的最接近的类似问题,但没有解决方法...
http://developerweb.net/viewtopic.php?id=7458
以前有人遇到过这种行为吗?我不知所措。我已经检测了代码以验证这是延迟发生的地方。(编辑:我们实际上只是验证了下面的整个方法很慢,而不是任何特定的系统调用。)这似乎是内核/操作系统问题,但我不确定在哪里查找。这是代码...
// protected
bool
Message::wait(int socket, const timespec & deadline) {
// Bail if deadline not provided
if (deadline.tv_sec == 0 && deadline.tv_nsec == 0) {
return true;
}
// Make sure we haven't already exceeded deadline
timespec currentTime;
clock_gettime(CLOCK_REALTIME, ¤tTime);
if (VirtualClock::cmptime(currentTime, deadline) >= 0) {
LOG_WARNING("Timed out waiting to receive data");
m_timedOut = true;
return false;
}
// Calculate receive timeout
timespec timeout;
memset(&timeout, 0, sizeof(timeout));
timeout.tv_nsec = VirtualClock::nsecs(currentTime, deadline);
VirtualClock::fixtime(timeout);
// Wait for data
fd_set descSet;
FD_ZERO(&descSet);
FD_SET(socket, &descSet);
int result = pselect(socket + 1, &descSet, NULL, NULL, &timeout, NULL);
if (result == -1) {
m_error = errno;
LOG_ERROR("Failed to wait for data: %d, %s",
m_error, strerror(m_error));
return false;
} else if (result == 0 || !FD_ISSET(socket, &descSet)) {
LOG_WARNING("Timed out waiting to receive data");
m_timedOut = true;
return false;
}
return true;
}
Run Code Online (Sandbox Code Playgroud)
VirtualClock是一个与时间相关的实用程序类,仅在此处用于比较/修正时间规格(即不引入任何延迟)。我希望对这种行为有任何见解。
事实上,这对于任何系统调用来说都不是问题。我们使用 strace 进行诊断,并看到大量对clock_gettime 的调用。对调用代码的另一次(第三次)审查发现了一个编程错误,导致被调用的代码引用了损坏的堆栈数据。这是由于我的 API 设计有缺陷导致截止日期被破坏而促成的。
我允许用户传递对包含配置(包括与截止日期相关的数据)的 ServerConfig 类的引用。我的服务器类保存引用而不是复制对象。用户在堆上创建了我的 Server 类的实例,并在堆栈上(在方法中)传入了对 ServerConfig 的引用,导致当方法退出且 ServerConfig 超出范围时,配置中出现不确定的垃圾。这是较旧的代码,我已经在被烧毁后阻止了这种事情在其他地方发生,但这个却被忽略了。
因此,我学到的教训是:编写依赖于用户提供的引用的 API 时要小心,重新考虑过早的优化(我依赖于引用而不是仅仅进行复制的全部原因),并在看到堆栈损坏时查找堆栈损坏像这样的非确定性行为(当我怀疑构建被提升但这次没有怀疑时我会检查这一点)。另外,strace 是一个很棒的工具...我见过其他人使用它,但现在我自己也能轻松使用它。
感谢您的评论,并对误报表示歉意。
| 归档时间: |
|
| 查看次数: |
753 次 |
| 最近记录: |