¿Linux limpia automáticamente los sockets de dominio abstracto?

15

Hay una gran respuesta en StackOverflow sobre proporcionar un mejor bloqueo para los demonios (sintetizado por Eduardo Fleury ) que no depende del mecanismo común de bloqueo de archivos PID para los demonios. Hay muchos buenos comentarios sobre por qué los archivos de bloqueo PID a veces pueden causar problemas, por lo que no los volveré a mencionar aquí.

En resumen, la solución se basa en sockets de dominio de espacio de nombres abstractos de Linux, que realiza un seguimiento de los sockets por nombre para usted, en lugar de depender de archivos, que pueden quedarse después de que el demonio es SIGKILL'd. El ejemplo muestra que Linux parece liberar el socket una vez que el proceso está inactivo.

Pero no puedo encontrar documentación definitiva en Linux que diga qué hace exactamente Linux con el socket abstracto cuando el proceso enlazado es SIGKILL'd. ¿Alguien sabe?

Dicho de otra manera, ¿cuándo se libera exactamente el socket abstracto para volver a utilizarlo?

No quiero reemplazar el mecanismo del archivo PID con sockets abstractos a menos que resuelva definitivamente el problema.

CivFan
fuente
3
No puedo encontrar nada que responda esto directamente. Pero dado que no hay una API para eliminar sockets abstractos, parece que tendrían que ser gestionados automáticamente por el núcleo. Cuando no hay procesos con el socket abierto, debería desaparecer.
Barmar
@Barmar lo suficientemente justo. ¿Te importaría agregarlo como respuesta?
CivFan
Preferiría tener información más definitiva.
Barmar

Respuestas:

5

Sí, Linux "limpia" automáticamente los sockets abstractos en la medida en que la limpieza incluso tiene sentido. Aquí hay un ejemplo de trabajo mínimo con el que puede verificar esto:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>

int
main(int argc, char **argv)
{
  int s;
  struct sockaddr_un sun;

  if (argc != 2 || strlen(argv[1]) + 1 > sizeof(sun.sun_path)) {
    fprintf(stderr, "usage: %s abstract-path\n", argv[0]);
    exit(1);
  }

  s = socket(AF_UNIX, SOCK_STREAM, 0);
  if (s < 0) {
    perror("socket");
    exit(1);
  }
  memset(&sun, 0, sizeof(sun));
  sun.sun_family = AF_UNIX;
  strcpy(sun.sun_path + 1, argv[1]);
  if (bind(s, (struct sockaddr *) &sun, sizeof(sun))) {
    perror("bind");
    exit(1);
  }
  pause();
}

Ejecute este programa como ./a.out /test-socket &, luego ejecute ss -ax | grep test-socket, y verá el socket en uso. Entonces kill %./a.out, y ss -axmostrará que el zócalo se ha ido.

Sin embargo, la razón por la que no puede encontrar esta limpieza en ninguna documentación es que realmente no se está limpiando en el mismo sentido que los sockets de dominio unix no abstractos necesitan limpieza. Un socket no abstracto en realidad asigna un inodo y crea una entrada en un directorio, que debe limpiarse en el sistema de archivos subyacente. Por el contrario, piense en un socket abstracto más como un número de puerto TCP o UDP. Claro, si vincula un puerto TCP y luego sale, ese puerto TCP estará libre nuevamente. Pero cualquier número de 16 bits que utilizó todavía existe de manera abstracta y siempre lo hizo. El espacio de nombres de los números de puerto es 1-65535 y nunca cambia ni necesita limpieza.

Entonces, piense en el nombre del socket abstracto como un número de puerto TCP o UDP, simplemente seleccionado de un conjunto mucho más grande de números de puerto posibles que parecen nombres de ruta pero no lo son. No puede enlazar el mismo número de puerto dos veces (salvo SO_REUSEADDRo SO_REUSEPORT). Pero cerrar el socket (explícita o implícitamente al terminar) libera el puerto, sin nada que limpiar.

usuario3188445
fuente
Ya pasó la prueba del pato. Eso está bien para algunas cosas, como python, pero espero más para las características del kernel de Linux. Tome sus ejemplos de puertos TCP / UDP: hay mucha documentación que describe con precisión cuándo se puede reutilizar el puerto.
CivFan
2
¿Y por qué esa documentación no se aplica igualmente a los sockets abstractos? Tiene una referencia? Una mejor pregunta podría ser dónde se documenta la complicación de limpieza adicional para sockets de dominio Unix no abstractos. En mi sistema, eso está en Unix (7) , que dice "Linux también admite un espacio de nombres abstracto que es independiente del sistema de archivos". Entonces, para mí, "independiente del sistema de archivos" no implica una limpieza específica del sistema de archivos.
user3188445
5

Publiqué esta pregunta hace más de un año y nunca estuve muy satisfecho con la falta de documentación definitiva. Pensé en revisar la documentación de Linux nuevamente para ver si había actualizaciones, y me alegró ver esto :

Tomas abstractas

Los permisos de socket no tienen significado para los sockets abstractos: el proceso umask (2) no tiene ningún efecto al vincular un socket abstracto, y cambiar la propiedad y los permisos del objeto (a través de fchown (2) y fchmod (2)) no tiene ningún efecto en el Accesibilidad del zócalo.

Los sockets abstractos desaparecen automáticamente cuando se cierran todas las referencias abiertas al socket.

Además, la interfaz de programación de Linux de Michael Kerrisk cubre la pregunta (publicada en esta otra respuesta ):

57.6 El espacio de nombres de sockets abstractos de Linux

El llamado espacio de nombres abstracto es una característica específica de Linux que nos permite vincular un socket de dominio UNIX a un nombre sin que ese nombre se cree en el sistema de archivos. Esto proporciona algunas ventajas potenciales:

  • No debemos preocuparnos por posibles colisiones con nombres existentes en el sistema de archivos.
  • No es necesario desvincular el nombre de ruta del socket cuando hayamos terminado de usar el socket. El nombre abstracto se elimina automáticamente cuando se cierra el socket.
  • No necesitamos crear una ruta de acceso del sistema de archivos para el socket. Esto puede ser útil en un entorno chroot, o si no tenemos acceso de escritura a un sistema de archivos.

Para crear un enlace abstracto, especificamos el primer byte del campo sun_path como un byte nulo (\ 0). [...]

Creo que, junto con la respuesta de @ user3188445, esto aclara la pregunta con mucha precisión.

Dicho esto, todavía hay una suposición hecha aquí, que los procesos que son SIGKILL'd tendrán todos los sockets abiertos cerrados. Parece una suposición razonable, pero no tengo documentación que defina ese comportamiento.

CivFan
fuente
1
Re último párrafo: los sockets son descriptores de archivos, y todos los descriptores de archivos abiertos se cierran cuando sale un proceso. Ish Más específicamente, un socket es un archivo abierto, los archivos abiertos se pueden pasar, por ejemplo, heredados por procesos secundarios. Por lo tanto, debe asegurarse de llamar al socket SOCK_CLOEXECen caso de que algún código (incluida una biblioteca) alguna vez haga fork () + exec (). Crear procesos secundarios adicionales usando fork () sin exec () es menos común; Probablemente ya sepas si estás haciendo eso.
sourcejedi
No es necesario desvincular ... - um, ya que no hay un nombre de ruta, no es posible desvincular, no solo innecesario.
domen