【问题标题】:C++ program memory leak on SIGINTSIGINT 上的 C++ 程序内存泄漏
【发布时间】:2011-08-20 22:38:03
【问题描述】:

我有一个程序,用作套接字客户端,这里是代码

#include <stdio.h>
#include <signal.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sys/socket.h>
#include <netdb.h>

using namespace std;

void signalHandler(const int signal) {
    cout << "SIGINT handled" << endl;
    exit(EXIT_SUCCESS);
}

void error(const char *msg) {
    perror(msg);
    exit(0);
}

const string readStr(int descriptor) {
    int n = 0;
    string cmd = "";

    char buffer[256];
    memset(buffer, 0, sizeof(buffer));

    while ((n = read(descriptor, buffer, 255)) != 0) {
        if (n < 0) {
            error("Error reading string");
        }

        // full string - just copy
        if (n == 255) {
            cmd += buffer;
            memset(buffer, 0, sizeof(buffer));
        }
        else {
            cmd += buffer;
            break;
        }
    }

    return cmd;
}

int main(int argc, char** argv) {
    signal(SIGINT, signalHandler);

    int fd, port = (argc >= 3 ? atoi(argv[2]) : 11212), n;
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        error("ERROR opening socket");
    }

    struct hostent *server = (argc >= 2 ? gethostbyname(argv[1]) : gethostbyname("localhost"));
    if (server == NULL) {
        error("ERROR no such host");
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));

    addr.sin_family = AF_INET;
    bcopy(server->h_addr, &addr.sin_addr.s_addr, server->h_length);
    addr.sin_port = htons(port);

    if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        error("ERROR connecting");
    }

    string tmp;
    while (1) {
        cout << "Please enter the message: \n< " << flush;
        tmp = readStr(0);
        n = send(fd, tmp.c_str(), tmp.size(), 0);
        if (n < 0) {
            perror("ERROR writing to socket");
        }

        tmp = readStr(fd);
        cout << "> " << tmp << endl;
    }
    close(fd);

    return EXIT_SUCCESS;
}

当我将 SIGNINT 发送到进程 (CTRL + C) 时,valgrind 说:

^CSIGINT 已处理 ==3798== ==3798== 堆摘要: ==3798== 在退出时使用:1 个块中的 39 个字节 ==3798== 总堆使用量:45 分配,44 释放,4,778 字节分配 ==3798== ==3798== 1 个块中的 39 个字节可能在丢失记录 1 of 1 中丢失 ==3798== at 0x402471C: operator new(unsigned int) (vg_replace_malloc.c:255) ==3798== by 0x40DBB64: std::string::_Rep::_S_create(unsigned int, 无符号整数,std::allocator const&) (在 /usr/lib/libstdc++.so.6.0.15) ==3798== 由 0x1BFF403: ??? ==3798== ==3798== 泄漏摘要: ==3798== 肯定丢失:0 个块中的 0 个字节 ==3798== 间接丢失:0 个块中的 0 个字节 ==3798== 可能丢失:1 个块中的 39 个字节 ==3798== 仍然可访问:0 个块中的 0 个字节 ==3798== 抑制:0 个块中的 0 个字节 ==3798== ==3798== 对于检测到和抑制的错误计数,重新运行:-v ==3798== 错误摘要:1 个上下文中的 1 个错误(抑制:9 个中的 20 个)

如果程序正确退出,例如如果将while(1) 替换为int i = 0; while(i++ &lt;= 2),则不会发生内存泄漏

那么有人可以帮我解决这个问题吗?

【问题讨论】:

  • @unapersson:错了,请参阅 James McNellis 的回答。
  • @nightcracker 在进程终止时释放所有内存。这不是我(和大多数其他人)所说的内存泄漏,它是正在运行的程序中的一块内存变得无法访问的地方。
  • @azat,这只是挑剔。通常使用 0 作为成功操作的退出代码,而使用非零作为某种错误的退出代码。
  • 这些不是内存泄漏。顺便说一句,您从信号处理程序中使用 cout 是不安全的。 exit 也是如此。只有_Exit 是安全的。或者你可以只是raise 另一个退出信号。
  • @R.. 你能给我举个例子吗?

标签: c++ c memory-leaks valgrind


【解决方案1】:

当您从信号处理程序调用std::exit 时,堆栈不会展开,并且不会破坏main 中的std::string 对象tmp 等局部变量。

由于tmp 在程序终止之前没有被销毁,它分配的内存永远不会被释放,因此泄漏。

【讨论】:

  • 但是 valgrind 说只有 std::string 没有被破坏,但是 tmp 会
  • 事实上,许多守护进程,例如 memcached,在 SIGINT 上被正确终止。我检查 valgrind
  • @James McNellis valgrind 不是说程序终止后 tmp 没有被破坏,它说只有来自readStr 的字符串是
  • 正确:字符串在readStr 中创建,并返回到maintmp 拥有它的所有权。当main 返回时,tmp 被销毁并释放内存。由于main 永远不会在您的程序中返回(您通过调用std::exit 不干净地终止程序),tmp 永远不会被破坏并且内存永远不会被释放。
  • 最好的办法是不要调用std::exit,而是让信号处理程序通知您的程序(可能通过一些消息队列或全局状态)它应该停止执行并让它干净地退出.
猜你喜欢
  • 1970-01-01
  • 2018-10-09
  • 2021-04-17
  • 2021-03-11
  • 1970-01-01
  • 1970-01-01
  • 2019-10-21
  • 1970-01-01
相关资源
最近更新 更多