在 malloc 之后 fork 导致内存泄漏

Fork after malloc causing memory leak

我正在从事一个需要分叉 N 个进程的系统编程项目。问题是我将 PID 保存在一个动态分配的数组中,并且带有“--leak-check=full --show-leak-kinds=all”标志的 valgrind 将此 PID 数组指针显示为可能的泄漏。

pid_t *pid;

//...globals & prototypes

int main(int argc, char *argv[])
{

    //... Input parsing

    // Create actor process=========================================
    pid = malloc((_N + _V + _C + 1) * sizeof(pid_t *)); // LINE VALGRIND IS POINTING STILL REACHABLE
    for (int i = 0; i < _N + _V + _C + 1; i++)
    {
        pid[i] = fork();
        if (pid[i] == 0)
            break;
    }
    // ======================================== Create actor process

    // Parent process ====================================================
    if (parent_pid == getpid())
    {

        // Wait for all the childeren=====================================
        for (int i = 0; i < _N + _V + _C + 1 || exit_requested != 0; i++)
        {
            int status;
            if (waitpid(pid[i], &status, 0) == -1)
            {
                errExit("waitpid");
            }
            
        }
        // =====================================Wait for all the childeren

        // Free resources
        free(pid);

        //.. destroy semaphores

        shm_unlink(SHARED_LINK);
    }

    // Child processes ===================================================
    else
    {
        for (int i = 0; i < _N + _V + _C + 1; i++)
        {
            if (i >= 0 && i < _N && pid[i] == 0)
            {
                producer(_I, shared_data, i);
            }
            else if (i >= _N && i < _N + _V && pid[i] == 0)
            {
                mid_consumer(shared_data, i - _N);
            }
            else if (i >= _N + _V && i < _N + _V + _C && pid[i] == 0)
            {
                end_consumer(shared_data, i - _N - _V);
            }
        }
    }
    // ===================================================================
    return 0;
}

这是 valgrind 的输出

==8056== Memcheck, a memory error detector
==8056== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8056== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==8056== Command: ./program -n 2 -v 2 -c 4 -b 81 -t 2 -i cold_storage.txt
==8056== Parent PID: 8055
==8056== 
==8061== 
==8061== HEAP SUMMARY:
==8061==     in use at exit: 72 bytes in 1 blocks
==8061==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8061== 
==8061== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8061==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8061==    by 0x10925D: main (main.c:169)
==8061== 
==8061== LEAK SUMMARY:
==8061==    definitely lost: 0 bytes in 0 blocks
==8061==    indirectly lost: 0 bytes in 0 blocks
==8061==      possibly lost: 0 bytes in 0 blocks
==8061==    still reachable: 72 bytes in 1 blocks
==8061==         suppressed: 0 bytes in 0 blocks
==8061== 
==8061== For counts of detected and suppressed errors, rerun with: -v
==8061== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8059== 
==8059== HEAP SUMMARY:
==8059==     in use at exit: 72 bytes in 1 blocks
==8059==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8059== 
==8057== 
==8062== 
==8057== HEAP SUMMARY:
==8057==     in use at exit: 72 bytes in 1 blocks
==8057==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8057== 
==8062== HEAP SUMMARY:
==8062==     in use at exit: 72 bytes in 1 blocks
==8062==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8062== 
==8059== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8059==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8059==    by 0x10925D: main (main.c:169)
==8059== 
==8059== LEAK SUMMARY:
==8059==    definitely lost: 0 bytes in 0 blocks
==8059==    indirectly lost: 0 bytes in 0 blocks
==8059==      possibly lost: 0 bytes in 0 blocks
==8059==    still reachable: 72 bytes in 1 blocks
==8059==         suppressed: 0 bytes in 0 blocks
==8059== 
==8059== For counts of detected and suppressed errors, rerun with: -v
==8059== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8062== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8057== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8062==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8057==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8062==    by 0x10925D: main (main.c:169)
==8062== 
==8057==    by 0x10925D: main (main.c:169)
==8062== LEAK SUMMARY:
==8057== 
==8062==    definitely lost: 0 bytes in 0 blocks
==8057== LEAK SUMMARY:
==8062==    indirectly lost: 0 bytes in 0 blocks
==8057==    definitely lost: 0 bytes in 0 blocks
==8062==      possibly lost: 0 bytes in 0 blocks
==8057==    indirectly lost: 0 bytes in 0 blocks
==8062==    still reachable: 72 bytes in 1 blocks
==8057==      possibly lost: 0 bytes in 0 blocks
==8062==         suppressed: 0 bytes in 0 blocks
==8057==    still reachable: 72 bytes in 1 blocks
==8062== 
==8057==         suppressed: 0 bytes in 0 blocks
==8062== For counts of detected and suppressed errors, rerun with: -v
==8057== 
==8062== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8057== For counts of detected and suppressed errors, rerun with: -v
==8057== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8063== 
==8063== HEAP SUMMARY:
==8063==     in use at exit: 72 bytes in 1 blocks
==8063==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8063== 
==8063== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8063==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8063==    by 0x10925D: main (main.c:169)
==8063== 
==8063== LEAK SUMMARY:
==8063==    definitely lost: 0 bytes in 0 blocks
==8063==    indirectly lost: 0 bytes in 0 blocks
==8063==      possibly lost: 0 bytes in 0 blocks
==8063==    still reachable: 72 bytes in 1 blocks
==8063==         suppressed: 0 bytes in 0 blocks
==8063== 
==8063== For counts of detected and suppressed errors, rerun with: -v
==8063== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8058== 
==8058== HEAP SUMMARY:
==8058==     in use at exit: 72 bytes in 1 blocks
==8058==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8058== 
==8058== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8058==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8058==    by 0x10925D: main (main.c:169)
==8058== 
==8058== LEAK SUMMARY:
==8058==    definitely lost: 0 bytes in 0 blocks
==8058==    indirectly lost: 0 bytes in 0 blocks
==8058==      possibly lost: 0 bytes in 0 blocks
==8058==    still reachable: 72 bytes in 1 blocks
==8058==         suppressed: 0 bytes in 0 blocks
==8058== 
==8058== For counts of detected and suppressed errors, rerun with: -v
==8058== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8064== 
==8064== HEAP SUMMARY:
==8064==     in use at exit: 72 bytes in 1 blocks
==8064==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8064== 
==8064== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8064==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8064==    by 0x10925D: main (main.c:169)
==8064== 
==8064== LEAK SUMMARY:
==8064==    definitely lost: 0 bytes in 0 blocks
==8064==    indirectly lost: 0 bytes in 0 blocks
==8064==      possibly lost: 0 bytes in 0 blocks
==8064==    still reachable: 72 bytes in 1 blocks
==8064==         suppressed: 0 bytes in 0 blocks
==8064== 
==8064== For counts of detected and suppressed errors, rerun with: -v
==8064== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8060== 
==8060== HEAP SUMMARY:
==8060==     in use at exit: 72 bytes in 1 blocks
==8060==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8060== 
==8060== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8060==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8060==    by 0x10925D: main (main.c:169)
==8060== 
==8060== LEAK SUMMARY:
==8060==    definitely lost: 0 bytes in 0 blocks
==8060==    indirectly lost: 0 bytes in 0 blocks
==8060==      possibly lost: 0 bytes in 0 blocks
==8060==    still reachable: 72 bytes in 1 blocks
==8060==         suppressed: 0 bytes in 0 blocks
==8060== 
==8060== For counts of detected and suppressed errors, rerun with: -v
==8060== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8065== 
==8065== HEAP SUMMARY:
==8065==     in use at exit: 72 bytes in 1 blocks
==8065==   total heap usage: 1 allocs, 0 frees, 72 bytes allocated
==8065== 
==8065== 72 bytes in 1 blocks are still reachable in loss record 1 of 1
==8065==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8065==    by 0x10925D: main (main.c:169)
==8065== 
==8065== LEAK SUMMARY:
==8065==    definitely lost: 0 bytes in 0 blocks
==8065==    indirectly lost: 0 bytes in 0 blocks
==8065==      possibly lost: 0 bytes in 0 blocks
==8065==    still reachable: 72 bytes in 1 blocks
==8065==         suppressed: 0 bytes in 0 blocks
==8065== 
==8065== For counts of detected and suppressed errors, rerun with: -v
==8065== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==8056== 
==8056== HEAP SUMMARY:
==8056==     in use at exit: 0 bytes in 0 blocks
==8056==   total heap usage: 1 allocs, 1 frees, 72 bytes allocated
==8056== 
==8056== All heap blocks were freed -- no leaks are possible
==8056== 
==8056== For counts of detected and suppressed errors, rerun with: -v
==8056== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

