Трубы и вилки - PullRequest
       8

Трубы и вилки

4 голосов
/ 05 мая 2011

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

Учитывая два исполняемых файла, lc1 и lc2, я хочу, чтобы lc2 установил канал к дескриптору файла stdoutlc1, так что когда вызывается execlp("lc1", argv[1], NULL), вывод будет прочитан как
while ((c= read(pipefd[0], readin, SIZE)) > 0)

Согласно моей книге по Unix, я должен использовать метод open, dup2, close дляперенаправление stdout в stdin, и вот мой код:

int pid, c, i;
char *readin= (char *)malloc(sizeof(SIZE));

if (pipe(pipefd)== -1)
  perror("Can't open a pipe\n");

for (i=1; i< argc; i++){
if ((pid= fork())==-1)
        perror("Can't fork\n");

  run(argv[i]);

}

//close pipe
close(1);
if (dup2(pipefd[0], 0)==-1)
  perror("Can't redirect stdin");
close(pipefd[1]);

for (i=1; i< argc; i++){
    if ((wait(NULL))== -1)
        perror("Wait error");

    while ((c= read(pipefd[0], readin, SIZE)) > 0){;
        //print buf count
        total += atoi(readin);
    }
}

Функция запуска

void run(char *f){
  int fp;
  if ((fp= open(f, O_RDONLY)) == -1)
      perror("Can't open the file");

  close(pipefd[0]);
  dup2(pipefd[1], 1);
  close(pipefd[1]);
  execlp("ls1", f, NULL);
} 

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

Ответы [ 2 ]

2 голосов
/ 05 мая 2011

run (argv [i]) выполняется как родительским, так и дочерним, потому что не назначают функциональность на основе возвращенного PID, поэтому одно закрытие за другим может закрыться.Посмотрите код ниже, может ли он пригодиться, я буду использовать пример кода для подобных ситуаций.:

int main()
{
    int pipe_fd[2] = {0};
    int pid = -1;
    int status = -1;
    int ret_value = INVALID_CMD;
    int cmd_output_len = -1;
    status = pipe(pipe_fd);
    if(status<0)
    {
        perror("pipe create err");
    }
    else
    {
        pid = fork();
        if(pid<0)
        {
        }
        else if (pid == 0)
        {
            /*Child functionality*/
            child_func(pipe_fd, cmd);
        }
        else
        {
            /*Parent functionality*/
            cmd_output_len = parent_fun(pid, pipe_fd);
        }
    }
    return ret_value;
}

int child_func(int pipe_fd[], const char * cmd)
{
    int status = 5;
    int read_fd = pipe_fd[0];       /*read file descriptor*/
    int write_fd = pipe_fd[1];      /*write file descriptor*/

    int exit_status = 0;

    /*close read fd*/
    close(read_fd);

    /*dup2 stdout to write fd*/
    //status = dup2(1, write_fd);
    status = dup2(write_fd, 1);
    if(status<0)
    {
        exit(-1);
    }
    else
    {
        system(cmd);
        exit(0);
    }
}


int parent_fun(int child_id, int pipe_fd[])
{
    int status = -1;
    int len = 0;
    bool_e break_loop = FALSE;
    int read_fd = pipe_fd[0];       /*read file descriptor*/
    int write_fd = pipe_fd[1];      /*write file descriptor*/

    /*close write fd*/
    close(write_fd);

    while(1)
    {
        sleep(1);
        status = waitpid(child_id, &status, WNOHANG);
        switch(status)
        {
            case 0:
                    /*Child is still active*/
                    printf("No process waiting to exit..\n");
                    len = do_ur_fun(read_fd);
                    write(1, output, len);
                break;
            /*case EINTR:
            case ECHILD:
            case EINVAL:
                    perror("waitpid error");
                    break_loop = TRUE;
                break;*/
            default:
                if(status<0)
                {
                    perror("waitpid error");
                    break_loop = TRUE;
                    len = -1;
                }
                else if(child_id == status)
                {
                    /*Valid staus from child*/
                    len = read_output(read_fd, output);
                    //write(1, output, len);
                    break_loop = TRUE;
                }
                else
                {
                }
                break;
        }
        if(TRUE == break_loop)
        {
            break;
        }
    }
    return len;
}




int do_ur_fun (int read_fd)
{
        /*Do your exec*/
}
1 голос
/ 06 мая 2011

MaheshGupta024 выявили очень важную проблему в вашем коде; Я полагаю, вы это исправите.

Одна из других проблемных областей:

close(1);
if (dup2(pipefd[0], 0)==-1)
    perror("Can't redirect stdin");
close(pipefd[1]);

for (i=1; i< argc; i++){
    if ((wait(NULL))== -1)
        perror("Wait error");

    while ((c= read(pipefd[0], readin, SIZE)) > 0){;
        //print buf count
        total += atoi(readin);
    }
}

Первое закрытие закрывает стандартный вывод процесса; это редко хорошая идея. Следующая строка дублирует конец чтения канала для стандартного ввода - это нормально. Как отмечено в комментарии выше, perror() не завершается. Затем вы закрываете конец записи канала - это правильно; но вы, вероятно, должны также закрыть конец чтения канала, так как вы установили его для выхода из канала.

Ваш цикл начинается нормально; у вас есть лишние скобки в строке wait(). Вы читаете из pipefd[0] вместо стандартного ввода - так что, возможно, вы не хотели закрывать pipefd [0], но вам также не нужно было дублировать его на стандартный ввод. Затем у вас есть вложенный цикл, который читает по каналу, в то время как у дочернего элемента есть больше данных для чтения - вам совершенно не нужен код wait() с его циклом, поскольку внутреннее while не завершится, пока все дочерние элементы не будут мертвы , С другой стороны, в этом нет большого вреда - после того, как умирает первый дочерний элемент, вы читаете данные всех остальных дочерних элементов, затем переходите во внешний цикл и ждете друг друга, причем внутренний цикл немедленно завершается, так как нет данных для чтения.

Итак:

  • Не закрывать стандартный вывод.
  • Не дублируйте канал, прочитанный на стандартный ввод.
  • Решите, хотите ли вы очистить цикл - он будет работать, но может быть чище.

Функция run():

void run(char *f){
  int fp;
  if ((fp= open(f, O_RDONLY)) == -1)
      perror("Can't open the file");

  close(pipefd[0]);
  dup2(pipefd[1], 1);
  close(pipefd[1]);
  execlp("ls1", f, NULL);
}

Аргумент должен быть const char *f (или использовать name или file вместо f). Я бы также передал массив pipefd в функцию вместо использования глобальной переменной , Не вызывайте файловый дескриптор fp; это имя условно обозначает переменную типа FILE *, а не int. Однако вам не нужно открывать файл в первую очередь - если только вы не хотите, чтобы вызывающая программа создавала отчеты об ошибках вместо вызываемой программы. Однако, если вы хотите, чтобы вызывающая программа создавала отчеты об ошибках, вы должны закрыть файловый дескриптор, прежде чем продолжить. (Я уже прокомментировал возвращение perror()).

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

Хорошие моменты: вы закрыли оба дескриптора файла канала.

Таким образом:

void run(const char *file, int *pipefd)
{
    close(pipefd[0]);
    dup2(pipefd[1], 1);
    close(pipefd[1]);
    execlp("ls1", f, NULL);
    perror("Failed to exec ls1");
    exit(EXIT_FAILURE);
}
...