child 进程在退出前未释放内存是否泄漏?

Is it a leak if memory isn't freed in a child process before exit?

到处都有类似的问题,其中一个已关闭来自 this 堆栈交换站点。但即便如此,我还是从阅读中学到了很多东西 none,其中完全回答了我的问题。

问题来了,说我有这个程序(是一个非常简化的版本)

inline void execute(char *cmd)
{
    int exitstat = 0;
    //lots of other thins happed
    exitstat = execve(cmd, cmdarg, environ);
    free(cmd), free(other passed adress);  ## DO I NEED THIS, since I am already about to exit
    _exit (exitstat);
}

int main(void)
{
    int childstat;
    char *str = malloc(6); //I also have many more allocated space in heap in the parent process
    strcpy(str, "Hello");

    pid_t childid = fork() //creat a child process, which will also get a copy of all the heap memories even tho it is CoW.
    if (childid < 0)
        exit(-1);
    if (child == 0)
    {
        execute(str);
    }
    else
    {
        wait(&childstat);
        free(str);
    }


//do somethign else with str with other functions and the rest of the program
}

在这个程序中,parentprocess做了一段时间,在heap分配了很多进程,free一些,其他留着稍后在某个时候它想要执行一些命令,但它不想终止,所以它创建了一个 child 来执行它的咬合。

child 然后调用另一个函数,该函数将执行一些任务,最后使用 execveexecute 传递给它的命令。如果成功就没有问题,因为执行的程序将处理所有分配的 spaces,但如果失败,child 将退出并返回一个状态码。 parent 等待 child 回答,当它回答时,它会继续下一个例程。这里的问题是,当 child 执行失败时,所有堆数据仍然分配,但这有关系吗?因为,

  1. 即将在下一行退出,
  2. 尽管我刚开始使用它,但我了解到在 fork 期间创建了一个新的 process,因此内存泄漏不应影响 parent,因为child快要死了
  3. 如果我的假设是正确的,而且这个泄漏实际上并不重要,为什么 valgrind 会为他们哀叹?
  4. 是否有更好的方法来 free child 堆中的所有内存而不实际将所有内存(本例中的 str, 和其他内存)传递给执行函数,每次都费力地调用freekill() 有这方面的机制吗?

编辑

这是工作代码

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

inline void execute(char *cmd)
{
    extern char **environ;
    int exitstat = 0;
    char *cmdarg[2];
    cmdarg[0] = cmd;
    cmdarg[1] = NULL;
    exitstat = execve(cmd, cmdarg, environ);
    free(cmd);
    _exit (exitstat);
}

int main(void)
{
    int childstat;
    char *str = malloc(6);
    pid_t  childid;

    strcpy(str, "Hello");
    childid = fork();
    if (childid < 0)
        exit(-1);
    if (childid == 0)
    {
        execute(str);
    }
    else
    {
        wait(&childstat);
        free(str);
    }

    return (0);
}

当 child 进程内部有空闲时,这里是 valgrinds 结果。

当运行与child进程中的free被注释时。(#free(cmd)在execute函数中)。

如您所见,总体错误 () since this programm is short lived. But in my real program which has an infinite loop every problem in the child () 无关紧要。所以我问我是否担心这个 child 进程在退出之前泄漏的内存并追捕它们,或者只是让它们保持在另一个虚拟 space 中并在它们的进程退出时被清理(但在那种情况下,如果相应的进程在退出期间清理它们,为什么 valgrind 仍然抱怨它们)

exitstat = execve(cmd, cmdarg, environ);
free(cmd), free(other passed adress);

如果成功,execve 不会return。之后没有代码执行,执行终止并启动 cmd 进程。操作系统无论如何都会释放当前进程持有的所有内存。如果 execve 调用成功,代码 free(cmd), free(other passed adress); 将不会执行

唯一可能与这些留置权相关的情况是 execve 错误。如果 execve return 出现错误,是的,您应该释放该内存。我非常怀疑大多数带有 execve 的程序实际上会这样做,我相信他们只会在 execve 调用失败后调用 abort()

inline void execute(char *cmd)

请阅读 inlineinline 很棘手。我建议不要使用它,忘记它的存在。我怀疑这里的目的是制作一个内联函数——编译器无论如何都没有其他函数可以选择。修复代码中的拼写错误后,由于对 execute 的未定义引用,我无法编译代码 - inline 必须删除。

The problem here is that when the child fails to execute all the heap data remains allocated, but does that matter?

最终取决于在child失败后想做什么。如果你只想调用 exit 并且你不关心这种情况下的内存泄漏,那么就不要关心它也没关系。

我看到 glibc exec*.c 函数尽量避免动态分配并使用可变长度数组或 alloca()

If my assumption is correct and this leaks doesn't actually matter, why does valgrind lament about them?

没有。如果您更正代码中的所有错误,那么 valgrind 将不会在调用 execve 之前“哀叹”内存泄漏(好吧,除非 execve 调用失败)。

Would there be a better way to free all the memories in the child heap with out actually passing all the memores (str, and the others in this example) to the execute function and laboriously call free each time?

“更好”往往是基于意见的。主观上:没有。但是 writing/using 自己的动态分配包装器和垃圾收集器来释放所有内存也是一种选择。

does kill() have a mechanism for this?

不,发送信号似乎与释放内存无关。

没有简单的解决方案。

终止时,OS 将为进程释放所有内存。这意味着您 没有 来释放所有内容。然而,对于不再需要的内存,通常最好尽快释放它。这样就留下了诸如缓冲区之类的东西,可能会一直使用到终止前的最后一刻。

释放内存的原因。

  1. 我只能想到一个。如果你在最后使用了大量的块,那么很难从谷壳中挑选出小麦。要么你最终得到大量无人查看的日志,要么你必须维护一个抑制文件。无论哪种方式,您都有可能忽视真正的问题。

不释放内存的原因

  1. 性能提升较小
  2. 有时候要释放所有东西是相当困难的。例如,如果您使用 putenv() 那么要知道字符串是被添加(并且需要释放)还是被替换(一定不能被释放)就有点棘手了。
  3. 如果您需要使用 atexit() 来释放东西,那么您可以通过 atexit() 调用的函数数量是有限制的。如果您在许多模块中释放了很多东西,那可能是个问题。

我的建议是尝试释放大部分东西,但不要着急,否则你会被递减法则所困扰 returns。对于难以释放的百分之几,明智地使用抑制文件。