трубопровод в c, 2 вилки разговаривают с главным - PullRequest
3 голосов
/ 13 апреля 2020

Хорошо, короткий рассказ таков: у меня есть программа, которая должна выполнять X, и она выполняет 0.25X. Я использую 2 вилки и 4 трубы, и я не знаю, как отладить это. (используя eclipse c / c ++ в linux env).

Длинная история: у меня есть программа, которая должна вычислять gcd (наибольший общий делитель) из текстового файла, содержащего пары целых чисел. В этой программе есть отец (основной) и 2 ребенка (вилки), которым нужно разговаривать с отцом по трубам. (2 канала для каждого ребенка.) Когда я компилирую ie и запускаю программу в Ubuntu, я не получаю ошибки, но программа не выполняет свои задачи. Я понятия не имею, где / почему это ломается. Как я могу отладить это? Я кодирую в eclipse c / c ++, и отладчик не может обработать вилки. Когда я отлаживаю, он читает все числа из файла (не вычисляет gcd), но когда я запускаю в терминале ubuntu, он читает только первую строку и разрывает. вот полный код:

int main(int argc, char **argv) {
if (argc != 2 || strcmp(argv[1], "--help") == 0) {
    fprintf(stderr, "‫‪usage: %s <FILE NAME>\n", argv[0]);
    return EXIT_FAILURE;
}

int pfd_child1_r[2], pfd_child1_w[2], pfd_child2_r[2], pfd_child2_w[2];
if (pipe(pfd_child1_r) == -1 || pipe(pfd_child1_w) == -1
        || pipe(pfd_child2_r) == -1 || pipe(pfd_child2_w) == -1) {
    perror("cannot pipe()");
    return EXIT_FAILURE;
}

createChilds(pfd_child1_r, pfd_child1_w, pfd_child2_r, pfd_child2_w);

FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
    perror("fopen(): ");
    return EXIT_FAILURE;
}

char line[100];
char *token;
int numbers[2], num, line_count = 1, counter = 0, result = 0;

while (fgets(line, sizeof(line), fp) != NULL) {
    token = strtok(line, " ");
    while (token != NULL) {
        num = atoi(token);
        if (num < 1 || counter == 2) {
            fprintf(stderr, "‫‪illegal‬‬ ‫‪input‬‬ at line %d\n",
                    line_count);
            return EXIT_FAILURE;
        }
        numbers[counter] = num;
        counter++;
        token = strtok(NULL, " ");
    }
    counter = 0;
    if (line_count % 2 == 0) { // use first child
        write(pfd_child1_w[1], &numbers[0], sizeof(int));
        write(pfd_child1_w[1], &numbers[1], sizeof(int));
    } else { // use second child
        write(pfd_child2_w[1], &numbers[0], sizeof(int));
        write(pfd_child2_w[1], &numbers[1], sizeof(int));
    }

    if (line_count > 1) { // after first run alternate to get result
        if (line_count % 2 == 0) { // read from second child
            read(pfd_child2_r[0], &result, sizeof(int));
            printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
        } else { // read from first child
            read(pfd_child1_r[0], &result, sizeof(int));
            printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
        }
    }

    line_count++;
}

fclose(fp);
return EXIT_SUCCESS;
}

