Каково поведение grep, когда он получает ввод через канал POSIX и выводит в канал POSIX? - PullRequest
2 голосов
/ 26 ноября 2010

У меня есть эта программа, которая разветвляет и создает три канала POSIX для дочерних элементов stdin, stdout и stderr.После разветвления дочерний и родительский элементы закрывают соответствующие концы своих соответствующих каналов, так что дочерний элемент может только читать из канала stdin и записывать только в каналы stdout и stderr (и наоборот относится к родительскому каналу).Затем дочерний процесс закрывает и перехватывает дочерние stdin, stdout и stderr до его открытых концов канала, а затем использует execvp для выполнения программы, имя которой было передано в качестве аргументов родительской программы.Программа ведет себя так, как и должно для таких команд, как cat, ls, rm, banner, & ps, без каких-либо заметных проблем.

Однако, когда я запускаю программу в терминале следующим образом:

программа grep blah

она принимает ввод, но ничего не выводит втерминал, но когда я пытаюсь:

grep blah

в терминале grep ждет ввода и выводит его обратно, если на входе содержалось слово «бла».Итак, вопрос в том, что это из-за чего-то не так с моей программой, как показано ниже?Или это нормально для grep, когда он должен общаться через каналы POSIX?Спасибо за прочтение.

Вот эта программа (прошу прощения за форматирование при первой публикации):

#include <stdio.h>
#include <stdlib.h> 
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <limits.h>        
int status, cpid; int child = 1;
char mode = 'c';

sigset_t sigMask;
void childHandler(){
    sigprocmask(SIG_BLOCK,&sigMask,NULL);
    int i = waitpid(-1,&status,WNOHANG);
    if(i){
        fprintf(stderr, "The child <%d> has terminated with code <%d>\n",cpid,         WEXITSTATUS(status));
        child = 0;
        mode = 'c';
    }
    sigprocmask(SIG_UNBLOCK,&sigMask,NULL);
 }


 int DEBUG = 0;
 int SLOW = 1;

 fd_set readfds,writefds,errorfds;
 struct timeval timeout;
 int numready;
 int cin,cout,cerr,in,out,err;
 int dcout = 0;
 int dcerr = 0;
 int p2c_in[2], c2p_out[2], c2p_err[2];
 int maxfd = 2;
 int tout = 0;

