如何在fork()之后防止子进程干扰父进程的标准输入流?

3
我有一个程序从stdin读取数据,同时有时需要fork()并异步执行命令。在这个fork之后的短时间内(可能是在命令正在运行时),父进程似乎会丢失从stdin读取的数据。如果只丢失了一个字节,程序将无法恢复。
fork()的man页面说子进程继承了父进程的打开文件描述符副本,但是在fork()之后并在execvp()之前尝试关闭(STDIN_FILENO)并没有起到作用。
我的程序基本上做了这些:
if (we_need_to_run_a_command_asynchronously) {
  if (!fork()) {
    close(STDIN_FILENO);
    char *args[] = { "command", "arg", "etc", 0 };
    setpriority(PRIO_PROCESS, 0, -19);
    execvp("command", args);
    exit(0);  // Ignore errors
  }
}

我该如何确保子进程无法干扰父进程的标准输入(或其他属于父进程的内容)?执行的命令通常是一个shell脚本。

我不知道我的程序出了什么问题,但只要它不运行任何命令,它就可以正常运行。仅在运行命令后有时(不总是)从标准输入读取的数据会被染污,这就是我怀疑问题与fork()之后的标准输入有关的原因。


1
你能否通过编写两个小测试程序来验证你的假设,一个从stdin读取,另一个不读取,并让你的父程序执行它们并观察结果? - Aasmund Eldhuset
请分享一个小的可编译程序。这可以帮助我们进入其中。 - theadnangondal
与其使用显式的close,你可以为标准输入设置close-on-exec标志。然而,问题同样可能有不同的原因(提供一个简短的演示程序有助于解决问题)。 - Thomas Dickey
也许可以生成一个 pthread 而不是一个新进程? - user1055568
你执行的命令有标准输入流处于开启状态。你可以提供 /dev/null 以读取数据。除了 command 是一个 shell 内置命令之外,你对它还了解多少呢?这个命令是你自己编写的吗?它从哪里读取数据?基本上,如果你将其标准输入流连接到 /dev/null,那么你不应该遇到问题。 - Jonathan Leffler
@user1055568:有两个原因说明为什么pthread不是一个好主意。(1)该命令已经存在独立的形式;将其升级为线程运行并升级父进程以作为线程至少会有些棘手。(2)这降低了操作之间的隔离性;两个线程将共享相同的标准输入,因此“子线程”的任何输入操作都会搞乱“父线程”的输入。对我来说这听起来不是一个好主意。 - Jonathan Leffler
3个回答

3

不要关闭标准输入流,而是可以打开另一个文件,并使用dup2将新的文件描述符复制到标准输入流上。如果在fork之后,在exec之前将/dev/null重定向到标准输入流,则子进程不会干扰父进程的标准输入流。


1
/dev/null更常规,比/dev/zero更好的选择,这是有充分理由的。 - Jonathan Leffler

2

我想我已经解决了这个问题。原来在我的程序的一个很少走到的路径中,执行了我忘记在fork()后对stdin做任何处理的命令。下面的代码确实可行:

int pid = fork();
if (!pid) {
  int fd = open("/dev/null", O_RDWR);
  dup2(fd, 0);
  dup2(fd, 1);
  dup2(fd, 2);
  if (fd > 2)
    close(fd);

  execvp(...);

因此,我犯了个错误,但也许我的问题仍可以作为一个重要的提醒,即在分叉执行应该与父进程分开运行的命令时,通常需要处理stdin/stdout/stderr。我认为上述方法是一种清晰的处理方式。


0

我在 POSIX 规范中找到了一个有趣的注释,与 execve() 和相关函数有关:

如果在执行 exec 函数族中的任何一个成功调用后,文件描述符 0、1 或 2 将被关闭,则实现可以为新进程映像中的文件描述符打开未指定的文件。如果使用文件描述符 0 未打开读取或使用文件描述符 1 或 2 未打开写入来执行标准实用程序或符合规范的应用程序,则应将实用程序或应用程序执行的环境视为不符合规范,因此实用程序或应用程序可能无法按照本标准所述的方式运行。

有趣的是,你可以推测你的实现是否正在使用“may”选项。 显式地将 /dev/null 打开作为标准输入是最安全的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接