【问题标题】:Fork after malloc causing memory leakmalloc后fork导致内存泄漏
【发布时间】:2021-07-23 20:50:48
【问题描述】:

我正在开发一个需要分叉 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,但从不释放它。 stackoverflow.com/questions/7601146/… 所以它不可能是“相同”的错误,它至少是另一个错误。请在你释放它的地方显示代码。
  • 我不明白子进程中pid[i] == 0 测试的目的,尤其是pid 内存块尚未完全初始化。
  • 另外,malloc 不应该分配(_N + _V + _C + 1) * sizeof(pid_t) 字节(不是(...) * sizeof(pid_t *) 字节)吗?
  • @EugeneSh。评论有帮助,我在一个无法访问的行中释放 pid。
  • @IanAbbott pid[i] == 0check 是为了确保函数在子进程中只执行一次,你对pid_t *是正确的

标签: c memory-leaks fork valgrind


【解决方案1】:

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

首先,pid 数组内存块根据sizeof(pid_t *) 的倍数分配了错误的大小,而它应该与sizeof(pid_t) 的倍数相同。如果sizeof(pid_t *) 大于sizeof(pid_t),这是对内存的无害浪费,但在sizeof(pid_t *) 小于sizeof(pid_t) 的不同情况下会导致缓冲区溢出。

其次,子进程不会释放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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-14
    • 2015-07-06
    • 2014-06-07
    • 2013-11-20
    • 2011-10-28
    • 2016-01-18
    • 2012-12-13
    相关资源
    最近更新 更多