【问题标题】:C stdio redirection to and from child processC stdio 重定向到子进程和从子进程重定向
【发布时间】:2021-07-30 00:03:48
【问题描述】:

我有一个 CLI 程序,它从标准输入读取命令行并将输出写入标准输出。据我了解,当我从终端启动该程序时,它成为该终端的子进程,因此继承了标准输入和标准输出。

我想要另一个程序来启动它,而不是从终端启动 CLI。我希望 CLI 从这个程序 stdout 获取它的输入,并将它的输出重定向到这个程序 stdin。

Terminal Program CLI
stdin -> stdin <-stdout
stdout<- stdout->stdin

我不确定,但我认为我可以这样做:

system("</dev/stdout ./CLI >/dev/stdin &");

有没有更好的方法来做到这一点,我觉得有。我可以创建命名管道,而不是使用标准输入和标准输出。

【问题讨论】:

  • 您需要创建管道并将子进程的标准输入和标准输出连接到管道。然后你的程序写入标准输入管道并从标准输出管道读取。
  • 您无法从/dev/stdout 读取,因为它只对输出开放。 /dev/stdin 反之亦然。
  • 请注意,您很可能会因为缓冲而陷入死锁。

标签: c linux io-redirection stdio


【解决方案1】:

你需要pipe(2):

pipe() 创建一个管道,一个单向的数据通道,可以 用于进程间通信。数组pipefd 用于 返回两个引用管道末端的文件描述符。 pipefd[0] 指管道的读取端。 pipefd[1] 指 到管道的写端。数据写入写入端 管道由内核缓冲,直到从读取中读取 管道的末端。详情请见pipe(7)

你需要dup2(2):

int dup2(int oldfd, int newfd);

dup2() 系统调用执行与dup() 相同的任务,但 而不是使用编号最小的未使用文件描述符,它 使用newfd 中指定的文件描述符编号。其他 换句话说,文件描述符newfd 已调整为现在 指与oldfd相同的打开文件描述。

如果文件描述符newfd之前打开过,则关闭 在被重复使用之前;关闭是静默执行的(即,任何 dup2() 不会报告关闭期间的错误。

所以,您需要的是 两个 管道。由于一根管道只能做一个方向,而你想要全双工,你需要两个。下面是两个非常简单的例子。 cap.c 是一个读取一个字符并将其回显为大写的程序。它会无限期地这样做。

pipe.c 是生成cap.c 的程序,创建两个管道,写入它,读回孩子的响应,并通过stderr 给我们响应(因为我们将有dup2(2)ed 另一个、stdinstdout)。代码已注释。

/* cap.c */
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>

int main(void)
{
    char buf;
    while(1) {
        read(0, &buf, 1);
        buf = toupper(buf);
        write(1, &buf, 1);
    }
}

还有一个……

/* pipe.c */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

int main(void)
{
    int parent_write[2], child_write[2], ws;
    char *cap[2] = {"./cap", NULL}, buf;
    pid_t cpid;

    pipe(parent_write);
    pipe(child_write);

    /* pipe[0] -> read (you read from it)
     * pipe[1] -> write (you write to it)
     *
     * parent_write[0] -> the child will read this
     * parent_write[1] -> the parent writes here
     *
     * child_write[0] -> the parent reads here
     * child_write[1] -> the child writes here
     */

    if(!(cpid = fork())) {
        /* child process, the "add" */

        /* close the read end of child_write
         * close the write end of parent_write
         */
        close(child_write[0]);
        close(parent_write[1]);

        /* dup2 will swap our stdin(0) and stdout(1)
         * for the pipe 
         *
         * parent_write[0] -> our new stdin(0)
         * child_write[1] -> our new stdout(1)
         */
        dup2(parent_write[0], 0);
        dup2(child_write[1], 1);

        execv(*cap, cap);

    } else {
        /* close the read end parent_write
         * close the write end of child_write
         */
        close(parent_write[0]);
        close(child_write[1]);

        /* dup2 will swap...
         *
         * parent_write[1] -> new stdout
         * child_write[0] -> new stdin
         */
        dup2(parent_write[1], 1);
        dup2(child_write[0], 0);

        for(int i = 0; i < 10; i++) {
            buf = 'a' + i;
            write(1, &buf, 1);
            fprintf(stderr, "sent: %c\n", buf);
            read(0, &buf, 1);
            fprintf(stderr, "got : %c\n", buf);
        }
            
        kill(cpid, 1);
        wait(&ws);
    }
    return 0;
}

然后……

$ gcc cap.c -o cap
$ gcc pipe.c -o pipe
$ ./pipe
sent: a
got : A
sent: b
got : B
sent: c
got : C
sent: d
got : D
sent: e
got : E
sent: f
got : F
sent: g
got : G
sent: h
got : H
sent: i
got : I
sent: j
got : J

【讨论】:

    【解决方案2】:

    通常,在终端中,您运行某种 shell,例如 bash、ksh 或 tcsh。这个 shell 通常是终端程序(如 xterm 或 Konsole)的子进程。每当您在终端中启动一个程序时,该程序将成为 shell 的子进程,如下所示:

    xterm -> bash -> my_cli_program

    有一个名为 pstree 的命令可以显示带有子进程的树。

    有一个函数 calle popen() 可以让你运行一个很像 system() 的程序,但它允许你写到程序的标准输入 从它的标准输出读取。不幸的是,您只能使用 popen 读取或写入,因为使用的是单向管道。

    要从子进程的标准输入和标准输出读取和写入,您需要使用 pipe() 创建两个管道。创建管道后,您可以 fork() 然后在子级和父级中使用 dup2() 将进程的标准输出和标准输入与您创建的管道连接起来。一切都连接好后,您可以在子进程中调用 system() 或更好的 execve(),因为 system() 会创建另一个进程。

    【讨论】:

      猜你喜欢
      • 2020-09-01
      • 1970-01-01
      • 2011-11-16
      • 1970-01-01
      • 2012-07-14
      • 2021-03-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多