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

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

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

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

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

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

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

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

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

Ответы [ 2 ]

21 голосов
/ 05 декабря 2012

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

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

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

9 голосов
/ 14 сентября 2011

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

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

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

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

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

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

...