void doSelect(){
    FD_ZERO(&writefds);
    FD_ZERO(&readfds);

    FD_SET(fileno(stdin), &readfds);
    FD_SET(fileno(stdout), &writefds);
    FD_SET(fileno(stderr), &writefds);

    FD_SET(p2c_in[1], &writefds);
    FD_SET(c2p_out[0], &readfds);
    FD_SET(c2p_err[0], &readfds);

    if(!tout) {
        numready = select((maxfd + 1),&readfds,&writefds,NULL,NULL);
    }
    else numready = select((maxfd + 1),&readfds,&writefds,NULL,&timeout);

    in = FD_ISSET(fileno(stdin), &readfds);
    out = FD_ISSET(fileno(stdout), &writefds);
    err = FD_ISSET(fileno(stderr), &writefds);

    cin = FD_ISSET(p2c_in[1], &writefds);
    if(dcout) cout = 0;
    else cout = FD_ISSET(c2p_out[0], &readfds);
    if(dcerr) cerr = 0;
    else cerr = FD_ISSET(c2p_err[0], &readfds);

    if(DEBUG){
        fprintf(stderr,"\nstdin: %d\n",in);
        fprintf(stderr,"stdout: %d\n",out);
        fprintf(stderr,"stderr: %d\n",err);
        fprintf(stderr,"p2c_in[1]: %d\n",cin);
        fprintf(stderr,"c2p_out[0]: %d\n",cout);
        fprintf(stderr,"c2p_err[0]: %d\n\n",cerr);
        if(SLOW)sleep(2);
    }
 }



  int main(int agrc, char* argv[]){
    //Naming convention of pipes: int [intial of write-end process]2[intial of read-end process] & 
    //_err represents the file descriptor  which the pipe is suppose to 'filter'

    int val = 0, dblval = 0;
    int pipe_result,signal_result,close_result;


      //This pipe has the parent writing and the child reading from the parent's input this pipe is to filter the child's stdin
     if((pipe_result = pipe(p2c_in)) < 0){
        perror("Creation of the pipe for the child's stdin has failed");
        return pipe_result;
     }
    if(p2c_in[1] > maxfd) maxfd = p2c_in[1];

     //This pipe has the child writing and the parent reading from the child's input this pipe is to filter the child's stdout
    if((pipe_result = pipe(c2p_out)) < 0){
            perror("Creation of the pipe for the child's stdout has failed");
        return pipe_result;
    }
    if(c2p_out[0] > maxfd) maxfd = c2p_out[0];
     //This pipe has the child writing and the parent reading from the child's input this pipe is to filter the child's stderr
    if((pipe_result = pipe(c2p_err)) < 0){
        perror("Creation of the pipe for the child's stderr has failed");
        return pipe_result;
    }
    if(c2p_err[0] > maxfd) maxfd = c2p_err[0];
    if(DEBUG){
        fprintf(stderr,"stdin: %d\n",fileno(stdin));
        fprintf(stderr,"stdout: %d\n",fileno(stdout));
        fprintf(stderr,"stderr: %d\n",fileno(stderr));
        fprintf(stderr,"p2c_in[0]: %d\n",p2c_in[0]);
        fprintf(stderr,"p2c_in[1]: %d\n",p2c_in[1]);
        fprintf(stderr,"c2p_out[0]: %d\n",c2p_out[0]);
        fprintf(stderr,"c2p_out[1]: %d\n",c2p_out[1]);
        fprintf(stderr,"c2p_err[0]: %d\n",c2p_err[0]);
        fprintf(stderr,"c2p_err[1]: %d\n",c2p_err[1]);
    }


    sigemptyset(&sigMask);
    sigaddset(&sigMask,SIGCHLD);
    if((signal_result = signal(SIGCHLD, childHandler)) == SIG_ERR){
        perror("Signal handler binding has failed");
        return signal_result;
    }

    //This creates the child process
    if((cpid = fork()) < 0){
        perror("Creation of the child process has failed");
        return cpid;
    }

    if(!cpid){ //Child-exclusive code
        //This makes it so the child can only read input from the stdin pipe
        if((close_result = close(p2c_in[1])) < 0){
            perror("Closing the write end of the child's stdin pipe has failed");
            exit(close_result);
        }

        //This makes it so the child can only write input to the stdout pipe
        if((close_result = close(c2p_out[0])) < 0){
            perror("Closing the read end of the child's stdout pipe has failed");
            exit(close_result);
        }

        //This makes it so the child can only write input to the stderr pipe
        if((close_result = close(c2p_err[0])) < 0){
            perror("Closing the read end of the child's stderr pipe has failed");
            exit(close_result);
        }

        //This closes the child's standard input
        if((close_result = close(0)) < 0){
            perror("Closing the child's standard input has failed");
            exit(close_result);
        }

        int dup_result;
        if((dup_result = dup(p2c_in[0])) < 0){
            perror("Duplication of the read end of the child's stdin pipe has failed");
            exit(dup_result);
        }

        //This closes the child's standard output
        if((close_result = close(1)) < 0){
            perror("Closing the child's standard output has failed");
            exit(close_result);
        }

        if((dup_result = dup(c2p_out[1])) < 0){
            perror("Duplication of the write end of the child's stdout pipe has failed"); 
            exit(dup_result);
        }

        //This closes the child's standard error
        if((close_result = close(2)) < 0){
            perror("Closing the child's standard error has failed");
            exit(close_result);
        }

        if((dup_result = dup(c2p_err[1])) < 0){
            perror("Duplication of the write end of the child's stderr pipe has failed");
            exit(dup_result);
        }

        execvp(argv[1], &argv[1]);
        perror("execvp has failed:");

        if((close_result = close(p2c_in[0])) < 0){
            perror("Closing the write end of the child's stdin pipe has failed");
            exit(close_result);
        }

        if((close_result = close(c2p_out[1])) < 0){
            perror("Closing the read end of the child's stdout pipe has failed");
            exit(close_result);
        }

        if((close_result = close(c2p_err[1])) < 0){
            perror("Closing the read end of the child's stderr pipe has failed");
            exit(close_result);
        }

        exit(66); //This occurs after p2c_in's write end is closed
    } else { //Parent-exclusive code

        //This makes it so the parent can only write input to the stdin pipe
        if((close_result = close(p2c_in[0])) < 0){
            perror("Closing the read end of the child's stdin pipe has failed");
            return close_result;
        }

        //This makes it so the parent can read only input from the stdout pipe
        if((close_result = close(c2p_out[1])) < 0){
            perror("Closing the write end of the child's stdout pipe has failed");
            return close_result;
        }

        //This makes it so the parent can read only input from the stderr pipe
        if((close_result = close(c2p_err[1])) < 0){
            perror("Closing the write end of the child's stderr pipe has failed");
            return close_result;
        }



        timeout.tv_sec = 1; timeout.tv_usec = 0;
        char mode = 'c';
        char t_mode;
        int maxlines = 20;
        int sig; int lcount = 0; int fill = 0;
        char buf_err;
        char buf_in[MAX_CANON];
        char buf_out;
        int read_resultin,read_resultout,read_resulterr;
                int write_resultin,write_resultout,write_resulterr;

        while(1){
            if(DEBUG)fprintf(stderr,"<%d> While Begin \n",getpid());
            doSelect();

            if(in && cin){
                if(((read_resultin = read(fileno(stdin), &buf_in, MAX_CANON)) > 0) && child){
                    //write(2, &buf_in, read_resultin);
                    if((write_resultin = write(p2c_in[1], &buf_in, read_resultin)) > -1){
                        fill = write_resultin;
                        if(DEBUG)fprintf(stderr,"<%d> Input \n",getpid());
                        doSelect();
                        while(cin && child && (fill < read_resultin)){
                            if(DEBUG)fprintf(stderr,"<%d> Input Loop \n",getpid());
                            write_resultin = write(p2c_in[1], &buf_in, read_resultin - fill);
                            fill += write_resultin;
                            doSelect();
                        }
                        //if(read_resultin < 0) perror("read from stdin without a / failed");
                    }
                    else perror("Reading to child's stdin has failed");

                }

            }
            if(DEBUG)fprintf(stderr,"<%d> Input Loop End \n",getpid());
            doSelect();

            if (out && cout){
                if(DEBUG)fprintf(stderr,"<%d> Output \n",getpid());
                while(out && cout){
                    if(DEBUG)fprintf(stderr,"<%d> Output Loop \n",getpid());
                    if((read_resultout = read(c2p_out[0], &buf_out, 1)) == 1)
                        write(fileno(stdout), &buf_out, 1);
                    else if(!read_resultout && !child/**/){
                        if(DEBUG)fprintf(stderr,"<%d> No More Output \n",getpid());
                        dcout = 1;
                    }
                    else if(read_resultout < 0)
                        perror("Output Read");

                    doSelect();
                }


            }

            if(DEBUG)fprintf(stderr,"<%d> Output End \n",getpid());
            doSelect();
            if (err && cerr){
                /**/if(DEBUG)fprintf(stdout,"<%d> Error \n",getpid());
                while(err && cerr){
                    /**/if(DEBUG)fprintf(stdout,"<%d> Error Loop \n",getpid());
                    if((read_resulterr = read(c2p_err[0], &buf_err, 1)) == 1){
                        write_resulterr = write(fileno(stderr), &buf_err, 1);
                        if(write_resulterr < 0) perror("Error Wirte");
                    }else if(!read_resulterr&& !child/**/){
                        dcerr = 1;
                        if(DEBUG)fprintf(stderr,"<%d> No More Error \n",getpid());
                    }
                    else if(read_resulterr < 1) perror("Error Read");
                    doSelect();
                }
                /**/if(DEBUG)fprintf(stdout,"<%d> Error Loop End \n",getpid());
                if(read_resulterr < 0) perror("Reading from child's stdout has failed");
                /**/if(DEBUG)fprintf(stdout,"<%d> Error End \n",getpid());

            }

            if(!child && dcout && dcerr) break;
        }




        //This closes up the write end of p2c_in that way the child will be able to terminate for it will recieve an EOF
        if((close_result = close(p2c_in[1])) < 0){
            perror("The last closing of the write end of the child's stdin pipe has failed");
            exit(close_result);
        }

        //This closes up the read end of c2p_out
        if((close_result = close(c2p_out[0])) < 0){
            perror("The last closing of the read end of the child's stdout pipe has failed");
            exit(close_result);
        }

        //This closes up the read end of c2p_err
        if((close_result = close(c2p_err[0])) < 0){
            perror("The last closing of the read end of the child's stderr pipe has failed");
            exit(close_result);
        }



    }
   }

1 Ответ

7 голосов
/ 26 ноября 2010

Многие программы имеют линейную буферизацию, когда имеют дело с вводом / выводом терминала, но в других случаях используют гораздо более крупные чанки (например, каналы).grep является одним из них.

Если вы используете grep из GNU coreutils, вы можете использовать grep --line-buffered, чтобы заставить grep выводить построчно, вместо того, чтобы ждать, пока в очередь будет добавлено больше данныхвверх.Однако вы действительно должны исправить свою программу, чтобы закрыть ее конец входного канала, когда вы получите EOF на stdin: тогда grep заметит EOF на своем конце входного канала и сбросит оставшийся вывод.

...