Определить, когда fifo открывается из программы - PullRequest
3 голосов
/ 28 октября 2011

У меня есть ситуация, когда мне нужно проверить, открыла ли другая сторона fifo, однако я не могу использовать открытие, потому что в противном случае программа начнет делать вещи.

Почему я долженсделать это: у меня есть программа (монитор), которая запускает серверную программу (обе созданы мной).Монитор использует это fifo для связи, потому что монитор можно закрыть / открыть, когда сервер уже запущен.

Проблема заключается в том, что монитор запускает сервер: в этом случае мне нужно каким-то образом подождать fifoчтобы быть созданным, а затем открыть их.На самом деле я использую некоторое время на мониторе, который проверяет, когда создаются fifo, однако таким образом он открывает fifo до , и сервер мог это сделать (даже если инструкция после mkfifo фактически является открытой!!!).

Вы можете сказать, что я открываю fifo в неправильном порядке на мониторе (я открываю запись fifo (НЕПРАВИЛЬНО) перед чтением fifo), проблема в том, что я могуне отменять этот порядок, поскольку требуется, чтобы сервер ожидал клиентов на открытой (RDONLY) fifo.

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

Спасибо всем

Редактировать 1:

Вот как все работает в данный момент

Сервер

mkfifo(fifo1)
mkfifo(fifo2)
open(fifo1 O_RDONLY)
open(fifo2 O_WRONLY)

Монитор

while (fifo1 doesn't exists && fifo2 doesn't exists);
open(fifo1 O_WRONLY)
open(fifo2 O_RDONLY)

Я думаю, что условие гонки теперь достаточно явное, важно заметить, что блокируются fifo (блокируются только RDONLY, блоки WRONLY не будут блокироваться, даже если на другой стороне нет никого => это поведение Unix, не спроектированное мной ).

Edit 2:

Состояние гонки происходит на первом уровне открытия FIFO. Я должен открыть первый fifo на сервере, прежде чем монитор сделает это.

Ответы [ 4 ]

1 голос
/ 31 октября 2011

Если вы выполняете open () в правильном порядке, условия гонки отсутствуют. (единственно возможная гонка - это третий процесс, мешающий тем же самым фифо) Из прекрасного руководства:

"Тем не менее, он должен быть открыт с обоих концов одновременно, прежде чем вы сможете приступить к выполнению каких-либо операций ввода или вывода на нем. Открытие FIFO для обычного чтения блоков, пока какой-либо другой процесс не откроет тот же FIFO для письма и наоборот. "

Это означает, что заказ

{процесс 1: открытый (fifo1, RO); процесс2: открытый (fifo1, WO); }

...

{процесс 1: открытый (fifo2, WO); процесс2: открытый (fifo2, RO); }

всегда будет успешным (без остановки процесса). Порядок операций на каждой пятерке не важен; для fifo1 первым может идти процесс1 или процесс2 (и он будет заблокирован, пока другая сторона не добьется успеха).

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#define FIFO1 "fifo1"
#define FIFO2 "fifo2"

void do_master(void);
void do_slave(void);
void randsleep(unsigned max);

