Solución:
Un proceso ininterrumpido es un proceso que se encuentra en una llamada al sistema (función del kernel) que no puede ser interrumpido por una señal.
Para comprender lo que eso significa, debe comprender el concepto de una llamada al sistema interrumpible. El ejemplo clásico es read()
. Esta es una llamada al sistema que puede llevar mucho tiempo (segundos) ya que potencialmente puede implicar girar un disco duro o mover cabezas. Durante la mayor parte de este tiempo, el proceso estará inactivo, bloqueando el hardware.
Mientras el proceso está durmiendo en la llamada al sistema, puede recibir una señal asíncrona de Unix (digamos, SIGTERM), luego sucede lo siguiente:
- La llamada al sistema sale prematuramente y está configurada para devolver -EINTR al espacio de usuario.
- Se ejecuta el manejador de señales.
- Si el proceso aún se está ejecutando, obtiene el valor de retorno de la llamada al sistema y puede realizar la misma llamada nuevamente.
Regresar temprano de la llamada al sistema permite que el código de espacio del usuario altere inmediatamente su comportamiento en respuesta a la señal. Por ejemplo, terminando limpiamente en reacción a SIGINT o SIGTERM.
Por otro lado, algunas llamadas al sistema no pueden interrumpirse de esta forma. Si las llamadas del sistema se detienen por alguna razón, el proceso puede permanecer indefinidamente en este estado imposible de matar.
LWN publicó un buen artículo que tocó este tema en julio.
Para responder a la pregunta original:
-
Cómo evitar que esto suceda: averigüe qué controlador le está causando problemas y deje de usarlo o conviértase en un pirata informático del kernel y corríjalo.
-
Cómo matar un proceso ininterrumpido sin reiniciar: de alguna manera, haga que la llamada al sistema termine. Con frecuencia, la forma más eficaz de hacer esto sin presionar el interruptor de encendido es tirar del cable de alimentación. También puede convertirse en un pirata informático del kernel y hacer que el controlador use TASK_KILLABLE, como se explica en el artículo de LWN.
Cuando un proceso está en modo de usuario, se puede interrumpir en cualquier momento (cambiando al modo de kernel). Cuando el kernel vuelve al modo de usuario, comprueba si hay señales pendientes (incluidas las que se utilizan para matar el proceso, como SIGTERM
y SIGKILL
). Esto significa que un proceso solo se puede matar al volver al modo de usuario.
La razón por la que un proceso no se puede matar en el modo kernel es que podría dañar las estructuras del kernel utilizadas por todos los demás procesos en la misma máquina (de la misma manera, matar un subproceso puede dañar las estructuras de datos utilizadas por otros subprocesos en el mismo proceso) .
Cuando el kernel necesita hacer algo que podría llevar mucho tiempo (esperar en una tubería escrita por otro proceso o esperar a que el hardware haga algo, por ejemplo), se duerme marcándose a sí mismo como durmiendo y llamando al programador para que cambie a otro. proceso (si no hay un proceso no inactivo, cambia a un proceso “ficticio” que le dice a la CPU que disminuya la velocidad un poco y se sienta en un bucle – el bucle inactivo).
Si se envía una señal a un proceso inactivo, debe despertarse antes de que vuelva al espacio de usuario y, por lo tanto, procese la señal pendiente. Aquí tenemos la diferencia entre los dos tipos principales de sueño:
-
TASK_INTERRUPTIBLE
, el sueño interrumpible. Si una tarea está marcada con esta bandera, está inactiva, pero se puede despertar mediante señales. Esto significa que el código que marcó la tarea como inactiva está esperando una posible señal, y después de que se despierte la buscará y regresará de la llamada al sistema. Una vez que se maneja la señal, la llamada al sistema puede reiniciarse automáticamente (y no entraré en detalles sobre cómo funciona). -
TASK_UNINTERRUPTIBLE
, el sueño ininterrumpido. Si una tarea está marcada con este indicador, no espera que la despierte nada más que lo que esté esperando, ya sea porque no se puede reiniciar fácilmente o porque los programas esperan que la llamada al sistema sea atómica. Esto también se puede utilizar para los sueños que se sabe que son muy cortos.
TASK_KILLABLE
(mencionado en el artículo de LWN vinculado por la respuesta de ddaa) es una nueva variante.
Esto responde a su primera pregunta. En cuanto a su segunda pregunta: no se pueden evitar los inactivos ininterrumpidos, son algo normal (sucede, por ejemplo, cada vez que un proceso lee / escribe desde / hacia el disco); sin embargo, deberían durar sólo una fracción de segundo. Si duran mucho más, generalmente significa un problema de hardware (o un problema de controlador de dispositivo, que se ve igual para el kernel), donde el controlador de dispositivo está esperando que el hardware haga algo que nunca sucederá. También puede significar que está usando NFS y el servidor NFS está inactivo (está esperando que el servidor se recupere; también puede usar la opción “intr” para evitar el problema).
Finalmente, la razón por la que no puede recuperarse es la misma razón por la que el kernel espera hasta regresar al modo de usuario para entregar una señal o matar el proceso: podría dañar las estructuras de datos del kernel (el código que espera en un sueño interrumpible puede recibir un error que le indica para volver al espacio de usuario, donde el proceso se puede matar; el código que espera en suspensión ininterrumpida no espera ningún error).
Los procesos ininterrumpidos generalmente están esperando E / S después de una falla de página.
Considera esto:
- El hilo intenta acceder a una página que no está en el núcleo (ya sea un ejecutable que se carga a demanda, una página de memoria anónima que se ha intercambiado, o un archivo mmap () ‘d que se carga a demanda, que son en gran medida los la misma cosa)
- El kernel ahora está (intentando) cargarlo en
- El proceso no puede continuar hasta que la página esté disponible.
El proceso / tarea no se puede interrumpir en este estado, porque no puede manejar ninguna señal; si lo hiciera, se produciría otro error de página y volvería a estar donde estaba.
Cuando digo “proceso”, en realidad me refiero a “tarea”, que en Linux (2.6) se traduce aproximadamente como “subproceso” que puede tener o no una entrada individual de “grupo de subprocesos” en / proc
En algunos casos, puede estar esperando mucho tiempo. Un ejemplo típico de esto sería donde el archivo ejecutable o mmap’d está en un sistema de archivos de red donde el servidor ha fallado. Si la E / S finalmente tiene éxito, la tarea continuará. Si finalmente falla, la tarea generalmente obtendrá un SIGBUS o algo así.