Правильно уничтожая именованные семафоры System V - PullRequest
0 голосов
/ 07 августа 2009

Я использую именованные семафоры System V для блокировки файла во всех моих приложениях в OSX и Linux. Не самый красивый API по любому определению.

Кажется, это работает, но я не могу понять, как правильно уничтожить семафор после того, как с ним все покончили.

Общая логика такая:

Создание:

[1] Поток или процесс пытается открыть набор семафоров с ключом_t, созданным для файла функцией ftok (). Набор содержит 2 семафора. [2] Если набор семафоров не существует, он создается с 666 разрешениями. [3] «Блокировка» (один из семафоров) установлена ​​в освобожденное состояние (значение 1). [4] «Счетчик ссылок» (другой семафор в том же наборе) увеличивается.

Блокировка / разблокировка:

Чтобы заблокировать [5], поток уменьшает значение семафора "Блокировка" на 1 (с отменой), ожидая, если оно уже равно нулю. Чтобы разблокировать [6], поток увеличивает его на единицу, что позволяет кому-то другому заблокировать его.

Уничтожая:

[7] Семафор «Счетчик ссылок» пытается уменьшить (с флагом IPC_NOWAIT). [8] Проверяется, что его значение равно 0, и если оно равно [9], набор семафоров уничтожается.

(Существует также слой логики, основанный на локальном хранилище потоков, чтобы сделать рекурсивную блокировку внутри одного потока.)

Вопросы:

  • Как мне синхронизировать шаги [1] и [2]? (если набор семафоров не существует, но пока мы подсчитывали звезды, он был создан кем-то другим, поэтому теперь создание тоже не удастся)
  • Как мне синхронизировать шаги [4] с [8], чтобы [9] не убил меня преждевременно?
  • Существуют ли другие условия гонки?

PS: хотя у семафоров POSIX API намного лучше, я не думаю, что смогу пережить поведение sem_inlink (), как описано здесь:

Вызывает sem_open () для повторного создания или повторно подключиться к семафору обратитесь к новый семафор после sem_unlink () называется.

Так что у меня не будет возможности освободить их ...

Ответы [ 2 ]

1 голос
/ 07 августа 2009

Несколько подходов:

Во-первых, если ваша цель - заблокировать файл, то использует вызов блокировки файла , как flock(2) или fcntl(2)+F_SETLK, а не семафор. ИМХО, это лучший подход.

Во-вторых, сохраняйте бесконечность . Вы правы, это предложение очень модно, и ваша фраза предполагает, что новые клиенты sem могут появиться в любое время. Вам понадобится отдельный механизм синхронизации, например отдельный долгоживущий sem, для управления созданием / уничтожением sem, который вам действительно нужен. Вы можете получить экзотику и комбинировать это с выделенным эсминцем «ожидания нуля» (mysembuf.sem_op := 0), наблюдая за подсчетом ответов и готовый к IPC_RMID. Тьфу. Лучше иметь только один постоянный двоичный семафор без учета количества ссылок, предоставленного пользователем.

В-третьих, использовать POSIX с именем sems . Проигнорируйте sem_unlink() и вместо этого просто sem_close() когда закончите (конечно, после разблокировки sem_post()!). Концептуально это похоже на предыдущий подход - сохраняется небольшой примитив синхронизации, но, как вы говорите, более простой API. Также вам не нужно иметь дело с роковым недостатком семафоров SysV .

0 голосов
/ 08 августа 2009

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

Создание

Попробуйте открыть [1] существующий набор sem с 3 sems, в случае неудачи попробуйте [2] создать его. Если не удается создать, потому что кто-то уже создал, вернитесь к [1]. Этот цикл в конечном итоге завершится либо с открытым, либо с созданным sem, либо из-за ошибки, с которой я не могу справиться, и в этом случае я беру мяч и иду домой. (У меня также есть предел N итераций, на всякий случай:)).

Одна из 3-х сем - это полезная нагрузка, другая - счетчик ссылок, а третья - блокировка для счетчика ссылок. После того, как [2] блокировка инициализируется до 0, состояние блокировки.

Крепежные

Если набор sem был создан с помощью [2], все 3 sem подаются в semoped [3] от 0 до 1. Полезная нагрузка освобождается, количество ссылок равно 1, блокировка снимается (без отмены). Если он был открыт с помощью [1], блокировка получается [4] (-1), счетчик ссылок увеличивается (+1) и блокировка снимается (+1). Это заблокирует, если блокировка равна нулю в данный момент. Если этот semop завершается неудачно из-за того, что множество наборов уничтожается в [6], пока мы ожидаем, сохранение не удастся, и мы возвращаемся к [1]. Этот цикл также имеет ограниченное количество итераций.

Высвобождение

Блокировка получена [5] (-1 с ожиданием), счетчик ссылок уменьшен (-1 без ожидания). Если это удастся, то если ref ref теперь равен нулю, набор sem уничтожается. В противном случае [6] блокировка снята (+1). Если получить блокировку не удалось из-за того, что набор sem был уничтожен - ничего не делать.

Между удержанием и освобождением полезная нагрузка используется как обычно.

Помимо сложности и накладных расходов на 2 семафора на набор, есть только одна проблема (теперь я вижу роковой недостаток :)) - когда создатель падает между [2] и [3]. Это приведет к гибели всех клиентов. Я могу использовать синхронизированное ожидание в Linux и убивать осиротевшие семафоры, но OSX, будучи обычным идиотом, не имеет временных операций, так что я вроде как ...

* ... уходит, чтобы написать собственное ядро ​​или что-то ... *

...