C программа не может использовать pipe для выполнения команды more в execlp для просмотра результатов программы - PullRequest
1 голос
/ 07 марта 2020

Заранее благодарен за любую помощь.

Я пытаюсь воспроизвести поведение команды оболочки ls -1 /usr/include | more с помощью программы C. Я написал этот код:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
    int page[2];   // page is the name of my pipe
    pipe(page);
    switch (fork()){
    case -1:
        exit(1);
        break;
    case 0:;
        close(page[1]);
        dup2(page[0], 0);
        close(page[0]);
        execlp("more", "more", NULL);
    default:
        close(page[0]);
        dup2(page[1], 1);
        close(page[1]);
        execlp("ls", "ls", "-1", "/usr/include", NULL);
        break;
    }
}

Но он печатает только одну страницу (как сделал бы more) и вызывает странное поведение, которое блокирует мой терминал (вынуждая меня использовать reset, чтобы установить его обратно в нормальный).

Ответы [ 2 ]

0 голосов
/ 07 марта 2020

Ваш исходный код создает своего рода условие гонки между родительским и дочерним процессами и оболочкой, которая запустила вашу программу. Процесс ls завершается до того, как more сможет прочитать все данные из канала, и поскольку в вашей программе родительский процесс заменяется процессом ls, когда этот процесс ls завершается (после записи всех его выходных данных в буфер канала), который он закрывает, и при этом закрывает канал и возвращает управление оболочке, которая сразу же готовится прочитать другую команду.

Итак, изначально и more, и оболочка могут читать с того же устройства TTY (оно читает из своего дескриптора STDERR, все еще подключенного к вашему TTY), а затем, когда more в конечном итоге получает какой-либо ввод, он снова попытается прочитать из канала (его STDIN) и получить конец файла (канал был закрыт в конце записи при выходе из ls), и поэтому more теперь также завершится (без вывода дополнительных выводов). Существует также возможная гонка между процессом more и оболочкой в ​​отношении того, какие (пере) установки режимов драйвера TTY и когда.

Альтернативная реализация вашей программы для исходного родительского процесса для запуска двух дочерних процессов. процессы, один для more и один для ls, а затем ждать завершения обоих процессов, но для этого, конечно, потребуются дополнительные системные ресурсы.

0 голосов
/ 07 марта 2020

Я только что понял, что роли родителя и дочернего процесса перепутаны. Тот, кто запускает команду more, должен быть родительским. Поскольку more является интерактивной командой , терминал будет лучше реагировать на нее как родитель (я предполагаю).

Итак, чтобы решить мою проблему, Я поменял роли родителя и ребенка.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(){
    int page[2];   // page is the name of my pipe
    pipe(page);
    switch (fork()){
    case -1:
        exit(1);
        break;
    case 0:;
        close(page[0]);
        dup2(page[1], 1);
        close(page[1]);
        execlp("ls", "ls", "-1", "/usr/include", NULL);
        break;
    default:
        close(page[1]);
        dup2(page[0], 0);
        close(page[0]);
        execlp("more", "more", NULL);
    }
}

ПОЧЕМУ ЭТО РЕШАЕТ ПРОБЛЕМУ? (Я до сих пор не понимаю, почему это сработало!)

...