Попытка реализовать конвейер в C - оболочка зависает и не работает команды - PullRequest
0 голосов
/ 09 февраля 2019

Я пытаюсь запустить эту команду ps -j | more.Я думаю, что правильно настроил каналы, но по какой-то причине он просто зависает:

enter image description here

Я вызываю форк, который работает ps -j и вторая вилка, которая работает more и соединяет их с трубами.

По некоторым причинам это все еще не работает должным образом.

Код ниже:

#define BUF_SIZE 100

int main() {

    pid_t pid1, pid2;
    int save_stdin, save_stdout;
    int fd[2];

    char *argv1[] = { "/bin/ps", "-j", NULL };
    char *argv2[] = { "/bin/more", NULL };

    char prompt[] = "Press enter: ";
    char buffer[BUF_SIZE];

    write(STDOUT_FILENO, prompt, sizeof(prompt) - 1);

    ssize_t readIn = readIn = read(STDIN_FILENO, buffer, BUF_SIZE);
    buffer[readIn - 1] = '\0';

    printf("readIn: %d\n", readIn);

    pipe(fd);

    pid1 = fork();
    if (pid1 == 0) { // child
        close(fd[0]);
        save_stdout = dup(1);
        dup2(fd[1], STDOUT_FILENO);
        execvp(argv1[0], argv1);
        close(fd[1]);
    } else { // parent
        pid2 = fork();
        if(pid2 == 0) {
            save_stdin = dup(0);
            dup2(fd[0], STDIN_FILENO);
            execvp(argv2[0], argv2);
            close(fd[0]);
        } else {

        }
    }

    dup2(save_stdin, 0);
    dup2(save_stdout, 1);
    close(save_stdin);
    close(save_stdout);

    int i = 1;
    do {
        wait(NULL);
    } while (i-- > 0);


    exit(0);

}

Любая помощь с благодарностью!

РЕДАКТИРОВАТЬ:

Я пытался закрыть трубы после dup2(), но до execvp(), но он все еще висит:

pipe(fd);

pid1 = fork();
if (pid1 == 0) { // child
    dup2(fd[1], STDOUT_FILENO);
    close(fd[1]);
    close(fd[0]);
    int res1 = execvp(argv1[0], argv1);
    printf("exec1: %d\n", res1);
} else { // parent
    pid2 = fork();
    if(pid2 == 0) {
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        close(fd[1]);
        int res2 = execvp(argv2[0], argv2);
        printf("exec2: %d\n", res2);
    } else {

    }
}

close(fd[1]);
close(fd[0]);
printf("finished\n");

int i = 1;
do {
    wait(NULL);
} while (i-- > 0);

1 Ответ

0 голосов
/ 09 февраля 2019

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

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

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

Вот рабочий код, который правильно закрывает трубы.Я удалил компонент пути команд, поскольку (а) они были неправильны для моей машины, и (б) нет смысла использовать execvp(), если вы указали путь к команде.Я сообщаю о большинстве ошибок (но я не проверяю результаты вызовов read() и write(). Я также сообщаю о статусах выхода детей. Дети сообщают и выходят, если им не удается выполнить команду. Ваш кодс 'save_stdin` и т. д. еще больше сбивало с толку, потому что переменные были неинициализированы в родительском объекте, где выполнялась последовательность:

dup2(save_stdin, 0);
dup2(save_stdout, 1);
close(save_stdin);
close(save_stdout);

. Ни вы, ни я не знаем, что делают эти два вызова dup2(), нобыло вероятно, что они потерпели неудачу или что они сделали что-то странное с вашими стандартными каналами ввода / вывода.

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

#define BUF_SIZE 100

int main(void)
{
    pid_t pid1, pid2;
    int fd[2];

    char *argv1[] = { "ps", "-j", NULL };   // Remove PATH because using execvp()
    char *argv2[] = { "more", NULL };       // Remove PATH because using execvp()

    char prompt[] = "Press enter: ";
    char buffer[BUF_SIZE];

    write(STDOUT_FILENO, prompt, sizeof(prompt) - 1);

    ssize_t readIn = readIn = read(STDIN_FILENO, buffer, BUF_SIZE);
    buffer[readIn - 1] = '\0';

    printf("readIn: %zd\n", readIn);

    if (pipe(fd) < 0)
    {
        fprintf(stderr, "failed to create pipe\n");
        exit(EXIT_FAILURE);
    }

    if ((pid1 = fork()) < 0)
    {
        fprintf(stderr, "failed to fork - 1\n");
        exit(EXIT_FAILURE);
    }

    if (pid1 == 0)   // child
    {
        dup2(fd[1], STDOUT_FILENO);
        close(fd[0]);
        close(fd[1]);
        execvp(argv1[0], argv1);
        fprintf(stderr, "failed to execute %s\n", argv1[0]);
        exit(EXIT_FAILURE);
    }

    if ((pid2 = fork()) < 0)
    {
        fprintf(stderr, "failed to fork - 2\n");
        exit(EXIT_FAILURE);
    }

    if (pid2 == 0)
    {
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        close(fd[1]);
        execvp(argv2[0], argv2);
        fprintf(stderr, "failed to execute %s\n", argv2[0]);
        exit(EXIT_FAILURE);
    }

    close(fd[0]);
    close(fd[1]);

    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
        fprintf(stderr, "%d: child %d exited with status 0x%.4X\n",
                (int)getpid(), corpse, (unsigned)status);

    return 0;
}

Программа была pipe41 (с небольшим редактированием на выходе, чтобы удалить то, что на самом деле неВам не нужно знать, что я делал в других окнах):

$ ./pipe41
Press enter: 
readIn: 1
USER              PID  PPID  PGID   SESS JOBC STAT   TT       TIME COMMAND
jonathanleffler  6618  6617  6618      0    1 S    s000    0:00.11 -bash
jonathanleffler  6629  6628  6629      0    1 S+   s001    0:00.04 -bash
jonathanleffler  6660  6645  6660      0    1 S+   s002    0:00.04 -bash
jonathanleffler  6716  6695  6716      0    1 S+   s003    0:00.04 -bash
jonathanleffler  6776  6746  6776      0    1 S+   s004    0:00.04 -bash
jonathanleffler  6800  6771  6800      0    1 S+   s005    0:00.04 -bash
jonathanleffler  7487  7486  7487      0    1 S    s006    0:00.04 -bash
jonathanleffler  9558  9557  9558      0    1 S    s007    0:00.06 -bash
jonathanleffler 10375  9558 10375      0    1 S+   s007    0:00.01 ./pipe41
jonathanleffler 10377 10375 10375      0    1 S+   s007    0:00.00 more
(END)10375: child 10376 exited with status 0x0000
10375: child 10377 exited with status 0x0000
$
...