Как узнать, привязан ли какой-либо процесс к сокету домена Unix?

Я пишу сервер сокетов домена Unix для Linux.

Особенность сокетов домена Unix, которую я быстро обнаружил, заключается в том, что при создании слушающего сокета Unix создается соответствующая запись файловой системы, закрытие сокета не удаляет ее. Более того, до тех пор, пока запись файловой системы не будет удалена вручную, невозможно снова bind() перейти к тому же пути к сокету: bind() завершается неудачно с EADDRINUSE, если указанный путь уже существует в файловой системе.

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

Действительно, unlink() установка сокета Unix, когда процесс все еще привязан к нему, а затем повторное создание слушающего сокета не вызывает никаких ошибок. В результате, однако, старый процесс сервера все еще работает, но недоступен: старый прослушивающий сокет «маскируется» новым. Такого поведения следует избегать.

В идеале, используя сокеты домена Unix, API сокетов должен демонстрировать такое же поведение «взаимного исключения», которое проявляется при привязке сокетов TCP или UDP: «Я хочу привязать сокет S к адресу A; если процесс уже привязан по этому адресу, просто пожалуйтесь! «К сожалению, это не так ...

Есть ли способ принудить к такому поведению «взаимного исключения»? Или, учитывая путь к файловой системе, есть ли способ узнать через API сокетов, есть ли у какого-либо процесса в системе сокет домена Unix, привязанный к этому пути? Должен ли я использовать примитив синхронизации, внешний по отношению к API сокетов (flock(), ...)? Или я что-то упускаю?

Спасибо за ваши предложения.

Примечание. Сокеты Unix абстрактного пространства имен Linux, похоже, решают эту проблему, поскольку в unlink() нет записи в файловой системе. Однако сервер, который я пишу, должен быть универсальным: он должен быть устойчивым к обоим типам сокетов домена Unix, поскольку я не несу ответственности за выбор адресов для прослушивания.


person Simon Malinge    schedule 13.09.2011    source источник


Ответы (2)


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

Когда вы встретите EADDRINUSE возврат от bind(), вы можете ввести процедуру проверки ошибок, которая подключается к сокету. Если соединение установлено успешно, значит, есть работающий процесс, который, по крайней мере, достаточно жив, чтобы выполнить accept(). Мне кажется, что это самый простой и переносимый способ достичь того, чего вы хотите достичь. У него есть недостатки в том, что сервер, который изначально создал UDS, на самом деле может все еще работать, но каким-то образом "завис" и не может выполнить accept(), поэтому это решение, безусловно, не является надежным, но это шаг в верное направление я думаю.

Если connect() не работает, продолжайте и unlink() конечную точку и попробуйте bind() снова.

person Kean    schedule 05.12.2012
comment
Я протестировал это, и, похоже, он работает так, как рекламируется. Блестяще! - person ioquatix; 04.03.2019

Я не думаю, что многое еще предстоит сделать, помимо того, что вы уже рассмотрели. Вы, кажется, хорошо это исследовали.

Есть способы определить, привязан ли сокет к сокету unix (очевидно, что это делают lsof и netstat), но они сложны и достаточно зависят от системы, поэтому я сомневаюсь, стоят ли они усилий для решения возникающих вами проблем.

На самом деле вы поднимаете две проблемы - столкновение имен с другими приложениями и работу с предыдущими экземплярами вашего собственного приложения.

По определению несколько экземпляров вашего pgm не должны пытаться привязаться к одному и тому же пути, так что это, вероятно, означает, что вы хотите, чтобы за один раз запускался только один экземпляр. В этом случае вы можете просто использовать стандартную технику блокировки файлов pid, чтобы два экземпляра не запускались одновременно. Вы не должны отключать существующий сокет или даже запускать его, если не можете получить блокировку. Это также заботится о сценарии сбоя сервера. Если вы можете получить блокировку, вы знаете, что можете отключить существующий путь к сокету перед привязкой.

Вы не так много можете сделать AFAIK для управления другими программами, создающими коллизии. Права доступа к файлам не идеальны, но если у вас есть такая возможность, вы можете поместить свое приложение в отдельного пользователя / группу. Если существует существующий путь к сокету, и вы не владеете им, не отключайте его, не выводите сообщение об ошибке и позволяйте пользователю или системному администратору разобраться в нем. Использование файла конфигурации, чтобы сделать его легко изменяемым и доступным для клиентов, может сработать. Помимо этого, вам почти нужно воспользоваться какой-то службой обнаружения, что кажется огромным излишеством, если только это не действительно критическое приложение.

В целом вас может утешить то, что на самом деле это случается нечасто.

person Duck    schedule 14.09.2011
comment
Спасибо за Ваш ответ. По общему признанию, использование традиционной системы файлов блокировки - самый безопасный способ. Также, что касается того, является ли система обнаружения служб избыточной или нет: по иронии судьбы этот сервер планируется сам по себе стать частью системы обнаружения служб (система регистрации служб кажется более подходящей). Это должно ответить на ваш вопрос ;-) - person Simon Malinge; 14.09.2011