
突然被老弟问了一个问题,有点蒙蔽,怎么说我也写过 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; } 1 codehz 2020 年 3 月 14 日 via iPhone 程序结束是保证能释放所占用的普通内存的(除了 hugepage 和共享内存) 如果观察到使用内存增多,那么肯定是因为系统缓存了一些东西 fork 到 exec 之间的这段时间所有内存分配都可以简单的无视,不需要考虑释放,exec 之后全都会被吃掉,valgrind 无需跟踪这部分的“泄漏”,除非 exec 失败,通过 exit 退出,这样 valgrind 才能跟踪到泄漏 |
2 52coder OP @codehz 单就这个例子而言:myargs[0] = “ wc”改成这样就不纠结了,我也有点搞不清楚 fork 之后子进程里的机制,execvp 失败退出,避免走到其它不该走的逻辑。例子这里有点不严谨。execvp 成功了不返回,失败了还是要回来,这个函数有点不厚道呀。 |
3 ysc3839 2020 年 3 月 14 日 > 我认为如果程序快速执行完退出,不必太过纠结资源释放问题,进程退出了,系统会回收资源 可以认为是对的,比如 Windows 下许多 GUI 程序在启动时会加载图标之类的资源,直到退出也不会释放。包括微软 VS 向导创建的代码也是这样的。 |
4 lance6716 2020 年 3 月 14 日 当然是用 gdb 看一下喽 |
5 geelaw 2020 年 3 月 15 日 如果惟一可能的释放点是进程结束之前,那么是没有必要进行这个操作的,因为“大厦马上就要拆除了,没有必要打扫房间”。 不过你的第一个问题可以用简单的逻辑论证为什么你不需要释放:如果你释放了 strdup 产生的内存,则无法正确调用 execvp (除非你准备静态存储用来放置参数,但这显然无端增加麻烦,系统不会这样设计),因此你无法释放这段内存。 第二个问题取决于你的对内存泄露的定义。 |
6 msg7086 2020 年 3 月 15 日 如果你的程序需要继续运行下去,那么你可以在 execvp 后释放 strdup 出来的空间。 execvp 执行成功以后你的进程就被替换了,内存当然全没了。所以最多也只要考虑执行失败的情况。 |