2017-02-09 64 views
4

我試圖避免Valgrind的誤報,但我吸了atexit()fork()的組合,儘管使用--trace-children=yes。我的代碼:valgrind --trace-children =是報告儘管atexit清理泄漏

#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抑制文件?

回答

4

基於printf()輸出,看起來沒有泄漏。我可以說服valgrind這個,或者我應該只是將其添加到我的valgrind抑制文件?

看來,valgrind是正確的。如果您將printf()輸出解釋爲表示沒有泄漏,那麼您不讚賞fork()的效果。

當您分娩一個孩子時,它會獲得其父母的地址空間的完整副本。這通常是通過寫入時複製頁面實現的,但它仍構成屬於該孩子的內存。在你的情況下,它包括一個動態分配數組arr的副本。

孩子通過調用_exit()退出,所以雖然它繼承了其父母的退出處理程序註冊,但在該過程中不會調用註冊的退出處理程序。你可以知道這是因爲你只看到cleanup()的輸出。因此,valgrind告訴你,屬於孩子的arr的副本永遠不會被釋放。儘管如此,將這稱爲內存泄漏有些迂腐。當程序終止時,所討論的內存仍然可以訪問,此時它會被系統回收。它只是不是明確終止前釋放。

+0

謝謝!是的,我確實誤解了'fork()'的「父母地址空間副本」部分。爲了幫助任何人在未來絆倒這個問題,請谷歌「虛擬地址空間」和/或閱讀:http://stackoverflow.com/a/5365635/7541781 – gperciva

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)。它應該工作。

+0

謝謝!我見過有關'fork()'的其他文檔,說孩子應該調用'_exit()'而不是'exit()'[1]。然而,手動調用'child'中的'cleanup()'似乎是可行的。 [1]「由於技術原因,此處必須使用POSIX _exit函數而不是C標準退出函數。)」 https://en.wikipedia.org/wiki/Fork_(system_call)#Application_usage – gperciva

+0

@gperciva你應該瞭解諸如你所描述的建議背後的*原因*。在這種情況下,主要是避免調用註冊的出口處理程序,這在分叉子進程中通常是需要的。如果你真的想'*'來調用已註冊的出口處理程序,就像你在這種情況下做的那樣,你可能只想使用'exit()'而不是'_Exit()',正如這個答案所暗示的。 –

+0

@gperciva,請注意,儘管'_exit()不是C標準庫的一部分,但標準庫自C99以來具有'_Exit()',它具有相同的功能並且便於攜帶。 –