2010-07-24 3 views
7

Это, по-видимому, довольно распространенная вещь, и мне удалось научить себя всему, что мне нужно, чтобы заставить ее работать, за исключением того, что у меня теперь есть одна проблема, что бросает вызов моим устранению неполадок.C fork/exec с неблокирующей трубой IO

int nonBlockingPOpen(char *const argv[]){ 
    int inpipe; 
    pid_t pid; 
    /* open both ends of pipe nonblockingly */ 
    pid = fork(); 

    switch(pid){ 
     case 0:   /*child*/ 
      sleep(1); /*child should open after parent has open for reading*/ 

      /*redirect stdout to opened pipe*/ 
      int outpipe = open("./fifo", O_WRONLY); 
      /*SHOULD BLOCK UNTIL MAIN PROCESS OPENS FOR WRITING*/ 
      dup2(outpipe, 1); 
      fcntl(1, F_SETFL, fcntl(1, F_GETFL) | O_NONBLOCK); 

      printf("HELLO WORLD I AM A CHILD PROCESS\n"); 
      /*This seems to be written to the pipe immediately, blocking or not.*/ 
      execvp(*argv, argv); 
      /*All output from this program, which outputs "one" sleeps for 1 second 
      *outputs "two" sleeps for a second, etc, is captured only after the 
      *exec'd program exits! 
      */ 
      break; 

     default:  /*parent*/ 
      inpipe = open("./fifo", O_RDONLY | O_NONBLOCK); 
      sleep(2); 
      /*no need to do anything special here*/ 
      break; 
    } 

    return inpipe; 
} 

Почему бы дочерний процесс не записывал свою станду в трубу каждый раз, когда генерируется линия? Есть ли что-то, что мне не хватает в том, как работают execvp или dup2? Я знаю, что мой подход ко всему этому немного странный, но я не могу найти другой способ программно отображать выходные данные двоичных файлов с закрытым исходным кодом.

+0

Я только что нашел http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html эту страницу, которая указывает что я не должен пытаться передать дочернему процессу неблокирующий дескриптор, но ни одна из причин, перечисленных здесь *, не кажется * применимой к тому, что я делаю. Я действительно понимаю, что программа, которую я запускаю, вероятно, потерпит крах, если процесс прослушивания должен был отключиться от канала (генерируя EAGAIN в дочернем элементе), но это меня не беспокоит. – conartist6

ответ

3

Я думаю, вы получите только выход программы exec'd после ее выхода, потому что после каждого сообщения не flush. Если это так, вы ничего не можете сделать извне.

Я не совсем уверен, как это должно относиться к выбору между блокировкой и неблокированием ввода-вывода в вашем вопросе. Неблокирующая запись может завершиться неудачно полностью или частично: вместо блокировки программы до тех пор, пока в трубе не будет доступна комната, вызов немедленно возвращается и говорит, что он не смог написать все, что должен был иметь. Неблокирующий ввод-вывод не увеличивает объем буфера и не выдает поток вывода, и некоторые из них могут быть плохо поддерживаемы.

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

EDIT: Ну, если программа exec'd использует только буферизацию, предоставляемую libc (не реализует ее собственную), и динамически связана, вы можете заставить ее скрыться, связав ее с измененным libc, который сбрасывает каждую запись , Это было бы отчаянной мерой. попробовать, только если все остальное не получилось.

+0

Я принимаю ваше решение и дополняю его своим. По-видимому, я не получил образование по количеству и типу буферов, которые работают в этой ситуации. http://www.pixelbeat.org/programming/stdio_buffering/ отлично меня наполнил. Вы были правы в утверждении, что причиной сбоя является то, что линейный буффер не размывается до завершения программы, однако на связанной странице подробно описаны многие возможные варианты изменения формы буферизации, используемой stdio.Метод, который я использую сейчас, - это просто вызов unbuffer перед моей командой. Я не уверен, насколько это эффективно, но это работает! – conartist6

1

Почему процесс дочернего процесса не записывает его stdout в трубу каждый раз, когда создается строка?

