valgrind --trace-children = yes报告泄漏,尽管atexit清理

时间:2017-02-09 17:57:21

标签: c memory-leaks static valgrind atexit

我试图避免使用valgrind进行误报,但我使用atexit()fork()的组合很糟糕,尽管使用了--trace-children=yes。我的代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

static int * arr;

static void cleanup() {
    free(arr);
    printf("free arr as: %p\n", (void *)arr);
}

int main()
{
    arr = malloc(16 * sizeof(int));
    printf("allocated arr as: %p\n", (void *)arr);
    atexit(cleanup);

    pid_t pid = fork();
    if (pid == -1) {
        exit(1);
    } else if (pid == 0) {
        // child
        _exit(0);
    } else {
        // parent
        exit(0);
    }
}

命令行:

$ clang -Weverything leak.c 
$ valgrind --trace-children=yes ./a.out 
==3287== Memcheck, a memory error detector
==3287== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3287== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3287== Command: ./a.out
==3287== 
allocated arr as: 0x5202040
free arr as: 0x5202040
==3288== 
==3288== HEAP SUMMARY:
==3288==     in use at exit: 64 bytes in 1 blocks
==3288==   total heap usage: 2 allocs, 1 frees, 1,088 bytes allocated
==3288== 
==3288== LEAK SUMMARY:
==3288==    definitely lost: 0 bytes in 0 blocks
==3288==    indirectly lost: 0 bytes in 0 blocks
==3288==      possibly lost: 0 bytes in 0 blocks
==3288==    still reachable: 64 bytes in 1 blocks
==3288==         suppressed: 0 bytes in 0 blocks
==3288== Rerun with --leak-check=full to see details of leaked memory
==3288== 
==3288== For counts of detected and suppressed errors, rerun with: -v
==3288== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==3287== 
==3287== HEAP SUMMARY:
==3287==     in use at exit: 0 bytes in 0 blocks
==3287==   total heap usage: 2 allocs, 2 frees, 1,088 bytes allocated
==3287== 
==3287== All heap blocks were freed -- no leaks are possible
==3287== 
==3287== For counts of detected and suppressed errors, rerun with: -v
==3287== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

根据printf()输出,看起来没有泄漏。我可以说服valgrind这个,还是我应该把它添加到我的valgrind抑制文件中?

2 个答案:

答案 0 :(得分:5)

  

基于printf()输出,看起来没有泄漏。我可以说服valgrind这个,还是我应该把它添加到我的valgrind抑制文件中?

看来valgrind是对的。如果您将printf()输出解释为表示没有泄漏,那么您就不会意识到fork()的效果。

当你分叉一个孩子时,它会获得其父母地址空间的完整副本。这通常通过写时复制页面实现,但它仍构成属于子节点的内存。在您的情况下,它包括动态分配的数组arr的副本。

子进程通过调用_exit()退出,因此虽然它继承了父进程的退出处理程序注册,但在该进程中不会调用已注册的退出处理程序。您可以这样说,因为您只看到cleanup()的输出一次。因此,valgrind告诉你,从未释放属于孩子的arr副本。

但是,将这称为内存泄漏有点迂腐。当程序终止时,仍然可以访问有问题的内存,此时它将被系统回收。它只是在终止之前没有显式释放。

答案 1 :(得分:4)

您正在子进程中使用_exit()。根据_exit的手册页:

The function _exit() is like exit(3), but does not call any functions registered with atexit(3) or on_exit(3). 

将其更改为退出(0)。它应该工作。