9

在对我编写的某些服务器代码进行压力测试时,我注意到即使我在描述符句柄上调用 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 位机器上运行。

有什么想法吗?

4

3 回答 3

5

因此,使用 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() 每次都在运行,这导致了我的灯泡时刻。

感谢大家的帮助!

于 2012-10-12T05:28:03.230 回答
1

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

于 2012-10-15T03:46:32.200 回答
0

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

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

于 2012-10-10T14:24:08.563 回答