web-dev-qa-db-fra.com

Qu'est-ce qu'un processus ininterruptible?

Parfois, chaque fois que j'écris un programme sous Linux et qu'il se bloque à cause d'un bogue quelconque, cela devient un processus ininterruptible et continue de s'exécuter indéfiniment jusqu'à ce que je redémarre mon ordinateur (même si je me déconnecte). Mes questions sont:

  • Qu'est-ce qui fait qu'un processus devient ininterruptible?
  • Comment puis-je empêcher que cela se produise?
  • C'est probablement une question idiote, mais y a-t-il un moyen de l'interrompre sans avoir à redémarrer mon ordinateur?
140
Jason Baker

Un processus ininterruptible est un processus qui se trouve être dans un appel système (fonction du noyau) qui ne peut pas être interrompu par un signal.

Pour comprendre ce que cela signifie, vous devez comprendre le concept d'un appel système pouvant être interrompu. L'exemple classique est read(). Il s’agit d’un appel système qui peut prendre un certain temps (en secondes) car il peut éventuellement impliquer de faire tourner un disque dur ou de déplacer des têtes. Pendant la majeure partie de cette période, le processus sera en veille et bloquera le matériel.

Pendant que le processus est en veille dans l'appel système, il peut recevoir un signal asynchrone Unix (par exemple, SIGTERM).

  • Les appels système se terminent prématurément et sont configurés pour renvoyer -EINTR dans l'espace utilisateur.
  • Le gestionnaire de signal est exécuté.
  • Si le processus est toujours en cours d'exécution, il obtient la valeur de retour de l'appel système et peut effectuer le même appel à nouveau.

Le renvoi anticipé de l'appel système permet au code d'espace utilisateur de modifier immédiatement son comportement en réponse au signal. Par exemple, terminer proprement en réaction à SIGINT ou à SIGTERM.

D'autre part, certains appels système ne peuvent pas être interrompus de cette manière. Si le système appelle un blocage pour une raison quelconque, le processus peut rester indéfiniment dans cet état impossible à détruire.

LWN a publié un article intéressant qui a touché ce sujet en juillet.

Pour répondre à la question initiale:

  • Comment empêcher cela: identifiez le pilote qui vous cause des problèmes et arrêtez d’utiliser ou devenez un pirate du noyau et corrigez-le.

  • Comment tuer un processus ininterruptible sans redémarrer: faites en sorte que l'appel système se termine. Le moyen le plus efficace de le faire sans appuyer sur l'interrupteur consiste à tirer sur le cordon d'alimentation. Vous pouvez également devenir un hacker du noyau et obliger le pilote à utiliser TASK_KILLABLE, comme expliqué dans l'article LWN.

179
ddaa

Lorsqu'un processus est en mode utilisateur, il peut être interrompu à tout moment (passage en mode noyau). Lorsque le noyau revient en mode utilisateur, il vérifie si des signaux sont en attente (y compris ceux utilisés pour tuer le processus, tels que SIGTERM et SIGKILL). Cela signifie qu'un processus peut être tué uniquement lors du retour en mode utilisateur.

La raison pour laquelle un processus ne peut pas être tué en mode noyau est qu'il pourrait potentiellement corrompre les structures de noyau utilisées par tous les processus de la même machine (de la même manière, tuer un thread peut potentiellement corrompre les structures de données utilisées par d'autres threads dans le même processus). .

Lorsque le noyau doit faire quelque chose qui pourrait prendre longtemps (attendre par exemple un tuyau écrit par un autre processus ou attendre que le matériel le fasse, par exemple), il dort en se signalant qu'il est en veille et appelle le planificateur pour passer à un autre. processus (s’il n’ya pas de processus non en veille, il passe à un processus "factice" qui demande au processeur de ralentir un peu et de s’asseoir dans une boucle - la boucle inactive).

