2015-10-30 3 views
1

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

Как известно, когда труба открыта для чтения, она блокирует процесс, пока другой процесс не откроет трубу для записи. (. Если флаг NONBLOCK не установлен) Когда другой процесс открывает трубу и записывает на него, мы можем получить вход, как это:

... 
opened_pipe = fopen(argv[1], "r") 
while(1) 
    { 
    if (fgets(readbuf, FIFO_READLEN, opened_pipe) != NULL) 
     { \\ process the input from the writer } 
    else 
     { 
     \\ this is the branch when the writer closed his end of the pipe and reader gets EOF 
     \\ usually one exits here 
     \\ but I would like to freeze the process and wait until another writer comes 
     \\ (like a server-like application would do) 
     } 
    } 

Но когда писатель выходит этот while переходит в бессмысленную петлю. Было бы лучше, если бы читатель вернулся в исходное состояние - процесс блокируется до тех пор, пока труба не будет снова подключена на другом конце. Можно ли это сделать?

PS

Я попытался создать фиктивный писателя в моей программе, которая открывает ту же трубу, как w и держит его открытым в петле в fgets все время. Но это не сработало для меня. Может, я ошибся. Можно ли вытащить этот трюк?

Также можно было постоянно закрывать и снова открывать трубу внутри while. Но я хочу использовать либо pipe, либо stdin как входной поток. Было бы лучше относиться к ним одинаково в программе. Итак, можно ли снова открыть поток stdin через fopen с некоторым "stdin" имени файла?

+1

Если вы получаете в петлю, когда выходит писатель, это означает, что вы не правильно тестирование для EOF и выхода из цикла. – Barmar

+0

@Barmar yep, я не выхожу из цикла на EOF, так как я хочу подождать, пока придет другой писатель. – xealits

+2

Рассмотрите возможность использования сокета 'AF_LOCAL' вместо именованного канала. API сложнее, но вы получаете целую кучу преимуществ, в том числе: ваша текущая проблема Just Goes Away; вы можете с уверенностью говорить с несколькими клиентами одновременно; и как только вы это сделаете, прием соединений по сети - это еще один шаг. – zwol

ответ

1

Просто откройте свой FIFO в серверном процессе дважды - сначала для чтения, затем для записи. Выполнение этого (открытие его для записи) гарантирует, что ваш процесс не увидит EOF, если все клиенты откажутся от FIFO.

Вот короткая демонстрация:

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

#include <sys/types.h> 
#include <sys/stat.h> 

#define FIFO_NAME "/tmp/fifo" 

int main() 
{ 
    if (mkfifo(FIFO_NAME, S_IRUSR | S_IWUSR | S_IWGRP) == -1) 
    { 
     perror("mkfifo"); 
     exit(EXIT_FAILURE); 
    } 

    FILE *readFd = fopen(FIFO_NAME, "r"); 
    FILE *writeFd = fopen(FIFO_NAME, "w"); 

    char c; 
    for (;;) 
    { 
     c = fgetc(readFd); 
     fprintf(stdout, "Read char: %c\n", c); 
    } 
} 
+1

Я пробовал эту идею (что действительно потрясающе). Но я, вероятно, допустил ошибку, не мог скомпилировать и не преследовал ее. Это действительно возможно? – xealits

+1

Я добавил фрагмент кода к моему ответу. –

+0

Да, это сработало! Как-то ранее у меня было предупреждение о некоторых проблемах с конверсией во второй версии 'fopen' и' SIGSEGV' во время выполнения. Теперь все гладко. – xealits

1

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

Нечто подобное (прилипание с псевдо-фрагмент):

while (1) 
{ 
    opened_pipe = fopen(argv[1], "r") 
    while(1) 
    { 
     if (fgets(readbuf, FIFO_READLEN, opened_pipe) == NULL) {...} 
     else if (!readbuf[0]) 
     { 
      fclose(opened_pipe); 
      break; 
     } 
    } 
} 

редактировать: дал свой комментарий здесь, я получаю впечатление, вы можете захотеть использовать Unix сокет вместо FIFO. Таким образом, вы могли бы подключать accept() и обрабатывать их отдельно, все еще ожидая новых соединений.

+0

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

+0

@xealits Ожидание очередного писателя - это именно то, что он делает. – zwol

+0

@xealits well Я отредактировал свой ответ с альтернативой, которая может быть лучше подходит для того, чего вы хотите достичь (если я не ошибаюсь здесь) –