公司一小兄弟问我:execvp函数执行后不返回,如何将申请的内存释放掉呢?还专门发我一个不知道从哪个英文网站copy的例子,问我使用valgrind怎么没有检测出来内存泄露,你不是吹valgrind吹的天花乱坠吗?这个例子仅仅为了说明这个问题,如果换成myargs[0] = "wc";这种写法,就不会存在本篇文章了。
完整例子如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>

int
main(int argc, char *argv[])
{
    int rc = fork();
    if (rc < 0) {
        // fork failed; exit
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // child: redirect standard output to a file
        close(STDOUT_FILENO); 
        open("./p4.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);

        // now exec "wc"...
        char *myargs[3];
        myargs[0] = strdup("wc");   // program: "wc" (word count)
        myargs[1] = strdup("p4.c"); // argument: file to count
        myargs[2] = NULL;           // marks end of array
        execvp(myargs[0], myargs);  // runs word count
    } else {
        // parent goes down this path (original process)
        int wc = wait(NULL);
    assert(wc >= 0);
    }
    return 0;
}

编译运行使用valgrind检测也确实没有内存泄露,valgrind检测结果如下:

==13561== Memcheck, a memory error detector
==13561== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13561== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==13561== Command: ./p4
==13561==
==13561==
==13561== HEAP SUMMARY:
==13561==     in use at exit: 0 bytes in 0 blocks
==13561==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==13561==
==13561== All heap blocks were freed -- no leaks are possible
==13561==
==13561== For counts of detected and suppressed errors, rerun with: -v
==13561== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

此处可以配上那张0 errors 0 warnings的表情包。可是子进程中明明有使用strdup函数申请内存。
我们可以检测如下例子:

// C program to demonstrate strdup()
#include<stdio.h>
#include<string.h>

int main()
{
    char source[] = "GeeksForGeeks";

    // A copy of source is created dynamically
    // and pointer to copy is returned.
    char* target = strdup(source);

    printf("%s", target);
    return 0;
}

检测结果可以看到有14(GeeksForGeeks长度加结束符)字节的内存泄露

[root ~]#valgrind --leak-check=full ./strdup
==15137== Memcheck, a memory error detector
==15137== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==15137== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==15137== Command: ./strdup
==15137==
GeeksForGeeks==15137==
==15137== HEAP SUMMARY:
==15137==     in use at exit: 14 bytes in 1 blocks
==15137==   total heap usage: 2 allocs, 1 frees, 1,038 bytes allocated
==15137==
==15137== 14 bytes in 1 blocks are definitely lost in loss record 1 of 1
==15137==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==15137==    by 0x4ED99B9: strdup (strdup.c:42)
==15137==    by 0x108737: main (strdup.c:11)
==15137==
==15137== LEAK SUMMARY:
==15137==    definitely lost: 14 bytes in 1 blocks
==15137==    indirectly lost: 0 bytes in 0 blocks
==15137==    possibly lost: 0 bytes in 0 blocks
==15137==    still reachable: 0 bytes in 0 blocks
==15137==    suppressed: 0 bytes in 0 blocks
==15137==
==15137== For counts of detected and suppressed errors, rerun with: -v
==15137== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
[root ~]#

现在回到开始的问题,为什么在子进程中申请了内存,确不存在内存泄露呢?
首先可以学习这篇关于fork 和exec的回答Please explain the exec() function and its family
这里我们要学习下valgrind的一个选项:--trace-children=yes

[root cpu-api]#valgrind --trace-children=yes --leak-check=full ./p4
==15091== Memcheck, a memory error detector
==15091== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==15091== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==15091== Command: ./p4
==15091==
==15092== Memcheck, a memory error detector
==15092== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==15092== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==15092== Command: /usr/bin/wc p4.c
==15092==
==15092==
==15092== HEAP SUMMARY:
==15092==     in use at exit: 0 bytes in 0 blocks
==15092==   total heap usage: 36 allocs, 36 frees, 8,809 bytes allocated
==15092==
==15092== All heap blocks were freed -- no leaks are possible
==15092==
==15092== For counts of detected and suppressed errors, rerun with: -v
==15092== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==15091==
==15091== HEAP SUMMARY:
==15091==     in use at exit: 0 bytes in 0 blocks
==15091==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==15091==
==15091== All heap blocks were freed -- no leaks are possible
==15091==
==15091== For counts of detected and suppressed errors, rerun with: -v
==15091== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

这里的真实原因是exec家族系列函数,在执行时会将自身进程替换成目标程序,当前进程的代码和内存会被清理,然后开始执行新的程序,新的程序会重新申请内存。因此在exec之前申请的那些内存都不会存在内存泄露。这里是说exec执行成功的情况。如果exec执行失败,那么valrgind还是可以检测到内存泄露。子进程valgrind扫描结果中的36 allocs, 36 frees, 8,809 bytes allocated也能从侧面验证这一点,示例代码存在一个问题是没处理execvp执行失败的情况。
对于内存泄露问题我最近有了不一样的认识,我开始认识到如下程序不存在内存泄露,因为程序执行完立刻退出,系统会回收内存,当然还是要养成正规与大气的编程习惯。

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

int main()
{
    int *p = (int *)malloc(10 *sizeof(int));
    printf("program exit\n");
    
    return 0;
}

我们要重点解决的内存泄露是在常驻进程中的内存泄露,例如程序中所夸大描述的问题:

#include <stdio.h>
#include <stdlib.h>
int main()
{
     while(1)
     {
            int *p = (int *)malloc(10 * sizeof(int));
     }
     return 0;
}