为什么在 vfork() 之后关闭同一个 fd 不会导致错误?

问题描述 投票:0回答:1

我有以下代码片段,它打开一个

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)
的情况下却出现了错误。为什么会这样?

c linux posix
1个回答
0
投票

在 Linux 中,

vfork()
创建的新进程与原始进程共享相同的虚拟地址空间,但它会获取其他所有内容的副本,包括文件描述符。

但是,请注意 手册页 中的这一点(已添加重点):

(来自 POSIX.1)

vfork()
函数与
fork(2)
具有相同的效果,除了如果由
vfork()
创建的进程修改除用于
pid_t
类型的变量以外的任何数据,则行为未定义。存储
vfork()
的返回值,或者从调用
vfork()
的函数返回,或者 在成功调用
_exit(2)
exec(3)
函数系列之一之前调用任何其他函数。

所以你使用

close()

dup2()
等都是无效的。最好完全避免使用 
vfork()
;如果不走出未定义行为的悬崖,就很难安全地使用(对 
exec() 函数的不成功
调用可能算作 UB;毕竟它会修改 
errno
。你不能记录错误。你可以'不进行文件描述符重定向等......只需坚持 
fork()
。)

© www.soinside.com 2019 - 2024. All rights reserved.