void createChilds(int pfd_child1_r[2], int pfd_child1_w[2], int pfd_child2_r[2],

    int pfd_child2_w[2]) {

switch (fork()) {
case -1:
    perror("cannot fork()");
    exit(EXIT_FAILURE);

case 0: /* First child: */
    if (close(pfd_child1_r[0]) == -1) { /* Read end is unused */
        perror("cannot close()");
        exit(EXIT_FAILURE);
    }

    if (close(pfd_child1_w[1]) == -1) { /* Write end is unused */
        perror("cannot close()");
        exit(EXIT_FAILURE);
    }

    /* Duplicate stdout on write end of pipe; close duplicated descriptor */

    if (pfd_child1_w[1] != STDOUT_FILENO) { /* Defensive check */
        if (dup2(pfd_child1_r[1], STDOUT_FILENO) == -1) {
            perror("cannot dup2()");
            exit(EXIT_FAILURE);
        }
        if (close(pfd_child1_r[1]) == -1) {
            perror("cannot close()");
            exit(EXIT_FAILURE);
        }
    }

    /* Duplicate stdin on read end of pipe; close duplicated descriptor */

    if (pfd_child1_w[1] != STDIN_FILENO) { /* Defensive check */
        if (dup2(pfd_child1_w[0], STDIN_FILENO) == -1) {
            perror("cannot dup2()");
            exit(EXIT_FAILURE);
        }
        if (close(pfd_child1_w[0]) == -1) {
            perror("cannot close()");
            exit(EXIT_FAILURE);
        }
    }
    execlp("./v1_child", "./v1_child", NULL); /* Writes to pipe */
    exit(EXIT_SUCCESS);

default: /* Parent go to next child */
    break;
}

switch (fork()) {
case -1:
    perror("cannot fork()");
    exit(EXIT_FAILURE);

case 0: /* Second child: exec 'wc' to read from pipe */
    if (close(pfd_child2_r[0]) == -1) { /* Read end is unused */
        perror("cannot close()");
        exit(EXIT_FAILURE);
    }

    if (close(pfd_child2_w[1]) == -1) { /* Write end is unused */
        perror("cannot close()");
        exit(EXIT_FAILURE);
    }

    /* Duplicate stdout on write end of pipe; close duplicated descriptor */

    if (pfd_child2_w[1] != STDOUT_FILENO) { /* Defensive check */
        if (dup2(pfd_child2_r[1], STDOUT_FILENO) == -1) {
            perror("cannot dup2()");
            exit(EXIT_FAILURE);
        }
        if (close(pfd_child2_r[1]) == -1) {
            perror("cannot close()");
            exit(EXIT_FAILURE);
        }
    }

    /* Duplicate stdin on read end of pipe; close duplicated descriptor */

    if (pfd_child2_w[1] != STDIN_FILENO) { /* Defensive check */
        if (dup2(pfd_child2_w[0], STDIN_FILENO) == -1) {
            perror("cannot dup2()");
            exit(EXIT_FAILURE);
        }
        if (close(pfd_child2_w[0]) == -1) {
            perror("cannot close()");
            exit(EXIT_FAILURE);
        }
    }
    execlp("./v1_child", "./v1_child", NULL); /* Writes to pipe */
    exit(EXIT_SUCCESS);

default: /* Parent falls through */
    break;
}

/* Parent closes unused file descriptors for pipe */

if (close(pfd_child1_r[1]) == -1 || close(pfd_child1_w[0]) == -1
        || close(pfd_child2_r[1]) == -1 || close(pfd_child2_w[0]) == -1) {
    perror("cannot close()");
    exit(EXIT_FAILURE);
}

второй файл - это файл gcd, у меня все еще нет fini sh его, а l oop, который должен продолжать получать числа, отсутствует. но я просто хочу, чтобы первая строка работала правильно, тогда я исправлю остальные.

int gcd(int n1, int n2) {
    if (n2 == 0)
        return n1;
    return gcd(n2, n1 % n2);
}

int main(int argc, char **argv) {

    int n1, n2, result;
    if (scanf("%d %d", &n1,&n2) != 2) {
        fprintf(stderr, "error reading numbers in child\n");
        return -1;
    }
    if (n1 > n2)
        result = gcd(n1, n2);
    else
        result = gcd(n2,n1);
    printf("%d", result);
}

Ответы [ 2 ]

1 голос
/ 14 апреля 2020

Как отлаживать

Простой способ отладки - всегда добавлять операторы fprintf(stderr, "...") в дочернюю программу. Затем вы можете запустить программу и также посмотреть, что делают дочерние процессы.

Передать значения

Поскольку вы перенаправляете stdin и stdout и используете sscanf / printf в v1_child Программа, которая рассчитывает GCD, я предполагаю, что вы хотите передать значения в виде строк.

Самый простой способ - использовать fprintf для записи целых чисел в виде форматированных строк. Вы можете использовать fdopen, чтобы связать поток с существующим дескриптором файла канала.

Соответственно, вы должны преобразовывать числа из и в строки.

Данные переменной длины и буферизованный ввод / вывод

Если для передачи значений используются строки, каждая пара значений имеет переменную длину. Как правило, символ новой строки используется в программе C для обозначения полной входной записи.

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

Используя потоки, вы работаете с буферизованным вводом / выводом, с помощью fflu sh вы можете убедиться, что все буферизованные данные записывается через базовую функцию записи потока.

Функции

Можно разделить функции на несколько функций, чтобы сделать поток немного легче для понимания.

Возможные улучшения

Возможно, это уже начало.

Другим возможным улучшением может быть использование strtol вместо atoi, поскольку atoi не выполняет проверку ошибок. Подобный sscanf не сообщает об ошибках преобразования (например, не числа c символов в конце строки), по крайней мере, мы смотрим на количество назначенных элементов ввода.

Вероятно, есть еще возможности улучшить удобочитаемость кода.

С помощью waitpid в родительском коде можно проверить код состояния выхода ребенка.

Программа

Ваш код, слегка измененный в упомянутых выше точках, может выглядеть следующим образом:

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

void create_pipe(int *);
void close_fd(int);
int child(const int *, const int *);
int read_input_line(FILE *fp, char *line, int max, int *numbers, int line_count);
void write_to_child(FILE *fp, const int *numbers);
int read_from_child(FILE *fp);


int main(int argc, char *argv[]) {

    if (argc != 2 || strcmp(argv[1], "--help") == 0) {
        fprintf(stderr, "‫‪usage: %s <FILE NAME>\n", argv[0]);
        return EXIT_FAILURE;
    }

    int pfd_child1_r[2];
    int pfd_child1_w[2];
    int pfd_child2_r[2];
    int pfd_child2_w[2];

    create_pipe(pfd_child1_r);
    create_pipe(pfd_child1_w);
    create_pipe(pfd_child2_r);
    create_pipe(pfd_child2_w);

    pid_t pid1 = fork();

    if (pid1 == 0) { //child 1
        close_fd(pfd_child2_r[0]);
        close_fd(pfd_child2_r[1]);
        close_fd(pfd_child2_w[0]);
        close_fd(pfd_child2_w[1]);
        return child(pfd_child1_r, pfd_child1_w);
    } else if (pid1 > 0) {
        close_fd(pfd_child1_r[1]);
        close_fd(pfd_child1_w[0]);

        pid_t pid2 = fork();
        if (pid2 == 0) { //child 2
            close_fd(pfd_child1_r[0]);
            close_fd(pfd_child1_w[1]);
            return child(pfd_child2_r, pfd_child2_w);
        } else if (pid2 > 0) {
            close_fd(pfd_child2_r[1]);
            close_fd(pfd_child2_w[0]);

            FILE *fp_child1_w = fdopen(pfd_child1_w[1], "w");
            FILE *fp_child2_w = fdopen(pfd_child2_w[1], "w");
            FILE *fp_child1_r = fdopen(pfd_child1_r[0], "r");
            FILE *fp_child2_r = fdopen(pfd_child2_r[0], "r");

            if (!fp_child1_w || !fp_child2_w || !fp_child1_r || !fp_child2_r) {
                perror("fdopen() failed");
                return EXIT_FAILURE;
            }

            FILE *fp = fopen(argv[1], "r");
            if (fp == NULL) {
                perror("fopen(): ");
                return EXIT_FAILURE;
            }

            char line[100];
            int numbers[2], line_count = 0;
            while (read_input_line(fp, line, sizeof(line), numbers, line_count) == 2) {
                if (line_count % 2 == 0) {
                    write_to_child(fp_child1_w, numbers);
                } else {
                    write_to_child(fp_child2_w, numbers);
                }

                if (line_count % 2 == 0) {
                    int result = read_from_child(fp_child1_r);
                    printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
                } else {
                    int result = read_from_child(fp_child2_r);
                    printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
                }
                line_count++;
            }

            //fclose closes also associated file descriptor
            fclose(fp_child1_w);
            fclose(fp_child2_w);
            fclose(fp_child1_r);
            fclose(fp_child2_r);

            fclose(fp);
            return EXIT_SUCCESS;
        } else {
            perror("second fork failed");
            return EXIT_FAILURE;
        }
    } else {
        perror("first fork failed");
        return EXIT_FAILURE;
    }
}


int read_input_line(FILE *fp, char *line, int max, int *numbers, int line_count) {
    char *token;
    int num, counter = 0;

    line[0] = '\0';
    if (fgets(line, max, fp) != NULL) {
        token = strtok(line, " ");
        while (token != NULL) {
            num = atoi(token);
            if (num < 1 || counter == 2) {
                fprintf(stderr, "‫‪illegal‬‬ ‫‪input‬‬ at line %d\n", line_count + 1);
                exit(EXIT_FAILURE);
            }
            numbers[counter] = num;
            counter++;
            token = strtok(NULL, " ");
        }
    }
    return counter;
}

int read_from_child(FILE *fp) {
    char buf[128];
    int result = -1;
    if (fgets(buf, sizeof(buf), fp)) {
        if (sscanf(buf, "%d", &result) == 1)
            return result;
    }
    return -1;
}

void write_to_child(FILE *fp, const int *numbers) {
    fprintf(fp, "%d %d\n", numbers[0], numbers[1]);
    fflush(fp);
}

int child(const int *pfd_child_r, const int *pfd_child_w) {
    dup2(pfd_child_r[1], STDOUT_FILENO);
    dup2(pfd_child_w[0], STDIN_FILENO);
    close_fd(pfd_child_r[0]);
    close_fd(pfd_child_r[1]);
    close_fd(pfd_child_w[0]);
    close_fd(pfd_child_w[1]);
    execlp("./v1_child", "./v1_child", NULL);
    fprintf(stderr, "execution of v1_child failed\n");
    exit(EXIT_FAILURE);
}

void create_pipe(int *fd) {
    if (pipe(fd) == -1) {
        perror("cannot pipe()");
        exit(EXIT_FAILURE);
    }
}

void close_fd(int fd) {
    if (close(fd) == -1) {
        perror("cannot close()");
        exit(EXIT_FAILURE);
    }
}

Соответствующий v1_child. c может выглядеть так:

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

int gcd(int n1, int n2) {
    if (n2 == 0)
        return n1;
    return gcd(n2, n1 % n2);
}

int main(void) {

    int n1, n2, result;
    char buf[128];
    while(fgets(buf, sizeof(buf), stdin)) {
        if (sscanf(buf, "%d %d", &n1, &n2) != 2) {
            fprintf(stderr, "error reading numbers in child\n");
            return -1;
        }
        if (n1 > n2)
            result = gcd(n1, n2);
        else
            result = gcd(n2, n1);
        printf("%d\n", result);
        fflush(stdout);
    }
    return EXIT_SUCCESS;
}  

Тест

При вводе

5 25
49 14
64 462
1155 84

вывод будет

5 25        gcd: 5
49 14       gcd: 7
64 462      gcd: 2
1155 84     gcd: 21
0 голосов
/ 14 апреля 2020

Вы записываете двоичные значения в каналы, но пытаетесь прочитать текстовое представление. Поэтому либо используйте вариант printf (например, dprintf) в родительском, либо (лучше) используйте readwrite) в дочернем.

...