/************************************/
void do_master(void)
{
int in,out, rc;
char buff[20];

randsleep(5);
mkfifo(FIFO1, 0644);
randsleep(7);
mkfifo(FIFO2, 0644);

out = open(FIFO2, O_WRONLY);
if (out == -1) {
    fprintf(stderr, "[Master]: failed opening output\n" );
    return;
    }
fprintf(stderr, "[Master]: opened output\n" );
in = open(FIFO1, O_RDONLY);
if (in == -1)  {
    fprintf(stderr, "[Master]: failed opening input\n" );
    close(out);
    return;
    }
fprintf(stderr, "[Master]: opened input\n" );

rc = write( out, "M2S\n\0" , 5);
fprintf(stderr, "[Master]: wrote %d\n", rc );

rc = read( in, buff , sizeof buff);
fprintf(stderr, "[Master]: read %d: %s\n", rc, buff );
unlink(FIFO1);
unlink(FIFO2);
}
/***********************************/
void do_slave(void)
{
int in,out, rc;
unsigned iter=0;
char buff[20];

loop1:
in = open(FIFO2, O_RDONLY);
if (in == -1) {
    fprintf(stderr, "[Slave%u]: failed opening input\n", ++iter );
    randsleep(2);
    goto loop1;
    }
fprintf(stderr, "[Slave]: opened input\n" );

loop2:
out = open(FIFO1, O_WRONLY);
if (out == -1) {
    fprintf(stderr, "[Slave%u]: failed opening output\n", ++iter );
    randsleep(3);
    goto loop2;
    }
fprintf(stderr, "[Slave]: opened output\n" );

rc = write( out, "S2M\n\0" , 5);
fprintf(stderr, "[Slave]: wrote %d\n", rc );

rc = read( in, buff , sizeof buff);
fprintf(stderr, "[Slave]: read %d:%s\n", rc, buff );
}
/*************************************/
void randsleep(unsigned max)
{
unsigned val;
val = rand();
val %= max;
sleep(val);
return;
}
/*************************************/
int main(void)
{
int rc;

switch (rc=fork()) {
    case -1: exit(1); break;
    case 0: do_slave(); break;
    default: do_master(); break;
    }
exit (0);
}
1 голос
/ 28 октября 2011

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

Обязательно используйте флаги O_CREAT и O_EXCL при первоначальном вызове sem_open(), чтобы создание семафора было атомарным. Например, и монитор, и серверная программа будут пытаться создать семафор при запуске, если он еще не существует ... если он существует, вызов завершится неудачно, то есть монитор или сервер, но не обе программы получил право создать семафор и инициализировать его. Затем монитор ожидает семафор, пока сервер инициализирует данные fifo ... после инициализации fifo сервер освобождает семафор, и монитор затем может продолжить работу.

Вот процесс, который я себе представляю ... Я считаю, что он должен эффективно решить вашу расу:

В мониторе:

  1. Создайте именованный семафор и установите его в заблокированное состояние
  2. Запустить сервер
  3. Подождите, пока FIFO будут видны на уровне файловой системы
  4. Открыть fifo1 для записи (неблокирующая)
  5. Открыть fifo2 для чтения (блокируется до тех пор, пока сервер не откроет fifo2 для записи)
  6. Подождите на семафоре (возможно, с тайм-аутом), пока сервер не разблокирует его, указывая, что он теперь успешно открыл оба FIFO.

На сервере:

  1. Создание FIFO
  2. Открыть fifo1 (блокирует, пока монитор не открывает его для записи)
  3. Открыто fifo2 (неблокирующее)
  4. Разблокируйте семафор теперь, когда сервер открыл оба FIFO

Таким образом, ваш монитор не может продолжать работу до тех пор, пока не появится «известное состояние», в котором все правильно инициализировано ... сервер указывает это состояние монитору через указанный семафор.

0 голосов
/ 30 октября 2011

По крайней мере, то, что я нашел до сих пор, заставляет меня понять, что нет способа определить, открыта ли fifo , кроме случаев, когда вы тоже открываете ее.

Edit 1:

Как сказал Джейсон, есть два способа (однако оба не разрешены в моей домашней работе):

1) * Вы можете выполнить поиск через / proc / PID / fd (замените PID числовым идентификатором процесса), чтобы увидеть, какие процессы клиента уже открыли ваш FIFO * 2) Другой вариант - вызвать fuser на вашем FIFO

Однако первый требует чего-то, чего учителя не хотят: смотреть внутри proc / PID / fd.Второй, который я услышал, требует прав суперпользователя, и я опять не могу этого сделать.Надеюсь, это поможет кому-то еще в будущем.

0 голосов
/ 29 октября 2011

Рассмотрите возможность использования доменных сокетов Unix типа SOCK_STREAM.Сервер будет bind свой сокет с именем в файловой системе.Каждый клиент получает собственное двунаправленное соединение с сервером.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...