Qu'est-ce qu'un processus ininterrompu?


156

Parfois, chaque fois que j'écris un programme sous Linux et qu'il plante à cause d'un bogue quelconque, il devient un processus ininterrompu et continue de fonctionner pour toujours 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 ininterrompu?
  • Comment puis-je empêcher cela de se produire?
  • C'est probablement une question stupide, mais y a-t-il un moyen de l'interrompre sans redémarrer mon ordinateur?

Est-il possible qu'un programme puisse être écrit pour initier un processus qui entre dans un TASK_UNINTERUPTIBLEétat chaque fois que le système n'est pas dans un état inactif, collectant ainsi de force des données, attendant de transmettre une fois que le super utilisateur quitte? Ce serait une mine d'or pour les pirates informatiques pour récupérer des informations, revenir à l'état de zombie et transmettre des informations via le réseau au repos. Certains peuvent soutenir que c'est une façon de créer un Blackdoorpour les pouvoirs en place, d'entrer et de sortir de n'importe quel système comme souhaité. Je crois fermement que cette faille peut être scellée pour de bon, en éliminant le `TASK_UNINTERUPTIB
Nuuwski

2
serait s'il vous plaît partager le code?
nouveau le

Réponses:


198

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

Pour comprendre ce que cela signifie, vous devez comprendre le concept d'appel système interruptible. L'exemple classique est read(). Il s'agit d'un appel système qui peut prendre beaucoup de temps (secondes) car il peut potentiellement impliquer la mise en marche d'un disque dur ou le déplacement de têtes. Pendant la plupart de ce temps, le processus sera en veille, bloquant 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), puis ce qui suit se produit:

  • Les appels système se terminent prématurément et sont configurés pour renvoyer -EINTR dans l'espace utilisateur.
  • Le gestionnaire de signaux 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 à nouveau le même appel.

Le retour prématuré 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 se bloque pour une raison quelconque, le processus peut rester indéfiniment dans cet état impossible à tuer.

LWN a publié un bel article sur ce sujet en juillet.

Pour répondre à la question initiale:

  • Comment éviter que cela ne se produise: déterminez quel pilote vous cause des problèmes, arrêtez d'utiliser ou devenez un hacker du noyau et corrigez-le.

  • Comment tuer un processus sans interruption sans redémarrer: faites en quelque sorte terminer l'appel système. Souvent, la manière la plus efficace de le faire sans toucher l'interrupteur d'alimentation est de tirer sur le cordon d'alimentation. Vous pouvez également devenir un hacker du noyau et faire en sorte que le pilote utilise TASK_KILLABLE, comme expliqué dans l'article LWN.


31
J'ai tiré sur le cordon d'alimentation de mon ordinateur portable et il ne fonctionne malheureusement pas. ;-)
thecarpy

1
N'est-ce pas EINTR au lieu d'EAGAIN? Read () renvoie également -1 et errno est défini sur l'erreur.
lethalman

2
@Dexter: Vous manquez en effet le point. Lisez l'article LWN : lwn.net/Articles/288056 . Ces problèmes sont causés par des programmeurs de pilote de périphérique paresseux et doivent être résolus dans le code du pilote de périphérique.
ddaa

4
@ddaa "La tradition Unix (et donc presque toutes les applications) pense que les écritures de stockage de fichiers ne sont pas interruptibles au signal. Il ne serait ni sûr ni pratique de changer cette garantie. -> C'est exactement la partie la plus fausse de toute cette OMI. Interrompez simplement la demande de lecture / écriture du pilote, et lorsque le périphérique réel (disque dur / carte réseau / etc.) fournit les données, ignorez-le. Un noyau de système d'exploitation doit être conçu de manière à ce qu'aucun développeur ne puisse le bousiller.
Dexter

2
@ddaa Je sais que Linux n'est pas un micro-noyau, même si je ne suis pas sûr de la partie de mon commentaire qui s'y rapporte ... Et puis, votre commentaire signifie-t-il qu'un OS de micro-noyau n'a pas de problème avec ces processus "ininterrompus"? Parce que si ce n'est pas le cas, il est peut-être temps pour moi de devenir fan de micro-noyau ...: D
Dexter

49

Lorsqu'un processus est en mode utilisateur, il peut être interrompu à tout moment (passage en mode noyau). Lorsque le noyau retourne en mode utilisateur, il vérifie s'il y a des signaux en attente (y compris ceux qui sont utilisés pour tuer le processus, tels que SIGTERMet SIGKILL). Cela signifie qu'un processus ne peut être tué qu'au 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 du noyau utilisées par tous les autres 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 a besoin de faire quelque chose qui pourrait prendre beaucoup de temps (attendre sur un tube écrit par un autre processus ou attendre que le matériel fasse quelque chose, par exemple), il dort en se marquant comme endormi et en appelant le planificateur pour passer à un autre processus (s'il n'y a pas de processus non dormant, il passe à un processus "factice" qui indique au processeur de ralentir un peu et se place dans une boucle - la boucle inactive).

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

  • TASK_INTERRUPTIBLE, le sommeil interruptible. Si une tâche est marquée de cet indicateur, 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 et, après son réveil, le vérifiera et reviendra de l'appel système. Une fois le signal traité, l'appel système peut potentiellement être redémarré automatiquement (et je n'entrerai pas dans les détails sur la façon dont cela fonctionne).
  • TASK_UNINTERRUPTIBLE, le sommeil ininterrompu. 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. Cela peut également être utilisé pour les dortoirs connus pour être très courts.

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

Cela répond à votre première question. Quant à votre deuxième question: vous ne pouvez pas éviter les sommeil ininterrompus, c'est une chose normale (cela arrive, par exemple, chaque fois qu'un processus lit / écrit depuis / 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), où 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 se rétablisse; 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 raison pour laquelle le noyau attend le retour en mode utilisateur pour délivrer un signal ou tuer le processus: cela corromprait potentiellement les structures de données du noyau (le code en attente d'un sommeil interruptible peut recevoir une erreur qui le signale pour revenir à l'espace utilisateur, où le processus peut être tué; le code en attente d'un sommeil ininterrompu n'attend aucune erreur).