Откуда вы знаете это? Вы даже не пытаетесь прочитать результат из fifo.

N.B. по имени файла я предполагаю, что вы используете fifo. Или это простой файл?

И незначительная ошибка у ребенка: после dup2() вам необходимо close(outpipe).

fcntl (1, F_SETFL, fcntl (1, F_GETFL) | O_NONBLOCK);

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

IIRC fifos имеет тот же размер буфера, что и трубы. В POSIX минимум - 512 байт, обычно 4K или 8K.

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

printf ("HELLO WORLD I AM THE CHILD PROCESS \ n");

stdout буферизуется, после этого я бы получил fflush(stdout). (Не удается найти документацию, независимо от того, был ли exec() сам по себе отключен stdout или нет.)

Есть ли что-то, что мне не хватает в работе execvp или dup2? Я знаю, что мой подход ко всему этому немного странный, но я не могу найти другой способ программно отображать выходные данные двоичных файлов с закрытым исходным кодом.

Я бы не играл с неблокирующимся IO - и оставил его как есть в режиме блокировки.

И я бы использовал pipe() вместо fifo. У Linux man pipe есть удобный пример с fork().

В противном случае это довольно обычная практика.

+0

Извините - здесь я не включил всю программу, просто функцию, которая открывает канал для чтения. Файл действительно называется fifo. Проблема заключалась не в том, что в этом коде, а в том, что блок exec запускает код (сервер), который будет работать неопределенно, производя только случайный вывод (недостаточно для заполнения буфера, если все идет хорошо). Кроме того, вы правы, кажется, что после * all * этого вполне нормально, если дочерний IO блокирует, и, если на то пошло, возможно, даже и у родителя. Благодарю. – conartist6

+0

@ conartist6: неблокирующий флаг IO имеет совершенно другой эффект для файловых дескрипторов. но в равной степени программно-логическая деструктивная. или вообще никакого эффекта. в зависимости от программы. http://linux.die.net/man/2/fcntl - и ищите O_NONBLOCK. просто удалите его. – Dummy00001

3

Когда процесс запускается (через execvp() в вашем примере), поведение стандартного вывода зависит от того, является ли устройство вывода терминалом или нет. Если это не так (и FIFO не является терминалом), то вывод будет полностью буферизованным, а не буферизированным по строке. Вы ничего не можете с этим поделать; (стандартная) библиотека C делает это.

Если вы действительно хотите, чтобы это работало с буферизацией, вам необходимо предоставить программу с псевдотерминалом в качестве стандартного вывода. Это попадает в интересные области - псевдо-терминалы или ptys не так просто обрабатывать. Для функций POSIX см:

  • grantpt() - предоставить доступ к подчиненной псевдо-терминал устройства
  • posix_openpt() - открыть псевдо-терминал устройство
  • ptsname() - получить имя подчиненного псевдо-терминала устройства
  • unlockpt() - разблокировать псевдо-терминал ведущий/ведомый пару
0

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

int nonBlockingPOpen(char *const argv[]) 
{ 
    int childpipe[2]; 
    pid_t pid; 

    pipe(childpipe); 
    pid = fork(); 

    if (pid == 0) 
    { 
     /*child*/ 

     /*redirect stdout to opened pipe*/ 
     dup2(childpipe[1], 1); 

     /* close leftover pipe file descriptors */ 
     close(childpipe[0]); 
     close(childpipe[1]); 

     execvp(*argv, argv); 

     /* Only reached if execvp fails */ 
     perror("execvp"); 
     exit(1); 
    } 

    /*parent*/ 

    /* Close leftover pipe file descriptor */ 
    close(childpipe[1]); 

    /* Check for fork() failing */ 
    if (pid < 0) 
    { 
     close(childpipe[0]); 
     return -1; 
    } 

    /* Set file descriptor non-blocking */ 
    fcntl(childpipe[0], F_SETFL, fcntl(childpipe[0], F_GETFL) | O_NONBLOCK); 

    return childpipe[0]; 
} 
Смежные вопросы