我正在C ++中的模拟文件系统上实现管道(主要是C)。它需要在主机外壳中运行命令,但在模拟文件系统上执行管道本身。

我可以用 pipe(), fork(), , 和 system() 系统电话,但我更喜欢使用 popen() (它处理创建管道,分叉一个过程并将命令传递给外壳)。这可能是不可能的,因为(我认为)我需要能够从管道的父进程中写入,在子过程结束时阅读,从孩子那里写回输出,最后读取父母的输出。男人页面 popen() 在我的系统上说,双向管是可能的,但是我的代码需要在一个较旧版本的系统上运行,仅支持单向管。

有了上面的单独呼叫,我可以打开/关闭管道来实现这一目标。这是可能的 popen()?

对于一个琐碎的例子,运行 ls -l | grep .txt | grep cmds 我需要:

  • 打开管道和运行的过程 ls -l 在主人上;阅读其输出
  • 管输出 ls -l 回到我的模拟器
  • 打开管道和运行的过程 grep .txt 在主机上的管道输出 ls -l
  • 将其输出回到模拟器(卡在此处)
  • 打开管道和运行的过程 grep cmds 在主机上的管道输出 grep .txt
  • 将其输出回到模拟器并打印

男人流行

来自Mac OS X:

popen() 函数通过创建双向管,分叉和调用外壳来“打开”一个过程。以前打开的任何流 popen()在新的子过程中,父过程中的呼叫已关闭。从历史上看 popen() 用单向管实施;因此,许多实施 popen() 仅允许模式参数指定阅读或写作,而不是两者兼而有之。因为 popen() 现在使用双向管实现,模式参数可以请求双向数据流。该模式参数是指零终止字符串的指针,必须是“ r”,用于读取的“ w”或“ r+”以进行阅读和写作。

有帮助吗?

解决方案

您似乎已经回答了自己的问题。如果您的代码需要在不支持的旧系统上工作 popen 打开双向管,那么您将无法使用 popen (至少不是提供的)。

真正的问题是关于所讨论的旧系统的确切功能。特别是他们 pipe 支持创建双向管?如果他们有一个 pipe 可以创建双向管,但是 popen 那不是,那么我会编写代码的主流 popen 带有双向管,并提供 popen 可以使用双向管,该双管在需要时被用在使用的情况下。

如果您需要足够老化的系统,以至于 pipe 仅支持单向管,然后您几乎坚持使用 pipe, fork, dup2, 等等,自己。我可能仍然将其包裹在有效的功能中 几乎 就像现代版本的 popen, ,但没有返回一个文件句柄,而是用两个文件柄填充一个小结构,一个用于孩子的 stdin, ,另一个用于孩子的 stdout.

其他提示

我建议写自己的功能,为您进行管道/分叉/系统效果。您可以将函数产生一个过程并返回读/写文件描述符,如...

typedef void pfunc_t (int rfd, int wfd);

pid_t pcreate(int fds[2], pfunc_t pfunc) {
    /* Spawn a process from pfunc, returning it's pid. The fds array passed will
     * be filled with two descriptors: fds[0] will read from the child process,
     * and fds[1] will write to it.
     * Similarly, the child process will receive a reading/writing fd set (in
     * that same order) as arguments.
    */
    pid_t pid;
    int pipes[4];

    /* Warning: I'm not handling possible errors in pipe/fork */

    pipe(&pipes[0]); /* Parent read/child write pipe */
    pipe(&pipes[2]); /* Child read/parent write pipe */

    if ((pid = fork()) > 0) {
        /* Parent process */
        fds[0] = pipes[0];
        fds[1] = pipes[3];

        close(pipes[1]);
        close(pipes[2]);

        return pid;

    } else {
        close(pipes[0]);
        close(pipes[3]);

        pfunc(pipes[2], pipes[1]);

        exit(0);
    }

    return -1; /* ? */
}

您可以在其中添加所需的任何功能。

posix规定了 popen() 呼叫并非旨在提供双向通信:

popen()的模式参数是指定I/O模式的字符串:

  1. 如果模式为r,则在启动子进程时,其文件描述符stdout_fileno应为管道的可写端,并且在调用过程中的文件描述符fileNo(流),其中流是popen()返回的流指针,应为管道的可读端。
  2. 如果模式为w,则在启动子进程时,其文件描述符stdin_fileno应为管道的可读端,并且在调用过程中的文件描述符fileNo(流),其中流是popen()返回的流指针,成为管道的可写末端。
  3. 如果模式是任何其他值,则结果未指定。

任何便携式代码都不会做出任何假设。 BSD popen() 与您的问题所描述的相似。

此外,管道与插座不同,每个管道文件描述符都是单向的。您必须创建两个管道,一个为每个方向配置。

在一个 Netresolve 后端我正在与脚本交谈,因此我需要写信给它 stdin 并从中读取 stdout. 。以下功能用stdin执行命令,并将其重定向到管道。您可以使用它并将其适应您的喜好。

static bool
start_subprocess(char *const command[], int *pid, int *infd, int *outfd)
{
    int p1[2], p2[2];

    if (!pid || !infd || !outfd)
        return false;

    if (pipe(p1) == -1)
        goto err_pipe1;
    if (pipe(p2) == -1)
        goto err_pipe2;
    if ((*pid = fork()) == -1)
        goto err_fork;

    if (*pid) {
        /* Parent process. */
        *infd = p1[1];
        *outfd = p2[0];
        close(p1[0]);
        close(p2[1]);
        return true;
    } else {
        /* Child process. */
        dup2(p1[0], 0);
        dup2(p2[1], 1);
        close(p1[0]);
        close(p1[1]);
        close(p2[0]);
        close(p2[1]);
        execvp(*command, command);
        /* Error occured. */
        fprintf(stderr, "error running %s: %s", *command, strerror(errno));
        abort();
    }

err_fork:
    close(p2[1]);
    close(p2[0]);
err_pipe2:
    close(p1[1]);
    close(p1[0]);
err_pipe1:
    return false;
}

https://github.com/crossdistro/netresolve/blob/master/backends/exec.c#l46

(我在 同时读写)

无需在每个过程中创建两个管道并浪费归档的标题。只需使用插座即可。 https://stackoverflow.com/a/25177958/894520

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top