V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
52coder
V2EX  ›  程序员

fork vs execvp,子进程中内存泄露?

  •  
  •   52coder · 2020-03-14 23:09:40 +08:00 · 1855 次点击
    这是一个创建于 1475 天前的主题,其中的信息可能已经有所发展或是发生改变。

    突然被老弟问了一个问题,有点蒙蔽,怎么说我也写过 1 年 C 呀,哈哈哈 持续征集C 短小精悍开源代码 完整代码如下:

    #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;
    }
    

    子进程里使用了 strdup,没看到内存释放的地方,即使 valgrind 开启了--trace-children=yes 也没有检测到内存泄露,这里是否存在内存泄露?之前有看过 execvp 会替换 fork 之后的子进程的内存空间?不清楚这块内存关系是怎么样的?

    我和老弟争执一点如下,我认为如果程序快速执行完退出,不必太过纠结资源释放问题,进程退出了,系统会回收资源,当然好的习惯是不用的时候去 free 掉。针对常驻进程,需要重点关注内存泄露问题。 我认为下面的程序没有内存泄露,即使 valgrind 检测出来:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        int *p = (int *)malloc(10 *sizeof(int));
        printf("program exit\n");
        
        return 0;
    }
    
    9 条回复    2020-03-15 15:04:15 +08:00
    codehz
        1
    codehz  
       2020-03-14 23:33:44 +08:00 via iPhone
    程序结束是保证能释放所占用的普通内存的(除了 hugepage 和共享内存)
    如果观察到使用内存增多,那么肯定是因为系统缓存了一些东西
    fork 到 exec 之间的这段时间所有内存分配都可以简单的无视,不需要考虑释放,exec 之后全都会被吃掉,valgrind 无需跟踪这部分的“泄漏”,除非 exec 失败,通过 exit 退出,这样 valgrind 才能跟踪到泄漏
    52coder
        2
    52coder  
    OP
       2020-03-14 23:43:00 +08:00
    @codehz 单就这个例子而言:myargs[0] = “ wc”改成这样就不纠结了,我也有点搞不清楚 fork 之后子进程里的机制,execvp 失败退出,避免走到其它不该走的逻辑。例子这里有点不严谨。execvp 成功了不返回,失败了还是要回来,这个函数有点不厚道呀。
    ysc3839
        3
    ysc3839  
       2020-03-14 23:45:31 +08:00
    > 我认为如果程序快速执行完退出,不必太过纠结资源释放问题,进程退出了,系统会回收资源

    可以认为是对的,比如 Windows 下许多 GUI 程序在启动时会加载图标之类的资源,直到退出也不会释放。包括微软 VS 向导创建的代码也是这样的。
    lance6716
        4
    lance6716  
       2020-03-14 23:46:38 +08:00
    当然是用 gdb 看一下喽
    geelaw
        5
    geelaw  
       2020-03-15 03:49:29 +08:00   ❤️ 1
    如果惟一可能的释放点是进程结束之前,那么是没有必要进行这个操作的,因为“大厦马上就要拆除了,没有必要打扫房间”。

    不过你的第一个问题可以用简单的逻辑论证为什么你不需要释放:如果你释放了 strdup 产生的内存,则无法正确调用 execvp (除非你准备静态存储用来放置参数,但这显然无端增加麻烦,系统不会这样设计),因此你无法释放这段内存。

    第二个问题取决于你的对内存泄露的定义。
    msg7086
        6
    msg7086  
       2020-03-15 05:50:34 +08:00
    如果你的程序需要继续运行下去,那么你可以在 execvp 后释放 strdup 出来的空间。
    execvp 执行成功以后你的进程就被替换了,内存当然全没了。所以最多也只要考虑执行失败的情况。
    52coder
        7
    52coder  
    OP
       2020-03-15 10:40:26 +08:00
    @msg7086 执行成功以后进程被替换?执行时需要的内存从哪来的?有介绍这方面的文章吗?我觉得应该是你说的这个点。
    msg7086
        8
    msg7086  
       2020-03-15 11:30:53 +08:00
    @52coder exec 不就是把自身进程替换成目标程序吗。当前进程的代码和内存空间都会被抹平,然后开始执行新的程序,让新的程序来重新申请内存空间。
    比如你这里运行了 exec("wc")以后,你程序的代码和内存都会消失,然后内核把 wc 的代码加载进内存,然后运行 wc 的入口,让 wc 去申请和使用它自己新的内存空间。
    52coder
        9
    52coder  
    OP
       2020-03-15 15:04:15 +08:00
    @msg7086 学习了,多谢大佬
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2855 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 13:12 · PVG 21:12 · LAX 06:12 · JFK 09:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.