1
Le bogue de verrouillage du système de fichiers est également une cause probable, IME.
Tobu

3
Je ne comprends pas tout cela. "vous ne pouvez pas éviter les sommeil ininterrompus" - le système d'exploitation ne peut-il pas être conçu de telle sorte que le sommeil ininterrompu n'existe tout simplement pas en tant qu'état? Ensuite, la partie sur la corruption - la partie en mode noyau du processus lui-même (ou ce qui pourrait causer la corruption) ne peut-elle pas être arrêtée ou simplement son code modifié en mémoire pour simplement revenir? Veuillez expliquer pourquoi est-ce si difficile / impossible à faire que même Linux ne l'a pas fait. (Je pensais que ce problème n'existe que sur Windows)
Dexter

Le seul cas auquel je puisse penser qui rendrait (en toute sécurité) la suppression de ces processus vraiment impossible (et pas seulement, disons, exceptionnellement difficile) est si le matériel lui-même pourrait causer la corruption. Le matériel ne peut pas être contrôlé; noyau peut . Mais c'est le noyau qui récupère les données du matériel et modifie la mémoire (c'est pourquoi il ne faut pas le libérer avant que le processus ne retourne en mode utilisateur et pourquoi la corruption pourrait se produire) ... changer le code du noyau en mémoire et plus de problèmes.
Dexter

@Dexter pense au noyau comme s'il s'agissait d'un seul processus multi-thread, où la partie en mode noyau de chaque processus est un thread dans le noyau. Votre suggestion serait aussi mauvaise que de tuer un seul thread dans un programme multi-thread: cela pourrait laisser des verrous suspendus, des structures de données temporairement modifiées ou en cours de modification, etc.
CesarB

@CesarB eh bien vous avez raison de tuer un thread ... Mais le thread "principal" (ce serait le noyau du système d'exploitation et d'autres threads seraient des pilotes par exemple) ne peut-il pas le gérer? Bien que ces structures "en cours de modification" semblent être un problème vraiment difficile ... peut-être que nous ne verrons jamais vraiment un système d'exploitation où des processus ininterrompus seraient impossibles :(
Dexter

23

Les processus ininterrompus attendent généralement des E / S suite à une erreur de page.

Considère ceci:

  • Le thread essaie d'accéder à une page qui n'est pas dans le noyau (soit un exécutable qui est chargé à la demande, une page de mémoire anonyme qui a été permutée, soit un fichier mmap () 'd qui est chargé à la demande, qui sont même chose)
  • Le noyau est maintenant (essaie 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 erreur de page se produirait et elle serait de retour là où elle était.

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

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


1
Si elle échoue finalement, la tâche obtiendra généralement un SIGBUS ou quelque chose. Attendez, est-ce que le noyau ne peut pas être fait pour que, lors de la suppression de ces processus "ininterrompus", il leur dise simplement que l'opération d'E / S a échoué? Ensuite, le processus reviendrait en mode utilisateur et serait parti? Il doit y avoir un moyen de tuer en toute sécurité ces processus à l'état «D». Je suppose que ce n'est tout simplement pas facile et c'est pourquoi ni Windows ni Linux n'ont encore cette possibilité. De l'autre côté, je voudrais être en mesure de tuer ces processus au moins de manière non sécurisée. Je ne me soucie pas d'un éventuel crash système ou autre ...
Dexter

@Dexter hmm, je n'ai jamais rencontré ce problème avec Windows. Comment le reproduire là-bas? Au moins selon cet article , toutes les demandes d'E / S peuvent être interrompues dans Windows.
Ruslan

1

À 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 terminer les processus en cours et après l'avoir exécuté, mes processus ininterrompus avaient disparu.


-3

Si vous parlez d'un processus "zombie" (qui est désigné comme "zombie" dans la sortie ps), il s'agit d'un enregistrement inoffensif dans la liste de processus en attente que quelqu'un récupère son code de retour et il pourrait être ignoré en toute sécurité.

Pourriez-vous s'il vous plaît décrire ce qu'est un «processus sans interruption» pour vous? Survit-il au "kill -9" et avance-t-il joyeusement? Si tel est le cas, alors il est bloqué sur un appel système, qui est bloqué dans un pilote, et vous êtes coincé avec ce processus jusqu'au redémarrage (et parfois il est préférable de redémarrer bientôt) ou au déchargement du pilote concerné (ce qui est peu probable) . Vous pouvez essayer d'utiliser "strace" pour savoir où votre processus est bloqué et l'éviter à l'avenir.


Ne peuvent pas être conducteurs avec force déchargés de la même manière un processus pourrait être tué? Je sais que le mode noyau a un accès plus privilégié que le mode utilisateur, mais il ne peut jamais être plus privilégié que le système d'exploitation lui-même. Tout ce qui s'exécute en mode noyau peut toujours altérer tout ce qui s'exécute en mode noyau - il n'y a tout simplement aucun contrôle.
Dexter
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.