программы, использующие pipe(), вызывают EIO

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

Это мое решение этого домашнего задания. Этот же файл воспроизводится ниже.

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

#define SHELL   "/bin/sh"

extern int  main(int, char*[]);
static int  readingend(int, int, const char*);
static int  writingend(int, int, const char*);
static int  usr1handler();

extern int
main(int argc, char *argv[])
{
    int pipefd[2], status;
    pid_t rpid, wpid;

    if (argc != 3) {
        fprintf(stderr, "Invalid argument count\n");
        return (EXIT_FAILURE);
    }

    if (pipe(pipefd)) {
        perror("Cannot make a pipe");
        return (EXIT_FAILURE);
    }

    signal(SIGUSR1, (void(*)(int))usr1handler);

    if ((rpid = fork()) < 0) {
        perror("Cannot fork");
        return (EXIT_FAILURE);
    }

    if (rpid == 0)
        return (readingend(pipefd[0], pipefd[1], argv[2]));


    if ((wpid = fork()) < 0) {
        perror("Cannot fork");
        kill(rpid, SIGKILL);
        return (EXIT_FAILURE);
    }

    if (wpid == 0)
        return (writingend(pipefd[0], pipefd[1], argv[1]));

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

    waitpid(rpid, &status, 0);
    return (status);
}

static int
readingend(int rfd, int wfd, const char *cmd)
{
    close(wfd);
    dup2(rfd, STDIN_FILENO);

    /* execl() only returns on error */
    execl(SHELL, SHELL, "-c", cmd, NULL);
    fprintf(stderr, "Cannot execute %s: ", cmd);
    perror(NULL);

    /* clean up */
    kill(getppid(), SIGUSR1);
    return (EXIT_FAILURE);
}

static int
writingend(int rfd, int wfd, const char *cmd)
{
    close(rfd);
    dup2(wfd, STDOUT_FILENO);

    execl(SHELL, SHELL, "-c", cmd, NULL);
    fprintf(stderr, "Cannot execute %s: ", cmd);
    perror(NULL);

    /* clean up */
    kill(getppid(), SIGUSR1);
    return (EXIT_FAILURE);
}

static int
usr1handler()
{
    /* child already printed diagnostic */
    exit(EXIT_FAILURE);
}

Проблема в том, что при некоторых вызовах первый процесс получает EIO при чтении со стандартного ввода. Кажется, это происходит только на некоторых оболочках: это происходит с ksh93 и bash, но не с bosh. Такая ошибка выглядит следующим образом:

$ LANG=C ./mypipe cat true
$ cat: -: Input/output error

Может ли кто-нибудь сказать мне, почему возникает эта ошибка? Что я могу сделать, чтобы эта ошибка больше не появлялась? Ясно, что должен быть способ, поскольку выполнение cat | true в обычной оболочке дает желаемый результат без каких-либо ложных EIO.


person fuz    schedule 01.06.2014    source источник


Ответы (1)


Эта проблема возникает из-за того, что true мгновенно завершается, в результате чего mypipe также завершается, оставляя cat осиротевшим. Если это произойдет, read() может выйти из строя:

ЭИО

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

Эту проблему можно предотвратить, подождав оба процесса следующим образом:

extern int
main(int argc, char *argv[])
{
    int status, estatus;

    /* ... */


    /* wait for both processes to terminate */
    if (wait(&status) == rpid) {
        estatus = status;
        wait(&status);
    } else {
        wait(&status);
        estatus = status;
    }

    return (estatus);
}
person fuz    schedule 01.06.2014