2015-09-26 4 views
1

Я пишу программу, состоящую из 2 процессов. Родитель читает данные через stdin, а затем отправляет их дочернему устройству через канал. Я использую функции fgets() и fputs() для записи и чтения данных.Сложность с использованием fgets при чтении строк по трубе

Проблема в том, что в определенных ситуациях вызов функции fgets() дочернего процесса блокируется. Это:

  1. Когда текстовый файл отправляется на stdin, все работает нормально до самой последней строки файла. Fgets ребенка никогда не выполняется и процесс зависает.
  2. Когда я отправляю/dev/urandom к программе, такая же ситуация происходит в случайный момент.

Я подозреваю, что проблема имеет какое-то отношение к EOF или/0 в конце этих строк, но я не могу точно определить проблему. Любая помощь будет принята с благодарностью.

Я использую очереди сообщений как средство синхронизации. Я также знаю, что в этом коде нет очистки. Вот оно:

#define KEY5 7626 

struct pidbuf 
{ 
    long mtype; 
    int mtext; 
} msgpid; 

int main(){ 
    sleep(5); 
    int mqpid; 
    FILE * strumien; 
    int fd[2]; 
    if (pipe(fd)== -1){ 
     perror("Create pipe error"); 
     exit(1);} 

    int buf_len = 128; 

    msgpid.mtext=0; 
    int size1=sizeof(struct pidbuf)-sizeof(long); 

    if((mqpid = msgget(KEY5, 0666 | IPC_CREAT))==-1){ 
     return(-1); 
    } 
    char data[buf_len]; 

    if(fork()!=0){ 
     close(fd[0]); 
     strumien=fdopen(fd[1],"w"); 
     while (fgets(data,buf_len,stdin) != NULL) { 
      printf("1:\n%s\n",data); 
      fputs(data,strumien); 
      fflush(strumien); 
      msgpid.mtype=2; 
      if(msgsnd(mqpid,&msgpid,size1,0)==-1){perror("msgsnd failed: 1");} 

      msgrcv(mqpid,&msgpid,size1, 1, 0); 
     } 
     if(feof(stdin)!=0) printf("\nEnd of file\n"); 
    } 
    else{ 
     close(fd[1]); 
     strumien=fdopen(fd[0],"r"); 
     msgrcv(mqpid,&msgpid,size1, 2, 0); 
     while(fgets(data,buf_len,strumien)!=NULL){ 
      printf("2:\n%s\n\n",data); 

      msgpid.mtype=1; 
      if(msgsnd(mqpid,&msgpid,size1,0)==-1){perror("msgsnd failed: 1");} 
      msgrcv(mqpid,&msgpid,size1, 2, 0); 
     } 
    } 
    return 0; 
} 
+0

Здесь он * снова *: 'if (feof (stdin)! = 0) printf (" \ nEnd файла \ n ");' http://stackoverflow.com/questions/5431941/why-is- while-feof-file-always-wrong 'feof()' does * not * означает end-of -file. В нем говорится, что вы пытались прочитать за пределами файла. Хотя в этом случае это кажется доброкачественным, поскольку вы использовали возвращаемое значение из 'fgets()', чтобы закончить чтение. –

+1

@WeatherVane: Это фактически (редкое) правильное использование 'feof' - оно используется после того, как другая функция stdio уже вернула результат EOF-или-ошибки, чтобы определить, действительно ли это EOF или какая-либо другая ошибка. –

+0

@ChrisDodd ты уверен? После достижения конца файла с помощью 'fgets() == NULL'' feof' будет недействительным, потому что код не читается за пределами конца файла.Почему это отличается от любого другого неправильного использования? –

ответ

1

Не слишком удивительно, - родительский процесс посылает одно сообщение типа 2 для каждой строки файла для чтения, и ждет сообщения типа 1 после записи каждой строки. С другой стороны, ребенок отличается - в то время как он отправляет тип 1 и ждет тип 2 после каждой строки, он также ожидает тип 2 перед прочтением самой первой строки.

Таким образом, родитель будет отправлять сообщения N типа 2, пока ребенок будет ждать сообщений типа N + 1 типа 2 для файла с N строкой. Это означает, что после того, как родитель закончит и выйдет, ребенок будет заблокирован, ожидая последнего последнего сообщения, которое никогда не появится.

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

Для /dev/urandom (или любого двоичного входа) у вас есть дополнительная проблема, когда вход содержит байт NUL. Затем fgets в родительском слове с радостью прочитает NUL и продолжит его до новой строки (или будет заполнен буфер), но следующее fputs будет записывать только (и не включая) NUL. Таким образом, некоторые данные будут потеряны, и ребенок продолжит чтение в поисках новой строки, оставив ребенка заблокированным в fgets, а родитель заблокирован в msgrcv перед отправкой следующей строки.

Нет проблем с этой проблемой, поскольку fgets/fputs предназначены для работы с текстом, а не с двоичными данными. Вместо этого вы можете использовать fread или getline и fwrite, что даст вам явные длины для количества прочитанных/записанных данных, поэтому вы можете знать, когда он прочитал NUL, который не является окончанием этих данных.

+0

Спасибо за помощь. Я решил изменить функции, используемые для fwrite/fread. Я также сделал цикл для замените нули внутри строки, считанной с stdin, на '/ 0'. Кажется лучшим из обоих слов. Я получаю текст по строкам, и он работает с/dev/urandom. – tomullus

Смежные вопросы