我也试过在分叉进程中释放 PID 数组,但它仍然给出相同的错误。

这不是一个完整的答案,但它解决了一些问题。

首先,pid 数组内存块根据 sizeof(pid_t *) 的倍数分配了错误的大小,而它应该是 sizeof(pid_t) 的相同倍数。如果 sizeof(pid_t *) 大于 sizeof(pid_t),那是一种无害的内存浪费,但在 sizeof(pid_t *) 小于 sizeof(pid_t).[=25 的不同情况下,这将是缓冲区溢出=]

其次,子进程不释放pid数组内存块。

第三,子进程循环遍历pid数组内存块的所有元素寻找pid[i] == 0。我相信这是试图找到自己索引的子进程。但是,pid 数组内存块在子进程 fork 中还没有完全初始化,因此可能存在多个值为 0 的元素。此外,子进程根本不需要使用 pid 数组,如果它所需要的只是它可以在 fork().

时确定的索引号

以下版本修复了这些问题,但我不知道它是否修复了 Valgrind 错误。

pid_t *pid;

//...globals & prototypes

int main(int argc, char *argv[])
{
    int cid;  // child index

    //... Input parsing

    // Create actor process=========================================
    pid = malloc((_N + _V + _C + 1) * sizeof(pid_t));
    for (cid = 0; xid < _N + _V + _C + 1; cid++)
    {
        pid[cid] = fork();
        if (pid[cid] == 0)
        {
            free(pid);
            break;
        }
    }
    // ======================================== Create actor process

    // Parent process ====================================================
    if (parent_pid == getpid())
    {

        // Wait for all the childeren=====================================
        for (int i = 0; i < _N + _V + _C + 1 || exit_requested != 0; i++)
        {
            int status;
            if (waitpid(pid[i], &status, 0) == -1)
            {
                errExit("waitpid");
            }
            
        }
        // =====================================Wait for all the childeren

        // Free resources
        free(pid);

        //.. destroy semaphores

        shm_unlink(SHARED_LINK);
    }

    // Child processes ===================================================
    else
    {
        if (cid >= 0 && cid < _N)
        {
            producer(_I, shared_data, cid);
        }
        else if (cid >= _N && cid < _N + _V)
        {
            mid_consumer(shared_data, cid - _N);
        }
        else if (cid >= _N + _V && cid < _N + _V + _C)
        {
            end_consumer(shared_data, cid - _N - _V);
        }
    }
    // ===================================================================
    return 0;
}