通过父级执行命令时使用管道

mik*_*ike 6 c linux pipe

我要实现一个无名管道,我必须在父进程中执行该命令,而不是在他的任何一个孩子中执行.每个" - "等于一个管道("|")的调用,也是我有这个代码的赋值的一部分.谁能向我解释为什么它不起作用?

#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h> // for open flags
#include <time.h> // for time measurement
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

void my_exec(char* cmd, char** argv)
{
    int pipefd[2], f;

    if (pipe(pipefd) < 0)
        perror("pipe creation failed");

    f = fork();
    assert(f >= 0);

    if (f == 0) {
        // inside son process - connecting STDOUT to pipe write
      if (dup2(pipefd[1], STDOUT_FILENO) < 0)
            perror("dup2 failed");

        close(pipefd[0]);
        close((int)stdout);


    } else {
        // inside parent process - connecting STDIN to pipe read  and execute command with args
      if (dup2(pipefd[0], STDIN_FILENO) < 0)
            perror("dup2 failed");

        close(pipefd[1]);
       close((int)stdin);



if (execvp(cmd, argv) < 0)
                perror("execvp failed");
        }

}

int main(int argc, char** argv)
{
    assert(strcmp(argv[argc-1], "-"));

    int i;
    for (i = 1; i < argc; ++i) {
        if (!strcmp(argv[i], "-")) {
            argv[i] = NULL;
            my_exec(argv[1], &argv[1]);
            argv = &argv[i];
            argc -= i;
            i = 0;
        }
    }

    char* args[argc];
    args[argc-1] = NULL;

    for (i = 1; i < argc; ++i) {
        args[i-1] = argv[i];
    }

    if (execvp(args[0], args) == -1)
        perror("execvp failed");
    return;
}
Run Code Online (Sandbox Code Playgroud)

命令 :

./mypipe.o ls -l - grep "pipe"
Run Code Online (Sandbox Code Playgroud)

回报

total 24
-rw-rw-r-- 1 omer omer 1463 May 23 19:38 mypipe.c
-rwxrwxr-x 1 omer omer 7563 May 23 19:37 mypipe.o
-rw-rw-rw- 1 omer omer  873 May 23 20:01 nice.c
-rwxrwxr-x 1 omer omer 7417 May 23 19:44 nice.o
-rw-rw-r-- 1 omer omer    0 May 23 17:10 try
Run Code Online (Sandbox Code Playgroud)

这显然意味着管道没有用......任何想法?

我需要确保每次调用np_exec都会启动一个子进程,继续解析其余的参数,而原始父进程执行给定的程序和参数(使用execvp),

编辑:我想我发现了错误:我切换管道的"读写"端.

正确的功能:

void np_exec(char* cmd, char** argv)
{
    int pipefd[2];
    int file;

    if (pipe(pipefd) < 0)
        perror("failed to create pipe");

    file = fork();
    assert(file >= 0);

    if (file != 0) {
        // inside parent process - connecting STDOUT to pipe write and execute command with args
      if (dup2(pipefd[WRITE], STDOUT_FILENO) < 0)
            perror("the function dup2 failed");

        close(pipefd[READ]);
        close((int)stdout);

        if (execvp(cmd, argv) < 0)
            perror("the function execvp failed");

    } else {
        // inside son process - connecting STDIN to pipe read
      if (dup2(pipefd[READ], STDIN_FILENO) < 0)
            perror("the function dup2 failed");

        close(pipefd[WRITE]);
       close((int)stdin);
    }

}
Run Code Online (Sandbox Code Playgroud)

Fil*_*ves 5

更新:所以,事实证明我原来的答案是大错特错。直到现在我才完全明白你想做什么。

你的代码大部分是正确的。它不起作用的原因很简单:shell 假定命令在启动它的进程完成时已完成。

请允许我用一个例子来解释。想象一下你跑步./mypipe a - b - c

以下是幕后发生的事情:

  • shell fork 一个进程来执行mypipe。这是您的程序开始执行的时间。

  • 你的程序分叉,父程序调用exec(a). 子进程按照预期继续解析其他参数并处理管道创建。

所以,现在,此时,您有正在运行的父程序a- 在 shell 看来,它是与命令相对应的进程./mypipe,并且您有一个子进程,shell 完全忽略它,执行以下工作mypipe- 设置管道,读取其余程序来执行等。

所以,现在你有一个竞争条件。因为后面的进程mypipe已经被程序取代a,所以一旦a终止,shell 就假定你键入的命令已经完成(即它假设mypipe已完成),并打印提示符,期望你键入下一个命令。

问题是:a快速终止,您的子进程仍在检查其他程序列表并设置管道,重定向输入和输出,等等。因此,例如,子进程可能仍在创建管道并解析其他程序,而那时a已经完成并将所有内容写入标准输出 - 哎呀!

你如何解决这个问题?简单:颠倒命令执行的顺序。首先执行最后一个命令,然后执行倒数第二个命令,依此类推。为什么?因为如果这样做,父级将是管道上的最后一个,因此它会阻塞等待输入到达管道读取通道。当父进程终止时,意味着管道中的最后一个命令已终止,这正是我们想要的。不存在竞争条件,并且 shell 不会被欺骗认为我们的命令已完成,但实际上并未完成。

因此,这只是从右到左而不是从左到右解析程序列表的问题。另外,如果你这么做了,你就不再需要辅助args阵了,这太棒了!

这是代码 - 经过测试和工作:

#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h> // for open flags
#include <time.h> // for time measurement
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

void my_exec(char* cmd, char** argv)
{
    int pipefd[2], f;

    if (pipe(pipefd) < 0)
        perror("pipe creation failed");

    f = fork();
    assert(f >= 0);

    if (f == 0) {
        if (dup2(pipefd[1], STDOUT_FILENO) < 0)
            perror("dup2 failed");

        close(pipefd[0]);

    } else {
        if (dup2(pipefd[0], STDIN_FILENO) < 0)
            perror("dup2 failed");

        close(pipefd[1]);

        if (execvp(cmd, argv) < 0)
            perror("execvp failed");
        }
}

int main(int argc, char** argv)
{
    assert(strcmp(argv[argc-1], "-"));

    int i;
    for (i = argc-1; i >= 1; i--) {
        if (!strcmp(argv[i], "-")) {
            argv[i] = NULL;
            my_exec(argv[i+1], &argv[i+1]);
            argc = i;
        }
    }

    if (execvp(argv[0], &argv[1]) == -1)
        perror("execvp failed");

    return 0;
}
Run Code Online (Sandbox Code Playgroud)