Команды конвейера bash echo и bc в программу на языке C

Я пытаюсь создать небольшую программу на C, которая реализует конвейер из двух команд bash: echo $ arithmeticOperation | До нашей эры

$ arithmeticOperation - это строка, принимаемая в качестве входных данных.

Программа отлично работает, выполняя первую команду, но когда я запускаю вторую, я получаю правильный результат, но дочерний процесс, выполняющий bc, остается зависшим, предотвращая завершение дочернего процесса.

Таким образом, в этой строке блокируется родительский процесс: waitpid (pid2, NULL, 0);

Как вы думаете, в чем может быть проблема?

Извините, если я неправильно задал вопрос, это мой первый вопрос. Спасибо.



    #define SYSCALL(r,c,e) if((r=c)==-1) { perror(e);exit(EXIT_FAILURE);}

    int main(){
        char buf[128];
        int pfd[2],err;
        pid_t pid1,pid2;
        SYSCALL(err,pipe(pfd),"pipe");
        switch (pid1=fork()) {
            case -1: { perror("fork"); exit(EXIT_FAILURE);}
            case 0 : { 
                scanf("%s",buf);
                SYSCALL(err,dup2(pfd[1],1),"dup");
                close(pfd[1]);
                close(pfd[0]);
                execl("/bin/echo","echo",buf,(char *)NULL);
                return 1;
            }   
        }   
        switch (pid2=fork() ){
             case -1 : { perror("fork"); exit(EXIT_FAILURE);}
             case 0 : { 
                 SYSCALL(err,dup2(pfd[0],0),"dup");
                 close(pfd[1]);
                 close(pfd[0]);
     //          execl("/usr/bin/bc","bc",(char *)NULL);
                 execlp("bc","bc",(char *)NULL);
                return 1;
            }   
        }   
    printf("waiting . . . \n");
    waitpid(pid1,NULL,0);
    printf("wait\n");
    waitpid(pid2,NULL,0);
    close(pfd[1]);
    close(pfd[0]);
    return 0;
    }

Итак, если я цифру «1 + 1» в качестве входной строки, я получаю правильный вывод, но тогда процесс, выполняющий bc, никогда не завершается


person F. Anicetti    schedule 17.05.2019    source источник
comment
Ваш родительский процесс должен закрыть дескрипторы файлов каналов перед ожиданием, чтобы bc получил EOF.   -  person Jonathan Leffler    schedule 17.05.2019
comment
Это решено, спасибо большое. я должен позвонить close (pfd []); до подождите вместо этого после.   -  person F. Anicetti    schedule 17.05.2019


Ответы (1)


Как я отмечал в комментарии, ваш родительский процесс должен закрыть дескрипторы файлов для канала до ожидания bc (и вы согласились, что это решает проблему).

Это происходит из-за того, что bc имеет канал, открытый для чтения, а у родителя канал открыт для записи, и ядро ​​считает, что родительский канал может, таким образом, отправлять данные в bc. Не будет, но может.

При обращении с трубами нужно быть очень осторожным. Вы тщательно избегали обычной проблемы с закрытием достаточного количества файловых дескрипторов в дочерних элементах.


Практическое правило: если вы dup2() один конец канала к стандартному вводу или стандартному выводу, закройте оба исходных файловых дескриптора, возвращаемых _ 5_ как можно скорее. В частности, вам следует закрыть их перед использованием любого из семейства exec*() функций.

Правило также применяется, если вы дублируете дескрипторы с помощью dup() или _ 8_ с F_DUPFD


Мне нужно расширить это, чтобы охватить и родительские процессы.

Если родительский процесс не собирается связываться ни с одним из своих дочерних процессов через канал, он должен убедиться, что он закрывает оба конца канала, чтобы его потомки могли получать указания EOF при чтении (или получать сигналы SIGPIPE или ошибки записи при записи) , а не блокировать на неопределенный срок. Обычно родительский элемент должен закрывать по крайней мере один конец канала - для программы было бы крайне необычно читать и писать на обоих концах одного канала.

person Jonathan Leffler    schedule 17.05.2019