试图运行" LS | grep的R"与" execvp()" [英] trying to run "ls | grep r" with "execvp()"

查看:86
本文介绍了试图运行" LS | grep的R"与" execvp()"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


  1. 我创建了一个管道两个子进程之间,
    首先,我跑 LS ,其中写到正确的FD,
    然后,我跑的grep - [R ,从正确的FD读取,


  2. 我可以在该的grep 命令工作正常的终端看到(输出)


  3. 的问题是,的grep 不退出,它停留在那里,即使 LS ISN 'T运行了


对于其他程序管道工作正常..

 为(i = 0; I< commands_num;我++){// EXEC所有的命令瞬间
    如果(pcommands [I] ._ flag_pipe_out == 1){//创建管道,如果有必要
        如果(管(pipe_fd)== -1){
            PERROR(错误:\\管道()\\失败);
        }
        pcommands [Ⅰ] ._ fd_out = pipe_fd [1];
        pcommands [I + 1] ._ fd_in = pipe_fd [0];
    }
    PID =叉(); //孩子的exec命令
    如果(PID == -1){
        PERROR(错误:\\fork()的\\失败);
        打破;
    }否则如果(!PID){//子进程        如果(pcommands [I] ._ flag_pipe_in == 1){//如果有一个管道,这条命令
            如果(dup2(pcommands [I] ._ fd_in,STDIN)== -1){
                PERROR(错误:\\dup2()\\失败);
                出口(0);
            }
            关闭(pcommands [I] ._ fd_in);
        }        如果(pcommands [I] ._ flag_pipe_out == 1){//如果有一个管这个命令
            如果(dup2(pcommands [I] ._ fd_out STDOUT)== -1){
                PERROR(错误:\\dup2()\\失败);
                出口(0);
            }
            关闭(pcommands [I] ._ fd_out);
        }
        execvp(pcommands [Ⅰ] ._命令[0],pcommands [Ⅰ] ._命令); //运行命令        PERROR(错误:\\execvp()\\失败);
        出口(0);
    }如果(PID大于0)其他{//父进程
    waitpid函数(PID,NULL,WUNTRACED);
    }
}
//关闭所有打开的FD的
对于(i = 0; I< commands_num;我++){
    如果(pcommands [I] ._ fd_in!= STDIN){//如果有一个其他标准输入不是0
        关闭(pcommands [I] ._ fd_in);
    }
    如果(pcommands [I] ._ fd_out!= STDOUT){//如果有一个其他标准输出不是1
        关闭(pcommands [I] ._ fd_out);
    }
}

所以,我有一个命令即时 pcommands [I]
它具有:
pipein,pipeout旗
fdin,fdout,
和一个char **(为真正的命令,如ls -l)

让我们说什么都好,
这意味着:

  pcommands [0]:
pipein = 0
pipeout = 1
焦炭** = {LS, - 1,NULL}pcommands [1]:
pipein = 1
pipeout = 0
焦炭** = {grep的,R,NULL}

现在,循环会去两次(因为我有两个命令瞬间)
在第一时间,它会看到pcommands [0]具有pipeout == 1
创建管道
做叉
pcommands [0]具有pipeout == 1
孩子:dup2到标准输出
execvp

第二次:
不创建管
做叉
儿童:
所述pcomands [1]具有pipein == 1
然后:dup2到输入
exevp
..

这个命令的作品,我的输出是:


  

errors.log exer2.pdf multipal_try


(所有带R的事情)
但随后被卡住,并没有摆脱的grep ..
在另一端,我可以看到的grep 仍在工作。

我希望关闭所有的fd的我需要关闭......

我不明白为什么没有工作,好像我这样做是正确的(当然,它适用于其他命令。)

有人可以请帮助?谢谢


解决方案

您不关闭足够的管道文件描述符。

拇指

规则:


  • 如果您使用 DUP() dup2()来复制一个管道文件描述符标准输入或标准输出时,应关闭的原来的管道文件描述符的两个

您还需要确保,如果父shell创建管道,它关闭两个管道文件描述符的副本。

还要注意的是在管线的处理应该允许同时运行。特别是,管道有一个有限的容量,并且处理块时,就没有余地留在管。限制可以是相当小的(POSIX强制要求它必须至少4昆明植物研究所,但仅此而已)。如果你的程序处理兆字节的数据,他们必须被允许在管道中同时运行。因此, waitpid函数()应该是启动儿童外循环发生。您还需要等待之前关闭在父进程的管道;否则,子阅读管决不会看到的EOF(因为父,在理论上,写入到管道,尽管它不会)。

您有结构成员名字开始以下划线。这是危险的。以下划线开头的名称被保留用于实施。 C标准说:


  

