pselect块,即使数据可在套接字上读取 当我从POSIX套接字(RHEL6X86Y64 C++ ICPC)读取时,我经历了间歇性的延迟。我的代码是这样设计的,用户可以提供一个绝对timespec截止时间(相对于相对超时),以便在对recv的多个调用中使用。在尝试调用recv之前,我会调用pselect以确保数据可供读取

pselect块,即使数据可在套接字上读取 当我从POSIX套接字(RHEL6X86Y64 C++ ICPC)读取时,我经历了间歇性的延迟。我的代码是这样设计的,用户可以提供一个绝对timespec截止时间(相对于相对超时),以便在对recv的多个调用中使用。在尝试调用recv之前,我会调用pselect以确保数据可供读取,c++,sockets,posix,C++,Sockets,Posix,这通常按预期工作(将等待数据,但不会超过截止日期,如果数据可供recv使用,则不会产生明显的延迟)。然而,我有一个用户可以周期性地(大约50%的时间)让他的应用程序进入一种状态,在这种状态下,即使套接字上有可用的数据,select也会阻塞约400-500毫秒。如果我观察/proc/net/tcp,我可以看到数据在RX队列中可用,并且我可以看到应用程序正在缓慢地从队列中读取数据。如果我跳过对pselect的调用,只调用recv,则行为类似(但总体延迟较小,表明recv也在不必要地阻塞)。当应用程序

这通常按预期工作(将等待数据,但不会超过截止日期,如果数据可供recv使用,则不会产生明显的延迟)。然而,我有一个用户可以周期性地(大约50%的时间)让他的应用程序进入一种状态,在这种状态下,即使套接字上有可用的数据,select也会阻塞约400-500毫秒。如果我观察/proc/net/tcp,我可以看到数据在RX队列中可用,并且我可以看到应用程序正在缓慢地从队列中读取数据。如果我跳过对pselect的调用,只调用recv,则行为类似(但总体延迟较小,表明recv也在不必要地阻塞)。当应用程序进入这种状态时,它保持这种状态(每个pselect/recv都会经历一致的延迟)

我花了几个小时在这里和其他网站上闲逛。这是我能找到的最接近的类似问题,但没有解决方案

以前有人遇到过这种行为吗?我不知如何是好。我检测了代码,以验证这就是延迟发生的地方。(编辑:我们实际上只是验证了下面的整个方法很慢,没有任何特定的系统调用。)这似乎是内核/操作系统的问题,但我不确定该去哪里寻找。这是密码

// 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, &currentTime);
    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;
}

VirtualClock是一个与时间相关的实用程序类,这里仅用于比较/修复TimeSpec(即不引入任何延迟)。我希望您能了解这种行为。

事实上,这不是任何系统调用的问题。我们使用strace进行诊断,并看到了大量的打卡电话。对调用代码的另一次(第三次)检查显示了一个编程错误,导致被调用代码引用了损坏的堆栈数据。这是由于我的API设计有缺陷,导致截止期被破坏

我允许用户传入对包含配置(包括与截止日期相关的数据)的ServerConfig类的引用。我的服务器类正在保存引用,而不是复制对象。用户在堆上创建了my Server类的实例,在堆栈(在方法中)上传递了一个引用ServerConfig,当方法退出并且ServerConfig超出范围时,会在配置中产生非确定性垃圾。这是较旧的代码,我已经阻止了这种事情在其他地方发生后被烧毁,但这一个通过

因此,我学到的教训是:在编写依赖于用户提供的引用的API时要小心,重新考虑过早的优化(我之所以依赖于引用而不是仅仅复制引用的全部原因),当您看到这样的非确定性行为时,要寻找堆栈损坏(当我怀疑构建被顶起时,我会检查它,但这次我没有怀疑)。此外,strace是一个很棒的工具……我见过其他人使用它,但现在我自己也很习惯使用它


感谢您的评论,并为错误警报感到抱歉。

套接字是否为非阻塞?不,它正在阻塞。但我认为select将以同样的方式工作?当有数据可读取时,它应该立即返回,无论套接字是否阻塞,不是吗?我正在考虑尝试设置从套接字到非阻塞,但根据我所读的内容,它似乎不会产生任何影响。而且,有时会发生,有时不会,这让我很恼火。我认为你是对的,它不应该产生任何影响。不过,它可能会确认内核出于某种原因正在阻塞套接字。如果偶尔发生,我想评测不实用?可能是特定于负载的东西?这段代码在我编写的库中,其他开发人员正在使用。他们做了一些基本评测(通过valgrind和跟踪)识别阻塞发生的位置。关于有效负载的想法很好,但我不认为这里是这样。正在处理的消息由头和正文组成,并且在处理头时观察到问题,每次处理头都是相同的。我可以想到一些可能导致延迟的事情,但诚实地说,不需要挖掘在内核跟踪中,它们或多或少都是猜测。我不知道您是否已经在使用,但这至少有助于缩小原因。您可能希望向redhat提交错误报告。