首页 > 解决方案 > 在父进程中调用 fork() 后子进程内存泄漏,为什么?

问题描述

Mac OS X 10.13.4。运行此程序会触发 Valgrind 中的内存泄漏信号并泄漏系统调用:

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

char    *ft_strcpy(char *s1, const char *s2)
{
    size_t  cur;

    cur = -1;
    while (s2[++cur] != '\0')
        s1[cur] = s2[cur];
    s1[cur] = '\0';
    return (s1);
}

void    ptr_test()
{
    char    *ptr;

    ptr = (char *)malloc(sizeof(char) * 15);
    printf("FIRST: %p\n", ptr);
    ft_strcpy(ptr, "Hello, World!");
    printf("SECOND: %p\n\n", ptr);
    free(ptr);
}

int     main(void)
{
    pid_t   pid;

    pid = fork();
    while(1)
        ptr_test();
    if (pid != 0)
        wait(NULL);
    return (0);
}

泄漏系统调用:

Process:         main [98746]
Path:            /nfs/2018/p/patrisor/Desktop/ptr/main
Load Address:    0x1095a2000
Identifier:      main
Version:         ???
Code Type:       X86-64
Parent Process:  main [98687]

Date/Time:       2019-09-22 05:28:32.273 -0700
Launch Time:     2019-09-22 05:28:23.881 -0700
OS Version:      Mac OS X 10.13.4 (17E199)
Report Version:  7
Analysis Tool:   /usr/bin/leaks

Physical footprint:         280K
Physical footprint (peak):  280K
----

leaks Report Version: 3.0
Process 98746: 176 nodes malloced for 27 KB
Process 98746: 4 leaks for 4128 total leaked bytes.

Leak: 0x7f9d9ad00040  size=16  zone: DefaultMallocZone_0x1095a7000
Leak: 0x7f9d9ad00050  size=16  zone: DefaultMallocZone_0x1095a7000
Leak: 0x7f9d9b800800  size=2048  zone: DefaultMallocZone_0x1095a7000
Leak: 0x7f9d9b801000  size=2048  zone: DefaultMallocZone_0x1095a7000

瓦尔格林:

==1388== LEAK SUMMARY:
==1388==    definitely lost: 32 bytes in 2 blocks
==1388==    indirectly lost: 4,096 bytes in 2 blocks
==1388==      possibly lost: 72 bytes in 3 blocks
==1388==    still reachable: 215 bytes in 7 blocks
==1388==         suppressed: 27,726 bytes in 170 blocks
==1388==
==1388== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 13 from 13)

最初我怀疑这是 free() 无法使用正确的分配地址的问题。但是,当我手动扫描进程的内存使用情况时,运行时它的使用情况并没有显着增加。后来我怀疑是子进程没有正确结束,可能是创建了僵尸进程。泄漏发生在开始的某个地方,即调用 free 的地方。是函数本身有问题,还是……?我似乎很失落。

有什么猜测吗?

标签: cmemory-leaksmallocforkvalgrind

解决方案


以下包含对代码问题的评论:

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


/* 
 * strongly suggest:
 * #include <string.h> and
 * eliminating this function and in ptr_test()
 * strcpy( ptr, "Hello, World! );
 */
char    *ft_strcpy(char *s1, const char *s2)
{
    size_t  cur;

    /* this causes an implicit conversion to unsigned
     * so the value will be seen as a VERY LARGE number
     * suggest initialize to 0 then use: 
     * while( s2[cur++] != '\0' )
     */
    cur = -1;         
    while (s2[++cur] != '\0')
        s1[cur] = s2[cur];
    s1[cur] = '\0';
    return (s1);
}

void    ptr_test()
{
    char    *ptr;

    /*
     * returned value from malloc has type `void*` 
     * which can be assigned to any pointer
     * casting just clutters the code, 
     * making it more difficult to understand, debug, etc
     * suggest removing that cast.
     */
    /* sizeof( char ) is defined in the c standard as 1
     * multiplying anything by 1 has no effect
     * suggest removing that expresson
     */
    /*
     * when calling any of the heap allocation functions
     * always check (!=NULL) the returned value
     * to assure the operation was successful
     * if not successful, call 'perror( "malloc failed" );
     * to output your error message and the text reason
     * the system thinks the error occurred to 'stderr'
     */
    ptr = (char *)malloc(sizeof(char) * 15); 

    printf("FIRST: %p\n", ptr);

    ft_strcpy(ptr, "Hello, World!");
    printf("SECOND: %p\n\n", ptr);
    free(ptr);
}

int     main(void)
{
    pid_t   pid;

    pid = fork();

    /*
     * this runs 'ptr_test()' FOREVER
     * and in the fork() error condition FOREVER
     * and in the child process  FOREVER
     * and in the parent process FOREVER
     * 
     * note: 'exit()' and EXIT_SUCCESS are from
     *     the header file: `stdlib.h`
     * Suggest:
     * if( !pid )
     * { // then child process
     *     for( int i=0; i<1024; i++ )
     *         ptr_test();
     *     exit( EXIT_SUCCESS );
     */
    while(1)
        ptr_test();

    /* there are three types of returned values from 'fork()'
     * <0  means an error occurred
     * ==0 means in the child process
     * >0  means in the parent process
     * 
     * a test for '!=0' will 'catch' the error AND the parent
     * however, there is no child process for the parent to 'wait()' 
     * strongly suggest testing for each condition separately
     * I.E.
     * if( pid < 0 )
     *     //handle error
     *     perror( "fork failed" );
     * else if( !pid )
     *     // handle child process ...
     *     exit( EXIT_ SUCCESS );
     * else
     *     // handle parent process
     *     wait( NULL );
     */
    if (pid != 0)

        /* the prototype for 'wait()' is missing
         * strongly suggest adding the header files:
         * #include <sys/types.h>
         * #include <sys/wait.h>
         */
        wait(NULL);
    return (0);
}

推荐阅读