sockets - que - socket protocolo
¿Cómo saber si algún proceso está vinculado a un socket de dominio Unix? (2)
Estoy escribiendo un servidor de socket de dominio Unix para Linux.
Una peculiaridad de los sockets de dominio Unix que descubrí rápidamente es que, al crear un socket Unix que escucha, crea la entrada del sistema de archivos correspondiente, cerrar el socket no lo elimina. Además, hasta que la entrada del sistema de archivos se elimine manualmente, no es posible bind()
un socket a la misma ruta de nuevo: bind()
falla con EADDRINUSE
si la ruta dada ya existe en el sistema de archivos.
Como consecuencia, la entrada del sistema de archivos del socket debe unlink()
''en el cierre del servidor para evitar que EADDRINUSE
reinicie. Sin embargo, esto no siempre se puede hacer (es decir, bloqueo del servidor). La mayoría de las preguntas frecuentes, publicaciones en foros, sitios web de preguntas y respuestas que encontré solo aconsejan, como una solución alternativa, unlink()
el socket antes de llamar a bind()
. Sin embargo, en este caso, es conveniente saber si un proceso está vinculado a este socket antes de unlink()
.
De hecho, unlink()
''un socket Unix mientras un proceso todavía está vinculado a él y luego volver a crear el socket de escucha no genera ningún error. Como resultado, sin embargo, el proceso del servidor anterior todavía se está ejecutando pero no se puede alcanzar: el nuevo socket de escucha está "enmascarado" por el nuevo. Este comportamiento debe ser evitado.
Idealmente, utilizando sockets de dominio Unix, la API de socket debería haber expuesto el mismo comportamiento de "exclusión mutua" que se expone al vincular sockets TCP o UDP: " Quiero vincular el socket S a la dirección A, si un proceso ya está vinculado a esta dirección ¡Solo denuncie! "Desafortunadamente este no es el caso ...
¿Hay alguna manera de hacer cumplir esta conducta de "exclusión mutua"? O, dada una ruta del sistema de archivos, ¿hay alguna manera de saber, a través de la API del socket, si algún proceso en el sistema tiene un socket de dominio Unix vinculado a esta ruta? ¿Debo usar una primitiva de sincronización externa a la API del socket ( flock()
, ...)? O me estoy perdiendo algo ?
Gracias por tus sugerencias
Nota: Los zócalos Unix de espacios de nombres abstractos de Linux parecen resolver este problema, ya que no hay una entrada del sistema de archivos para unlink()
. Sin embargo, el servidor que estoy escribiendo apunta a ser genérico: debe ser robusto frente a ambos tipos de sockets de dominio Unix, ya que no soy responsable de elegir las direcciones de escucha.
No creo que haya mucho más que hacer que las cosas que ya has considerado. Parece que lo has investigado bien.
Hay formas de determinar si un socket está vinculado a un socket unix (obviamente lsof y netstat lo hacen) pero son bastante complicados y dependen del sistema lo suficiente como para cuestionar si vale la pena el esfuerzo para resolver los problemas que plantea.
Realmente estás planteando dos problemas: resolver las colisiones de nombres con otras aplicaciones y lidiar con instancias previas de tu propia aplicación.
Por definición, varias instancias de su PGM no deberían intentar enlazarse con la misma ruta, por lo que probablemente solo quiera que se ejecute una instancia a la vez. Si ese es el caso, puede usar la técnica estándar de bloqueo de archivos pid para que dos instancias no se ejecuten simultáneamente. No deberías estar desvinculando el socket existente o incluso ejecutando si no puedes obtener el bloqueo. Esto también se ocupa del escenario de bloqueo del servidor. Si puede obtener el bloqueo, entonces sabrá que puede desvincular la ruta del socket existente antes de enlazar.
No hay mucho que pueda hacer AFAIK para controlar otros programas que crean colisiones. Los permisos de archivo no son perfectos, pero si la opción está disponible para usted, puede poner su aplicación en su propio usuario / grupo. Si hay una ruta de socket existente y no la posee, no la desenlace y envíe un mensaje de error y deje que el usuario o sysadmin la resuelvan. Usar un archivo de configuración para que sea fácilmente modificable, y disponible para los clientes, podría funcionar. Más allá de eso, casi tienes que ir a algún tipo de servicio de descubrimiento, que parece una exageración masiva a menos que esta sea una aplicación realmente crítica.
En general, puede estar tranquilo de que esto no sucede a menudo.
Sé que llegué muy tarde a la fiesta y que esto fue respondido hace mucho tiempo, pero acabo de encontrar esta búsqueda de otra cosa y tengo una propuesta alternativa.
Cuando encuentre el retorno de EADDRINUSE
desde bind()
, puede ingresar una rutina de comprobación de errores que se conecta al socket. Si la conexión tiene éxito, hay un proceso en ejecución que está al menos lo suficientemente vivo como para haber hecho el accept()
. Esto me parece la forma más simple y portátil de lograr lo que quiere lograr. Tiene inconvenientes porque el servidor que creó el UDS en primer lugar puede seguir ejecutándose pero "atascado" de algún modo y no puede hacer un accept()
, por lo que esta solución ciertamente no es infalible, pero es un paso en la dirección correcta, creo.
Si el connect()
falla, continúe y unlink()
el punto final y pruebe bind()
nuevamente.