Si un signal est envoyé à un processus en veille, il doit être réveillé avant qu'il ne retourne dans l'espace utilisateur et traite ainsi le signal en attente. Nous avons ici la différence entre les deux principaux types de sommeil:

  • TASK_INTERRUPTIBLE, le sommeil interruptible. Si une tâche est marquée avec ce drapeau, elle est en veille, mais peut être réveillée par des signaux. Cela signifie que le code qui a marqué la tâche comme étant en veille attend un signal possible. Après son réveil, il le recherchera et reviendra de l'appel système. Une fois le signal traité, l’appel système peut potentiellement être automatiquement redémarré (et je n’entrerai pas dans les détails pour savoir comment cela fonctionne).
  • TASK_UNINTERRUPTIBLE, le sommeil ininterruptible. Si une tâche est marquée avec cet indicateur, elle ne s'attend pas à être réveillée par autre chose que ce qu'elle attend, soit parce qu'elle ne peut pas être redémarrée facilement, soit parce que les programmes s'attendent à ce que l'appel système soit atomique. Ceci peut également être utilisé pour des nuits connues pour être très courtes.

TASK_KILLABLE (mentionné dans l’article de LWN lié à la réponse de ddaa) est une nouvelle variante.

Cela répond à votre première question. En ce qui concerne votre deuxième question: vous ne pouvez pas éviter les sommeil ininterruptible, ils sont une chose normale (cela se produit, par exemple, chaque fois qu'un processus lit/écrit à partir de/sur le disque); Cependant, ils ne devraient durer qu'une fraction de seconde. S'ils durent beaucoup plus longtemps, cela signifie généralement un problème matériel (ou un problème de pilote de périphérique qui ressemble au noyau), le pilote de périphérique attend que le matériel fasse quelque chose qui ne se produira jamais. Cela peut également signifier que vous utilisez NFS et que le serveur NFS est en panne (il attend que le serveur soit restauré; vous pouvez également utiliser l'option "intr" pour éviter le problème).

Enfin, la raison pour laquelle vous ne pouvez pas récupérer est la même que celle attendue par le noyau jusqu'à ce qu'il revienne en mode utilisateur pour envoyer un signal ou interrompre le processus: cela risquerait de corrompre les structures de données du noyau (le code en attente de veille interruptible peut recevoir une erreur qui le prévient). pour revenir à l'espace utilisateur, où le processus peut être tué; le code en attente de sommeil ininterruptible n'attend aucune erreur).

44
CesarB

Les processus ininterrompus attendent habituellement des E/S suite à une erreur de page.

Considère ceci:

  • Le thread tente d’accéder à une page qui n’est pas dans le noyau (un exécutable chargé à la demande, une page de mémoire anonyme remplacée ou un fichier mmap () qui est chargé à la demande, qui sont beaucoup même chose)
  • Le noyau est maintenant (essaye de) le charger dans
  • Le processus ne peut pas continuer tant que la page n'est pas disponible.

Le processus/la tâche ne peut pas être interrompu dans cet état, car il ne peut gérer aucun signal; si c'était le cas, une autre faute de page se produirait et ce serait le retour.

Quand je dis "processus", je veux vraiment dire "tâche", ce qui sous Linux (2.6) se traduit approximativement par "thread" qui peut ou non avoir une entrée individuelle "groupe de threads" dans/proc

Dans certains cas, il peut attendre longtemps. Un exemple typique de ceci serait où le fichier exécutable ou mmap'd est sur un système de fichiers réseau où le serveur a échoué. Si les E/S réussissent, la tâche se poursuit. Si cela échoue, la tâche obtiendra généralement un SIGBUS ou quelque chose du genre.

22
MarkR

Pour votre troisième question: Je pense que vous pouvez tuer les processus ininterrompus en exécutant Sudo kill -HUP 1. Il redémarrera init sans mettre fin aux processus en cours. Après l'avoir exécuté, mes processus ininterrompus ont disparu.

0
Ron Granger