我们都知道 linux 下 fork 一个子进程出来,他能够继承父进程的文件资源,网络资源等,也从父进程那里拷贝了代码段,数据段,缓冲区等等到自己这里有了新的一份,那么,如果父子进程对于打开的文件资源操作不同,会是怎样的结果呢,先看正常的使用代码
#include
#include
int main(int argc, char **argv) {
FILE *fp;
if ((fp=fopen("test.out", "w+")) == NULL) {
printf("open file failedn");
return -1;
}
fprintf(fp, "hello worldn");
pid_t fpid;
fpid = fork();
if (fpid < 0) {
printf("error in forkn");
return -1;
} else if (fpid == 0) {
// in son process
fprintf(fp, "from son processn");
} else {
// in father process
fprintf(fp, "from father processn");
}
fprintf(fp, "donen");
fclose(fp);
return 0;
}
这份代码可以生成以下的文件
hello world
from father process
done
hello world
from son process
done
需要注意的是第 4 行和第 6 行,多了一个 hello world 和 done
那么,接下来试试在父进程中关掉文件句柄
一开始,我是这样尝试着关闭的
#include
#include
int main(int argc, char **argv) {
FILE *fp;
if ((fp=fopen("test.out", "w+")) == NULL) {
printf("open file failedn");
return -1;
}
fprintf(fp, "hello worldn");
pid_t fpid;
fpid = fork();
if (fpid < 0) {
printf("error in forkn");
return -1;
} else if (fpid == 0) {
// in son process
fprintf(fp, "from son processn");
} else {
// in father process
fprintf(fp, "from father processn");
if (fp != NULL) {
fclose(fp);
}
}
fprintf(fp, "donen");
if (fp != NULL) {
fclose(fp);
}
return 0;
}
跑起来报
*** glibc detected *** ./fork: double free or corruption (top): 0x092fe008 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x73e42)[0xb7699e42]
/lib/i386-linux-gnu/libc.so.6(fclose+0x154)[0xb7689384]
./fork[0x80485dd]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xb763f4d3]
./fork[0x8048421]
======= Memory map: ========
08048000-08049000 r-xp 00000000 08:01 1063761 /home/zrj/c/fork/fork
08049000-0804a000 r--p 00000000 08:01 1063761 /home/zrj/c/fork/fork
0804a000-0804b000 rw-p 00001000 08:01 1063761 /home/zrj/c/fork/fork
092fe000-0931f000 rw-p 00000000 00:00 0 [heap]
b75f7000-b7613000 r-xp 00000000 08:01 1049526 /lib/i386-linux-gnu/libgcc_s.so.1
b7613000-b7614000 r--p 0001b000 08:01 1049526 /lib/i386-linux-gnu/libgcc_s.so.1
b7614000-b7615000 rw-p 0001c000 08:01 1049526 /lib/i386-linux-gnu/libgcc_s.so.1
b7625000-b7626000 rw-p 00000000 00:00 0
b7626000-b77c5000 r-xp 00000000 08:01 1049505 /lib/i386-linux-gnu/libc-2.15.so
b77c5000-b77c7000 r--p 0019f000 08:01 1049505 /lib/i386-linux-gnu/libc-2.15.so
b77c7000-b77c8000 rw-p 001a1000 08:01 1049505 /lib/i386-linux-gnu/libc-2.15.so
b77c8000-b77cb000 rw-p 00000000 00:00 0
b77da000-b77dd000 rw-p 00000000 00:00 0
b77dd000-b77de000 r-xp 00000000 00:00 0 [vdso]
b77de000-b77fe000 r-xp 00000000 08:01 1049485 /lib/i386-linux-gnu/ld-2.15.so
b77fe000-b77ff000 r--p 0001f000 08:01 1049485 /lib/i386-linux-gnu/ld-2.15.so
b77ff000-b7800000 rw-p 00020000 08:01 1049485 /lib/i386-linux-gnu/ld-2.15.so
bf940000-bf961000 rw-p 00000000 00:00 0 [stack]
已放弃 (核心已转储)
既然他说 double free,那就试试只 free 一次,改成这样后可以正常运行
#include
#include
int main(int argc, char **argv) {
FILE *fp;
if ((fp=fopen("test.out", "w+")) == NULL) {
printf("open file failedn");
return -1;
}
fprintf(fp, "hello worldn");
pid_t fpid;
fpid = fork();
if (fpid < 0) {
printf("error in forkn");
return -1;
} else if (fpid == 0) {
// in son process
fprintf(fp, "from son processn");
} else {
// in father process
fprintf(fp, "from father processn");
if (fp != NULL) {
fclose(fp);
}
}
fprintf(fp, "donen");
return 0;
}
结果是
hello world
from father process
hello world
from son process
done
从结果看,父进程关闭文件句柄,对于子进程应该是没有影响的。背后的原因,不知道是否与 copy-on-write 有关。
============================================
update:上面的第一次 fclose 之前虽然有判空,但是在 fclose 之后没有置空,典型的野指针问题,那么试试该改成这样?
#include
#include
int main(int argc, char **argv) {
FILE *fp;
if ((fp=fopen("test.out", "w+")) == NULL) {
printf("open file failedn");
return -1;
}
fprintf(fp, "hello worldn");
pid_t fpid;
fpid = fork();
if (fpid < 0) {
printf("error in forkn");
return -1;
} else if (fpid == 0) {
// in son process
fprintf(fp, "from son processn");
} else {
// in father process
fprintf(fp, "from father processn");
if (fp != NULL) {
fclose(fp);
fp = NULL;
}
}
fprintf(fp, "donen");
if (fp != NULL) {
fclose(fp);
fp = NULL;
}
return 0;
}
结果直接段错误
段错误 (核心已转储)
===============================================
update 最开始的那份代码中,hello world 出现了两次,这个其实是缓冲区引起的,具体的解释可以看这篇文章,http://coolshell.cn/articles/7…,改成这样之后
#include
#include
int main(int argc, char **argv) {
FILE *fp;
if ((fp=fopen("test.out", "w+")) == NULL) {
printf("open file failedn");
return -1;
}
fprintf(fp, "hello world, %dn", getpid());
fflush(fp);
pid_t fpid;
fpid = fork();
if (fpid < 0) {
printf("error in forkn");
return -1;
} else if (fpid == 0) {
// in son process
fprintf(fp, "from son processn");
fflush(fp);
} else {
// in father process
fprintf(fp, "from father processn");
fflush(fp);
}
fprintf(fp, "donen");
fflush(fp);
return 0;
}
输出变成这样
hello world, 13047
from father process
done
from son process
done
这就符合预期了