【问题标题】:Do not share stdin of process started from shell with shell itself不要与 shell 本身共享从 shell 启动的进程的标准输入
【发布时间】:2019-01-01 08:34:01
【问题描述】:

我正在编写一个具有一些类似于 shell 的功能的 C 程序,其中包括在后台通过 readline 接口启动的子进程中运行的程序。

我目前遇到的一个问题是,一旦我生成了这样的子进程并再次开始输入输入字符,这些字符就会被生成的子进程通过标准输入接收。这并不奇怪,因为我在分叉后没有修改子文件描述符,而且我理解这意味着它们都与主进程共享STDIN_FILENO 下的相同流。

然而,这不是我想要的。我希望我的程序表现得像例如bash 在这方面,在后台启动正在进行的进程后重新获得提示后,在该提示上键入的任何字符都只会被 bash 看到,而不会被生成的进程看到。这就是我的问题所在,因为我什至不明白 bash 是如何做到的。查看/proc/.../fd/0bash 以及任何衍生的进程,它们总是指向同一个伪终端。只需将生成的进程 stdin 重定向到例如/dev/null 显然也会产生不同的行为。

这里我不需要完整的实现,只是一个一般性的提示。

编辑:我刚刚发现,如果我使用带有O_RDWR 标志的open 命令打开/dev/null,则将stdin 重定向到/dev/null 会起作用。 然后,我的程序似乎按照我想要的方式运行,只是当我运行 ps 时,以这种方式启动的子进程显示为 <defunct>。我不确定为什么会这样。

编辑:我想我现在已经弄清楚了,最简单的方法似乎是创建一个伪终端从站,然后将孩子的标准输入重定向到它:

int master;
int slave;

openpty(&master, &slave, nullptr, nullptr, nullptr);

dup2(slave, STDIN_FILENO);

【问题讨论】:

  • 看看 heirloom bourne shell 如何处理作业可能更容易,因为在 jobs.c 中运行约 800 行 C 代码,而在“太大太慢”中运行约 3,300 行重击
  • 我会这样做的。我相信<defunct> 只是由于线程中等待子进程的错误,但无论如何,看看更专业的实现可能是个好主意。

标签: bash c stdin readline


【解决方案1】:

好的,所以您希望能够在 Unix 或类 Unix 系统上启动异步子进程,其输入直接连接到 /dev/null,并且在死亡时不会变成僵尸。

对于重定向部分,常见的做法是关闭fork和exec之间的标准输入文件(文件号0):

pid = fork();
if (pid == 0) {        // in child
    close(0);          // close standard input
    execve(...);
}

如果不关闭文件 1 和 2,子进程的输出仍然会到终端。您可以关闭它们,但任何输出都会被静默丢弃...

对于zombie()部分,在fork之前忽略SIGCHLD信号就足够了:

signal(SIGCHLD, SIG_IGN);
pid = fork();                // this child will never become a zombie process

参考:man fork、man wait、man execve

【讨论】:

  • 我没有考虑您答案的第二部分,所以这很有帮助。但是,如果我运行例如,简单的关闭标准输入似乎不是重定向部分的正确解决方案。 cat & 在终端中,生成的进程应该仍然打开一些标准输入流,因为 cat 会尝试从中读取,对吗?但是避免僵尸化以及将标准输入复制到 /dev/null 应该可以解决问题。
  • 再次尝试,这两种解决方案都不起作用,当execve 加载的程序尝试从标准输入读取时,关闭标准输入会产生Bad file descriptor 错误。但是使用/dev/null 解决方案,例如cat 毕竟会立即终止。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-13
  • 2012-04-12
  • 2010-10-07
  • 2012-04-15
  • 2015-01-26
相关资源
最近更新 更多