Tema representativo de entrevista

Entrevista técnica: ¿Cómo construirías un supervisor de procesos en Linux con pidfds?

CodingDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Un supervisor de workers actualmente almacena PID numéricos, sondea `/proc` y ocasionalmente mata un proceso recién creado después de una reutilización de PID. ¿Cómo lo rediseñarías con pidfds de Linux y demostrarías que el ciclo de vida está libre de condiciones de carrera?

Planteamiento y alcance

Los PID numéricos pueden reutilizarse después de que un proceso termina, por lo que una señal o verificación de estado demorada puede apuntar al proceso incorrecto. Linux proporciona pidfd_open() para un descriptor de archivo que hace referencia a un proceso y pidfd_send_signal() para enviar señales a dicho proceso. La habilidad principal es la programación de sistemas y la corrección del ciclo de vida, por lo que esta es una pregunta de coding.

Qué evalúan los entrevistadores

Las respuestas sólidas explican la identidad del descriptor, la disponibilidad en poll o epoll, la integración con waitid, close-on-exec, permisos y limpieza. Distinguen un pidfd de un identificador de proceso que otorga acceso ilimitado, manejan condiciones de carrera en fork/exec y definen el comportamiento ante salidas, reinicios, tiempos de espera (timeouts) y caídas del supervisor. También mencionan la detección de características según la versión del kernel y una alternativa de respaldo (fallback) probada.

Preguntas para aclarar primero

  • ¿Qué versiones del kernel y namespaces debe admitir el supervisor?
  • ¿Inicia los procesos hijos por sí mismo o se conecta a procesos existentes?
  • ¿Debe observar el estado de salida, enviar señales o ambos?
  • ¿Puede el worker bifurcar descendientes y quién es el propietario de su limpieza?
  • ¿Cuáles son las garantías de timeout, reinicio y caída del supervisor?
  • ¿Se requiere una solución de respaldo en kernels sin soporte para pidfd?

Estructura de respuesta en 30 segundos

“Obtendría un pidfd para cada proceso administrado y almacenaría el descriptor, no solo su PID numérico. Agregaría el descriptor a poll o epoll, usaría la operación wait documentada para recolectar el estado y llamaría a pidfd_send_signal() para timeouts o apagado. Configuraría close-on-exec, cerraría los descriptores en cada ruta terminal y trataría la disponibilidad junto con la recolección de estado como una sola máquina de estados del ciclo de vida. Probaría la reutilización de PID, salidas rápidas, errores de permisos, procesos hijos bifurcados, detección de características del kernel y el reinicio del supervisor.”

Respuesta paso a paso

Paso 1: Adquirir y poseer el descriptor

Después de iniciar o ubicar un proceso, llama a pidfd_open() donde esté soportado y registra el descriptor en una tabla de propietarios. Márcalo como close-on-exec y conserva el PID numérico solo para los registros (logs). No pases el descriptor a workers no relacionados sin una transferencia explícita de propiedad.

Paso 2: Observar los eventos del ciclo de vida

Registra los pidfds con poll o epoll. La disponibilidad indica que el proceso referenciado ha terminado; recolecta su estado con la operación wait adecuada y luego cierra el descriptor. No infieras la actividad a partir de una ruta obsoleta de /proc o un entero de PID.

Paso 3: Enviar la señal al proceso correcto

Usa pidfd_send_signal() para la terminación controlada (graceful) y el escalamiento. Maneja explícitamente los errores de permisos y namespaces. Un pidfd identifica el proceso referenciado incluso si su PID numérico se reutiliza posteriormente, pero no reemplaza las comprobaciones de autorización.

Paso 4: Modelar el reinicio y los descendientes

Representa a cada worker como iniciando (starting), en ejecución (running), deteniéndose (stopping), terminado (exited) o fallido (failed). Al reiniciar, crea un pidfd nuevo y un registro de generación. Decide si los descendientes están en el mismo grupo de procesos, cgroup o en un dominio de propiedad separado; nunca asumas que enviar una señal a un padre limpia a todos sus hijos.

Paso 5: Probar los límites de concurrencia y portabilidad

Prueba bajo estrés salidas rápidas y reutilización de PID, señales concurrentes con la salida, agotamiento de descriptores, caídas del supervisor, cambios de namespace y kernels no soportados. Compara contra una alternativa de respaldo cuidadosamente delimitada como waitpid para hijos directos, y documenta qué garantías no puede proporcionar dicha alternativa.

Respuesta modelo

“El defecto proviene de usar un entero reutilizable como identidad del proceso. Almacenaría un pidfd por worker, lo registraría en un bucle de eventos (event loop), recolectaría el estado de salida tras la disponibilidad y enviaría señales a través de pidfd_send_signal() para la detención controlada y el escalamiento. La propiedad de descriptores, close-on-exec, los permisos y la limpieza de descendientes se convierten en reglas explícitas de una máquina de estados. Cada reinicio obtiene una nueva generación y pidfd. Las pruebas deben forzar salidas rápidas y reutilización de PID, cubrir el soporte de namespaces y kernel, y documentar las garantías más débiles de una alternativa para hijos directos.”

Errores comunes

  • Conservar solo el PID numérico → las señales demoradas compiten con la reutilización del PID → conserva el pidfd.
  • Sondear /proc para verificar actividad → las observaciones quedan obsoletas → usa la disponibilidad de pidfd y la recolección de estado.
  • Asumir que un pidfd elude los permisos → las señales aún requieren autorización → maneja los errores de permisos.
  • Filtrar descriptores a través de exec → programas no relacionados heredan identificadores del ciclo de vida → configura close-on-exec.
  • Matar solo al padre → los descendientes quedan huérfanos → define la propiedad por grupo o cgroup.
  • Tratar la disponibilidad como un estado completo → el manejo del código de salida queda incompleto → recolecta y persiste el estado de wait.

Preguntas de seguimiento

Pregunta de seguimiento 1: ¿Evita un pidfd la reutilización de PID?

Proporciona una referencia de descriptor estable al proceso para las operaciones que aceptan pidfds. El PID numérico puede reutilizarse, pero las operaciones a través del descriptor siguen haciendo referencia al proceso original.

Pregunta de seguimiento 2: ¿Se pueden usar pidfds con epoll?

Sí, un pidfd se puede sondear (es pollable) para detectar la salida de un proceso, por lo que puede participar en el bucle de eventos del supervisor junto con pipes, temporizadores y sockets de control.

Pregunta de seguimiento 3: ¿Qué pasa si el kernel carece de soporte para pidfd?

Detecta la capacidad al iniciar y usa una alternativa documentada para hijos directos, exponiendo garantías más débiles de concurrencia y observabilidad en lugar de pretender que son equivalentes.

Pregunta de seguimiento 4: ¿Qué ocurre después de que el supervisor se reinicia?

Persiste suficientes metadatos de propiedad para redescubrir o abandonar deliberadamente a los workers, y luego recrea el estado de monitoreo y los pidfds. Nunca confíes únicamente en un PID numérico persistido.

Fuentes públicas

Preguntas relacionadas

Herramienta de entrevista relacionada

Usa Captura para un ejercicio de código

Captura el problema y luego aborda en orden las restricciones, la solución, el código, los casos extremos y la complejidad.

Ver la herramienta