ISO / IEC 9899:2011§7.1.3保留标识符


  
  

- 与下划线和大写字母或其他开头的所有标识符
  下划线,始终保留用于任何用途。结果
   - 以下划线开头的所有标识符总是保留用作标识符
  在这两个普通和标记的名称空间文件范围。


这意味着,如果你遇到了问题,那么麻烦的是你的,不系统的。显然,你的code的作品,但你应该知道,你可能会遇到的问题,这是最明智的,以避免它们。


样品code

这是一个基于code以上的固定SSCCE:

 的#include<&ASSERT.H GT;
#包括LT&;&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&; SYS / wait.h>
#包括LT&;&unistd.h中GT;typedef结构命令命令;
结构命令
{
    INT _fd_out;
    INT _fd_in;
    INT _flag_pipe_in;
    INT _flag_pipe_out;
    焦炭** _命令;
};的typedef诠释管[2];枚举{STDIN = STDIN_FILENO,STDOUT = STDOUT_FILENO,STDERR = STDERR_FILENO};INT主要(无效)
{
    字符* ls_cmd [] = {LS,0};
    字符* grep_cmd [] = {grep的,R,0};
    命令命令[] =
    {
        {
            ._fd_in = 0,._flag_pipe_in = 0,
            ._fd_out = 1,._flag_pipe_out = 1,
            ._commands = ls_cmd,
        },
        {
            ._fd_in = 0,._flag_pipe_in = 1,
            ._fd_out = 1,._flag_pipe_out = 0,
            ._commands = grep_cmd,
        }
    };
    INT commands_num = sizeof的(命令)/的sizeof(命令[0]);    / *允许的valgrind来检查内存* /
    命令* pcommands =的malloc(commands_num *的sizeof(命令));
    的for(int i = 0; I< commands_num;我++)
        pcommands [I] =命令[I]    的for(int i = 0; I< commands_num;我++){// EXEC所有的命令瞬间
        如果(pcommands [I] ._ flag_pipe_out == 1){//创建管道,如果有必要
            管pipe_fd;
            如果(管(pipe_fd)== -1){
                PERROR(错误:\\管道()\\失败);
            }
            pcommands [Ⅰ] ._ fd_out = pipe_fd [1];
            pcommands [I + 1] ._ fd_in = pipe_fd [0];
        }
        将为pid_t PID =叉(); //孩子的exec命令
        如果(PID == -1){
            PERROR(错误:\\fork()的\\失败);
            打破;
        }否则如果(!PID){//子进程            如果(pcommands [I] ._ flag_pipe_in == 1){//如果有一个管道,这条命令
                断言(I 0);
                断言(pcommands [I-1] ._ flag_pipe_out == 1);
                断言(pcommands [I-1] ._ fd_out> STDERR);
                如果(dup2(pcommands [I] ._ fd_in,STDIN)== -1){
                    PERROR(错误:\\dup2()\\失败);
                    出口(0);
                }
                关闭(pcommands [I] ._ fd_in);
                关闭(pcommands [I-1] ._ fd_out);
            }            如果(pcommands [I] ._ flag_pipe_out == 1){//如果有一个管这个命令
                断言(I< commands_num - 1);
                断言(pcommands [I + 1] ._ flag_pipe_in == 1);
                断言(pcommands [I + 1] ._ fd_in> STDERR);
                如果(dup2(pcommands [I] ._ fd_out STDOUT)== -1){
                    PERROR(错误:\\dup2()\\失败);
                    出口(0);
                }
                关闭(pcommands [I] ._ fd_out);
                关闭(pcommands [I + 1] ._ fd_in);
            }
            execvp(pcommands [Ⅰ] ._命令[0],pcommands [Ⅰ] ._命令); //运行命令            PERROR(错误:\\execvp()\\失败);
            出口(1);
        }
        其他
            的printf(儿童PID%d个运行\\ n,(INT)PID);
    }    //关闭所有打开的管道FD的
    的for(int i = 0; I< commands_num;我++){
        如果(pcommands [Ⅰ] ._ fd_in!= STDIN){//如果有另一个标准输入不是0
            关闭(pcommands [I] ._ fd_in);
        }
        如果(pcommands [Ⅰ] ._ fd_out!= STDOUT){//如果有另一个标准输出不是1
            关闭(pcommands [I] ._ fd_out);
        }
    }    INT状态;
    将为pid_t尸体;
    而((尸体= waitpid函数(-1,&放大器;状态,0))大于0)
        的printf((INT)的尸体,身份儿童PID%d个,状态为0x%.4X \\ n死);    免费(pcommands);    返回(0);
}


  

