【问题标题】:close on socket not releasing file descriptor关闭套接字不释放文件描述符
【发布时间】:2012-10-01 00:29:45
【问题描述】:

在对我编写的某些服务器代码进行压力测试时,我注意到即使我在描述符句柄上调用 close()(并验证结果是否有错误),描述符并未释放,最终导致 accept()返回错误“打开的文件太多”。

现在我明白这是因为 ulimit,我不明白为什么我在每个同步接受/读取/发送周期后调用 close() 时会点击它?

我正在通过使用 lsof 运行手表来验证描述符是否确实存在:

ctsvr 9733 mike 1017u sock 0,7 0t0 3323579 无法识别协议
ctsvr 9733 mike 1018u sock 0,7 0t0 3323581 无法识别协议
...

果然有大约 1000 个左右。此外,使用 netstat 检查我可以看到没有挂起的 TCP 状态(没有 WAIT 或 STOPPED 或任何东西)。

如果我只是从客户端执行单个连接/发送/接收,我确实注意到套接字确实保留在 lsof 中;所以这甚至不是负载问题。

服务器在 Ubuntu Linux 64 位机器上运行。

有什么想法吗?

【问题讨论】:

  • 假设你所说的一切都是真的,这听起来像是一个内核错误。成功关闭绝对必须释放描述符。
  • 您是否在调用shutdown 和/或在关闭套接字句柄之前使用套接字中的所有数据?拨打close时,您是否挂了reads 或writes?
  • 尝试使用 strace,如果 connect/send/recv 调用像你说的那样紧密耦合,从诊断输出中应该很清楚
  • netstat --inet 会告诉你 TCP 会话的状态。如果他们“成立”,你还没有打电话给close()。
  • 如果您希望我们提供帮助,请发布一些代码。如果您无法这样做,请使用像 valgrind 这样的分析器来跟踪描述符。这对我来说听起来像是一个应用程序错误。

标签: c++ linux sockets ulimit


【解决方案1】:

所以使用 strace(感谢 Gearoid),我不知道没有它我是如何生活的,我注意到我实际上正在关闭描述符。

但是。为了子孙后代,我揭露了我的愚蠢错误:

Socket::Socket() : impl(new Impl) {
    impl->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    ....
}

Socket::ptr_t Socket::accept() {
    auto r = ::accept(impl->fd, NULL, NULL);
    ...
    ptr_t s(new Socket);
    s->impl->fd = r;
    return s;
}

如您所见,我的构造函数立即分配了一个套接字,然后我用 accept 返回的描述符替换了描述符——造成了泄漏。我已将接受代码从独立的 Acceptor 类重构为 Socket 类,而没有更改它。

使用 strace 我可以很容易地看到 socket() 每次都在运行,这导致了我的灯泡时刻。

感谢大家的帮助!

【讨论】:

    【解决方案2】:

    你有没有在 close() 之后调用 perror() ? 我想返回的字符串会给你一些帮助;

    【讨论】:

      【解决方案3】:

      你很可能挂在recv() 或send() 命令上。考虑使用setsockopt 设置超时。

      当套接字在另一端关闭时,我注意到 lsof 上有类似的输出,但我的线程保持套接字打开,挂在等待数据的 recv() 命令上。

      【讨论】:

      • 阻塞recv() 或send() 不会阻止关闭套接字或释放FD。您的第二段描述了一种不可能的情况。如果对等方关闭了连接,您的recv() 将停止阻塞并返回零返回值。您完全有其他错误。 -1
      猜你喜欢
      • 1970-01-01
      • 2010-09-11
      • 1970-01-01
      • 1970-01-01
      • 2012-11-02
      • 1970-01-01
      • 2014-04-25
      • 2011-03-31
      • 1970-01-01
      相关资源
      最近更新 更多