Таймаут функции write для fifo на C

Рейтинг: 1Ответов: 1Опубликовано: 14.09.2014

Создан fifo-канал, необходимо установить таймаут для записи в него, если с другой стороны его никто не читает. Таймаут на чтение реализовал селектом: select(fd+1, &set, NULL, NULL, &to); при этом с флагом O_RDONLY в функции open почему-то не работает, только с O_RDWD или O_RDONLY | O_NONBLOCK. Пытался аналогичным образом сделать таймаут для записи, но тщетно.

Заранее спасибо.

Ответы

▲ 2Принят

@Kosterio, при работе с FIFO надо понимать, что этот файл задуман как точка рандеву, поэтому при open() обычно процессы блокируются, ожидая один другого.

Для процесса читателя открывать FIFO с O_RDWR действительно разумно (не будет блокировки в ожидании писателей), поскольку формально он достигает рандеву сам с собой. Именно поэтому попытки использовать select/poll для определения писателем наличия читателя безуспешны.

На мой взгляд (если у Вас на самом деле схема читатель-писатели (несколько писателей тоже достаточно разумный подход)), писатель должен открывать FIFO с O_WRONLY. Тогда можно сделать таймаут ожидания читателя в момент open().

Что же касается непосредственно упомянутого в вопросе write() с таймаутом, то это, конечно же, можно сделать аналогичным способом (signal/alarm). Только вот, как мне представляется, смысл тут будет несколько другой, а именно, мы будем отлавливать не отсутствие читателя (если он накрылся, то при вызове write() получим SIGPIPE), а его нежелание читать.

Вот простой пример с ожиданием читателя в течении 1 сек. и отладочной печатью:

// fifow.c (gcc / g++)
#include  <stdio.h>
#include  <stdlib.h>
#include  <string.h>

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

#include  <signal.h>
#include  <errno.h>
#include  <fcntl.h>
#include  <unistd.h>

#define fatal(msg) ({fprintf(stderr,"%ld %s: %m\n",(long)getpid(),msg); exit(-1);})
#define BUFSIZE 4096

static void sigh (int s) { puts("catch alarm"); };

int
openfifow (char *fname)
{
// чуть подправил для красивости
#ifdef __cplusplus
   struct sigaction act = {0}, old;
   act.sa_handler = sigh;
#else
   struct sigaction act = {.sa_handler = sigh}, old;
#endif

  sigaction(SIGALRM, &act, &old);
  int sec = alarm(1);

  int fd = open(fname, O_WRONLY);
  if (fd < 0) {
    if (errno == EINTR)
      perror("openfifow");
    else
      fatal("openfifow");
  } else 
    printf("openfifow: %d\n", fd);

  alarm(sec ? sec - 1 > 0 ? sec - 1 : 1 : 0);
  sigaction(SIGALRM, &old, 0);

  return fd;
}

int
main (int ac, char *av[])
{
  char *fifoname = av[1]? : (char *)"fifotst";
  printf ("fifow %ld started\n",(long)getpid());

  if (mkfifo(fifoname,0666) && errno != EEXIST)
    fatal("mkfifo");

  char buf[BUFSIZE];
  int l, out = -1;

  signal(SIGPIPE, SIG_IGN); // иначе надо обрабатывать сигнал, 
                            // когда читатель отвалил, а мы вызываем write()
  while (puts("enter"), fgets(buf, BUFSIZE, stdin)) {
    if (out < 0) 
      if ((out = openfifow(fifoname)) < 0)
        continue;
    if ((l = write(out, buf, strlen(buf))) <= 0) {
      close(out);
      out = -1;
      printf("write: %d %m\n", l);
    }
  }

}

Если что-то непонятно, спрашивайте.