只是为了我的知识,你会怎么做,所以它不会得到无可争议的杂乱?


我可能会保持管道的信息,使我的孩子没有必要担心包含在断言条件表达式(之前或之后在管道访问为​​孩子的孩子的信息)。如果每个子只需要在它自己的数据结构来获取信息,它是清洁的。我想重新组织'结构司令部,因此它包含了两个管道,再加上指标该管道包含需要关闭的信息。在许多方面,从你有没有什么根本不同;只是在那个孩子的 I 的整洁只需要看看 pcommands [I]

您可以在ÇMinishell加入管道看到在不同的上下文了部分答案。

  1. I created a pipe between two child processes, first, I run ls, which writes to the proper fd, then, I run grep r, which reads from the proper fd,

  2. I can see in the terminal that the grep command works fine (the output)

  3. The problem is that grep doesn't quit, it stays there, even though ls isn't running anymore

for other programs the pipe works fine..

for (i = 0; i < commands_num ; i++) {   //exec all the commands instants
    if (pcommands[i]._flag_pipe_out == 1) { //creates pipe if necessary 
        if (pipe(pipe_fd) == -1) {
            perror("Error: \"pipe()\" failed");
        }
        pcommands[i]._fd_out = pipe_fd[1];
        pcommands[i+1]._fd_in = pipe_fd[0]; 
    }
    pid = fork();   //the child exec the commands  
    if (pid == -1) {
        perror("Error: \"fork()\" failed");
        break;          
    } else if (!pid) { //child process

        if (pcommands[i]._flag_pipe_in == 1) {  //if there was a pipe to this command
            if (dup2(pcommands[i]._fd_in, STDIN) == -1) {
                perror("Error: \"dup2()\" failed");
                exit(0);
            }
            close(pcommands[i]._fd_in);
        }

        if (pcommands[i]._flag_pipe_out == 1) { //if there was a pipe from this command
            if (dup2(pcommands[i]._fd_out, STDOUT) == -1) {
                perror("Error: \"dup2()\" failed");
                exit(0);
            }
            close(pcommands[i]._fd_out);
        } 
        execvp(pcommands[i]._commands[0] , pcommands[i]._commands); //run the command

        perror("Error: \"execvp()\" failed");
        exit(0);
    } else if (pid > 0) { //father process
    waitpid(pid, NULL, WUNTRACED);
    }
}
//closing all the open fd's
for (i = 0; i < commands_num ; i++) {
    if (pcommands[i]._fd_in != STDIN) { //if there was an other stdin that is not 0
        close(pcommands[i]._fd_in);
    }           
    if (pcommands[i]._fd_out != STDOUT) { //if there was an other stdout that is not 1
        close(pcommands[i]._fd_out);            
    }   
}

So, I have a "command" instant pcommands[i] It has: a flag of pipein,pipeout fdin,fdout, and a char** (for the real command, like "ls -l")

lets say everything is good, that means that:

pcommands[0]:
pipein=0
pipeout=1
char** = {"ls","-l",NULL}

pcommands[1]:
pipein=1
pipeout=0
char** = {"grep","r",NULL}

now, the loop will go twice (because I have two commands instants) at the first time, it will see the pcommands[0] has pipeout==1 create pipe do fork pcommands[0] has pipeout==1 child: dup2 to the stdout execvp

second time: doesn't create pipe do fork child: the pcomands[1] has pipein==1 then: dup2 to the input exevp ..

this command works, my output is:

errors.log exer2.pdf multipal_try

(all the things with 'r') but then it get stuck, and doesn't get out of grep.. in an other terminal i can see grep is still working

I hope I close all the fd's I need to close...

I don't understand why doesn't it work, it seems like I do it right (well, it works for other commands..)

can someone please help? thanks

解决方案

You aren't closing enough pipe file descriptors.

Rule of Thumb:

  • If you use dup() or dup2() to duplicate a pipe file descriptor to standard input or standard output, you should close both of the original pipe file descriptors.

You also need to be sure that if the parent shell creates the pipe, it closes both of its copies of the pipe file descriptors.

