我有以下代码片段,它打开一个
file
并尝试在子进程中 grep
中的某些内容。使用 vfork()
+ exec()
生成子进程。
FILE *file = fopen("filename.txt", "r");
int pipefds[2];
pipe(pipefds);
pid_t pid = vfork();
if (pid == 0) {
// Child process
close(pipefds[0]); // child doesn't need read end of the pipe
dup2(fileno(file), STDIN_FILENO);
pclose(file);
dup2(pipefds[1], STDOUT_FILENO);
close(pipefds[1]);
execlp("grep", "grep", "pattern", NULL);
}
if (close(pipefds[1]) == -1) { // Why is this successful
perror("close");
return 1;
}
char buffer[1024];
// Why I can read from pipefds[0] if I closed it before exec() function
while (read(pipefds[0], buffer, sizeof(buffer) - 1) > 0) {
std::cout << buffer << std::endl;
}
pclose(file); // Segmentation Fault here, as expected
由于
vfork()
和 execlp()
子进程和父进程共享相同的地址空间,因此在子进程中调用 close(pipefds[1])
也应该改变父进程的 fds
。但是当我在 close(pipefds[1])
之后调用 exec()
时,我没有收到错误,而在 pclose(file)
的情况下却出现了错误。为什么会这样?
在 Linux 中,
vfork()
创建的新进程与原始进程共享相同的虚拟地址空间,但它会获取其他所有内容的副本,包括文件描述符。
但是,请注意 手册页 中的这一点(已添加重点):
所以你使用(来自 POSIX.1)
函数与vfork()
具有相同的效果,除了如果由fork(2)
创建的进程修改除用于vfork()
类型的变量以外的任何数据,则行为未定义。存储pid_t
的返回值,或者从调用vfork()
的函数返回,或者 在成功调用vfork()
或_exit(2)
函数系列之一之前调用任何其他函数。exec(3)
close()
、
dup2()
等都是无效的。最好完全避免使用
vfork()
;如果不走出未定义行为的悬崖,就很难安全地使用(对
exec()
函数的不成功调用可能算作 UB;毕竟它会修改
errno
。你不能记录错误。你可以'不进行文件描述符重定向等......只需坚持
fork()
。)