Also note that the processes in a pipeline should be allowed to run concurrently. In particular, pipes have a limited capacity, and a process blocks when there's no room left in the pipe. The limit can be quite small (POSIX mandates it must be at least 4 KiB, but that's all). If your programs deal with megabytes of data, they must be allowed to run concurrently in the pipeline. Therefore, the waitpid() should occur outside the loop that launches the children. You also need to close the pipes in the parent process before waiting; otherwise, the child reading the pipe will never see EOF (because the parent could, in theory, write to the pipe, even though it won't).

You have structure members whose names start with an underscore. That's dangerous. Names starting with an underscore are reserved for the implementation. The C standard says:

ISO/IEC 9899:2011 §7.1.3 Reserved Identifiers

— All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
— All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.

That means that if you run into problems, then the trouble is yours, not the system's. Obviously, your code works, but you should be aware of the problems you could run into and it is wisest to avoid them.


Sample Code

This is a fixed SSCCE based on the code above:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

typedef struct Command Command;
struct Command
{
    int _fd_out;
    int _fd_in;
    int _flag_pipe_in;
    int _flag_pipe_out;
    char **_commands;
};

typedef int Pipe[2];

enum { STDIN = STDIN_FILENO, STDOUT = STDOUT_FILENO, STDERR = STDERR_FILENO };

int main(void)
{
    char *ls_cmd[] = { "ls", 0 };
    char *grep_cmd[] = { "grep", "r", 0 };
    Command commands[] =
    {
        {
            ._fd_in  = 0, ._flag_pipe_in  = 0,
            ._fd_out = 1, ._flag_pipe_out = 1,
            ._commands = ls_cmd,
        },
        {
            ._fd_in  = 0, ._flag_pipe_in  = 1,
            ._fd_out = 1, ._flag_pipe_out = 0,
            ._commands = grep_cmd,
        }
    };
    int commands_num = sizeof(commands) / sizeof(commands[0]);

    /* Allow valgrind to check memory */
    Command *pcommands = malloc(commands_num * sizeof(Command));
    for (int i = 0; i < commands_num; i++)
        pcommands[i] = commands[i];

    for (int i = 0; i < commands_num; i++) {   //exec all the commands instants
        if (pcommands[i]._flag_pipe_out == 1) { //creates pipe if necessary
            Pipe pipe_fd;
            if (pipe(pipe_fd) == -1) {
                perror("Error: \"pipe()\" failed");
            }
            pcommands[i]._fd_out = pipe_fd[1];
            pcommands[i+1]._fd_in = pipe_fd[0];
        }
        pid_t pid = fork();   //the child exec the commands
        if (pid == -1) {
            perror("Error: \"fork()\" failed");
            break;
        } else if (!pid) { //child process

            if (pcommands[i]._flag_pipe_in == 1) {  //if there was a pipe to this command
                assert(i > 0);
                assert(pcommands[i-1]._flag_pipe_out == 1);
                assert(pcommands[i-1]._fd_out > STDERR);
                if (dup2(pcommands[i]._fd_in, STDIN) == -1) {
                    perror("Error: \"dup2()\" failed");
                    exit(0);
                }
                close(pcommands[i]._fd_in);
                close(pcommands[i-1]._fd_out);
            }

            if (pcommands[i]._flag_pipe_out == 1) { //if there was a pipe from this command
                assert(i < commands_num - 1);
                assert(pcommands[i+1]._flag_pipe_in == 1);
                assert(pcommands[i+1]._fd_in > STDERR);
                if (dup2(pcommands[i]._fd_out, STDOUT) == -1) {
                    perror("Error: \"dup2()\" failed");
                    exit(0);
                }
                close(pcommands[i]._fd_out);
                close(pcommands[i+1]._fd_in);
            }
            execvp(pcommands[i]._commands[0] , pcommands[i]._commands); //run the command

            perror("Error: \"execvp()\" failed");
            exit(1);
        }
        else
            printf("Child PID %d running\n", (int)pid);
    }

    //closing all the open pipe fd's
    for (int i = 0; i < commands_num; i++) {
        if (pcommands[i]._fd_in != STDIN) { //if there was another stdin that is not 0
            close(pcommands[i]._fd_in);
        }
        if (pcommands[i]._fd_out != STDOUT) { //if there was another stdout that is not 1
            close(pcommands[i]._fd_out);
        }
    }

    int status;
    pid_t corpse;
    while ((corpse = waitpid(-1, &status, 0)) > 0)
        printf("Child PID %d died with status 0x%.4X\n", (int)corpse, status);

    free(pcommands);

    return(0);
}

Just for my knowledge, how would you do it, so it won't get "indisputably messy"?

I'd probably keep the pipe information so that I the child didn't need to worry about the conditionals contained in the asserts (accessing the child information for the child before or after it in the pipeline). If each child only needs to access information in its own data structure, it is cleaner. I'd reorganize the 'struct Command' so it contained two pipes, plus indicators for which pipe contains information that needs closing. In many ways, not radically different from what you've got; just tidier in that child i only needs to look at pcommands[i].

You can see a partial answer in a different context at C Minishell adding pipelines.

这篇关于试图运行&QUOT; LS | grep的R&QUOT;与&QUOT; execvp()&QUOT;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