Comment traiter le signal en python sur une machine windows
Je suis en train d'essayer le code collé ci-dessous sur Windows, mais au lieu de traiter le signal, il est en train de tuer le processus.
Toutefois, le même code fonctionne sous Ubuntu.
import os, sys
import time
import signal
def func(signum, frame):
print 'You raised a SigInt! Signal handler called with signal', signum
signal.signal(signal.SIGINT, func)
while True:
print "Running...",os.getpid()
time.sleep(2)
os.kill(os.getpid(),signal.SIGINT)
Vous devez vous connecter pour publier un commentaire.
Python
os.kill
enveloppements deux indépendants des Api Windows. Il appelleGenerateConsoleCtrlEvent
lorsque lesig
paramètre estCTRL_C_EVENT
ouCTRL_BREAK_EVENT
. Dans ce cas, lepid
paramètre est un processus d'ID de groupe. Si le dernier appel échoue, et pour tous les autressig
valeurs, il appelleOpenProcess
et puisTerminateProcess
. Dans ce cas, lepid
paramètre est un IDENTIFIANT de processus et lasig
valeur est passée comme le code de sortie. Terminaison d'un processus de Windows ressemble à l'envoi deSIGKILL
à un processus POSIX. En général, cela devrait être évitée, car elle ne permet pas l'arrêt du processus proprement.Noter que les docs pour
os.kill
tort de prétendre que la "kill() en outre, prend processus de poignées pour être tué", qui n'a jamais été vrai. Il appelleOpenProcess
pour obtenir un handle de processus.La décision d'utiliser WinAPI
CTRL_C_EVENT
etCTRL_BREAK_EVENT
, au lieu deSIGINT
etSIGBREAK
, est malheureux pour la croix-plate-forme de code. Il n'est également pas défini ceGenerateConsoleCtrlEvent
n'lorsqu'il est passé d'un ID de processus qui n'est pas un processus ID de groupe. L'utilisation de cette fonction dans une API qui prend un ID de processus est douteux, au mieux, et potentiellement très mal.Pour vos besoins particuliers, vous pouvez écrire un adaptateur fonction qui rend
os.kill
un peu plus convivial pour la croix-plate-forme de code. Par exemple:Discussion
Windows n'est pas de mettre en œuvre les signaux au niveau du système [*]. Microsoft C runtime met en œuvre les six signaux qui sont requis par la norme C:
SIGINT
,SIGABRT
,SIGTERM
,SIGSEGV
,SIGILL
, etSIGFPE
.SIGABRT
etSIGTERM
sont mis en place seulement pour le processus en cours. Vous pouvez appeler le gestionnaire via Caugmenter
. Par exemple (en Python 3.5):SIGTERM
est inutile.Vous aussi vous ne pouvez pas faire grand chose avec
SIGABRT
en utilisant le module du signal car leannuler
fonction tue le processus une fois que le gestionnaire retourne, qui se produit immédiatement lorsque vous utilisez le module de signal interne du gestionnaire (il se déclenche un drapeau pour le régime enregistré d'Python appelable à être appelé dans le thread principal). Pour Python 3, vous pouvez utiliser à la place la faulthandler module. Ou appelez le CRT estsignal
fonction via ctypes pour définir un ctypes rappel que le gestionnaire.Le CRT met en œuvre
SIGSEGV
,SIGILL
, etSIGFPE
par la fixation d'un Windows structuré gestionnaire d'exception pour les Fenêtres correspondantes exceptions:Le CRT de la mise en œuvre de ces signaux est incompatible avec Python de traitement de signal. L'exception filtre les appels enregistrés gestionnaire, puis retourne
EXCEPTION_CONTINUE_EXECUTION
. Cependant, Python gestionnaire seuls les voyages d'un drapeau pour l'interprète d'appeler le régime enregistré d'callable un peu plus tard dans le thread principal. Ainsi, l'errant, le code qui a déclenché l'exception de continuer à déclencher dans une boucle sans fin. En Python 3, vous pouvez utiliser le faulthandler module d'exception pour ces signaux.Qui laisse
SIGINT
, pour qui Windows ajoute la non-standardSIGBREAK
. Les deux console et non de la console de processus peuventraise
ces signaux, mais seulement un processus de console peut recevoir d'un autre processus. Le CRT met en œuvre par l'enregistrement d'une console de contrôle du gestionnaire d'événement viaSetConsoleCtrlHandler
.La console envoie un événement de contrôle par la création d'un nouveau thread en pièce jointe dans un processus qui commence à s'exécuter à
CtrlRoutine
dans kernel32.dll ou kernelbase.dll (sans-papiers). Que le gestionnaire ne pas exécuter dans le thread principal peut conduire à des problèmes de synchronisation (par exemple, dans le REPL ou avecinput
). Aussi, un événement de contrôle de ne pas interrompre le thread principal si elle est bloquée en attente sur un objet de synchronisation ou en attente d'e/S synchrones à compléter. Les soins doivent être prises pour éviter de bloquer le thread principal si elle doit être interruptible parSIGINT
. Python 3 tentatives de contourner ce problème en utilisant un Windows événement de l'objet, qui peut également être utilisé en attend que devrait être interruptible parSIGINT
.Lorsque la console envoie le processus
CTRL_C_EVENT
ouCTRL_BREAK_EVENT
, le CRT du gestionnaire d'appels enregistrésSIGINT
ouSIGBREAK
gestionnaire, respectivement. LeSIGBREAK
gestionnaire est également appelé à laCTRL_CLOSE_EVENT
que la console envoie quand la fenêtre est fermée. Python par défaut de la manipulationSIGINT
par rasing unKeyboardInterrupt
dans le thread principal. Cependant,SIGBREAK
est d'abord le défautCTRL_BREAK_EVENT
gestionnaire, qui appelleExitProcess(STATUS_CONTROL_C_EXIT)
.Vous pouvez envoyer un événement de contrôle de tous les processus attachés à la console via
GenerateConsoleCtrlEvent
. Cela peut cibler un sous-ensemble de processus qui appartiennent à un groupe de processus, ou un groupe cible de 0 à envoyer l'événement à tous les processus attachés à la console actuelle.Groupes de processus ne sont pas bien documentés aspect de l'API Windows. Il n'y a pas de public API pour interroger le groupe d'un processus, mais tous les processus dans une session Windows appartient à un groupe de processus, même si c'est juste la wininit.exe groupe (session des services) ou winlogon.exe groupe (session interactive). Un nouveau groupe est créé par le passage de la création du drapeau
CREATE_NEW_PROCESS_GROUP
lors de la création d'un nouveau processus. Le groupe ID est l'ID de processus du processus créé. À ma connaissance, la console est le seul système qui utilise les processus de groupe, et c'est juste pourGenerateConsoleCtrlEvent
.Ce que la console n'lorsque l'ID de la cible n'est pas un groupe de processus ID n'est pas défini et ne doit pas être invoqué. Si à la fois le processus et de son processus parent sont attachés à la console, puis l'envoi d'un événement de contrôle agit fondamentalement comme l'objectif du groupe est de 0. Si le processus parent n'est pas attaché à la console, puis
GenerateConsoleCtrlEvent
échoue, etos.kill
appelsTerminateProcess
. Bizarrement, si vous ciblez le "Système" de processus (PID 4) et de ses processus enfant smss.exe (gestionnaire de session), l'appel réussit, mais rien ne se passe sauf que la cible est à tort ajouté à la liste des processus attachés (c'est à direGetConsoleProcessList
). C'est probablement parce que le processus parent est le "Ralenti", qui, depuis c'est le PID 0, est implicitement accepté que la diffusion PGID. Le processus parent règle s'applique également aux non-console de processus. Le ciblage d'un non-console processus enfant ne fait rien-sauf à tort endommager la console la liste des processus en ajoutant de la seule processus. J'espère qu'il est clair que vous ne devez envoyer un événement de contrôle dans le groupe 0 ou à un connu processus de groupe que vous avez créé viaCREATE_NEW_PROCESS_GROUP
.Ne comptez pas sur d'être en mesure d'envoyer
CTRL_C_EVENT
à rien, mais le groupe 0, puisque c'est d'abord désactivé dans un nouveau groupe de processus. Il n'est pas impossible d'envoyer cet événement à un nouveau groupe, mais le processus cible première est de permettreCTRL_C_EVENT
en appelantSetConsoleCtrlHandler(NULL, FALSE)
.CTRL_BREAK_EVENT
est tout ce que vous pouvez compter sur, car il ne peut pas être désactivé. L'envoi de cet événement est un moyen simple pour normalement tuer un processus enfant qui a commencé avecCREATE_NEW_PROCESS_GROUP
, en supposant qu'il a un WindowsCTRL_BREAK_EVENT
ou CSIGBREAK
gestionnaire. Si non, le gestionnaire par défaut va se terminer le processus, le réglage du code de sortie pourSTATUS_CONTROL_C_EXIT
. Par exemple:Noter que
CTRL_BREAK_EVENT
n'était pas envoyé la procédure en cours, car l'exemple d'objectifs le groupe de processus de le processus de l'enfant (y compris l'ensemble de ses processus enfants qui sont attachés à la console, et ainsi de suite). Si on a utilisé du groupe 0, le processus actuel aurait été tué ainsi puisque je n'ai pas de définir unSIGBREAK
gestionnaire. Nous allons essayer, mais avec un gestionnaire d'ensemble:[*]
Windows a les appels de procédure asynchrone (APC) de la file d'attente une fonction cible à un fil. Voir l'article À l'intérieur de NT de l'Appel de Procédure Asynchrone pour une analyse en profondeur de Windows Apc, en particulier pour clarifier le rôle de noyau de mode Apc. Vous pouvez file d'attente de l'utilisateur, le mode APC à un thread via
QueueUserAPC
. Ils ont aussi mis en file d'attente parReadFileEx
etWriteFileEx
pour la fin d'e/S de routine.Un utilisateur en mode APC s'exécute lorsque le thread entre dans une alertable d'attente (par exemple,
WaitForSingleObjectEx
ouSleepEx
avecbAlertable
commeTRUE
). En mode noyau de l'Apc, d'autre part, d'obtenir expédiés immédiatement (lors de l'IRQL est ci-dessousAPC_LEVEL
). Ils sont généralement utilisés par le gestionnaire d'e/S à compléter asynchronous I/O les Paquets de Demande dans le contexte du thread qui a émis la demande (par exemple, la copie de données à partir de l'IRP à une mémoire tampon en mode utilisateur). Voir Attend et les Apc pour un tableau qui montre comment les Apc affecter alertable et non alertable attend. Notez qu'en mode noyau de l'Apc de ne pas interrompre une attente, mais au lieu de cela sont effectuées à l'interne par l'attente de la routine.Windows pourrait mettre en œuvre POSIX comme signaux à l'aide de l'Apc, mais dans la pratique, on utilise d'autres moyens pour les mêmes fins. Par exemple:
__essayer
,__à l'exception de
,__enfin
,__laisser
,RaiseException
,AddVectoredExceptionHandler
.Répartiteur De Noyau D'Objets (c'est à dire Les Objets De Synchronisation), par exemple
SetEvent
,SetWaitableTimer
.Fenêtre de Messages, par exemple
SendMessage
(à une procédure de fenêtre),PostMessage
(à un thread de message de la file d'attente pour être expédié à une procédure de fenêtre),PostThreadMessage
(à un thread de message de la file d'attente),WM_CLOSE
,WM_TIMER
.Fenêtre de messages peuvent être transmis et affiché à tous les threads que la part de l'appelant fil de bureau et qui sont à la même ou plus bas niveau d'intégrité. L'envoi d'un message de fenêtre met dans un système de file d'attente à l'appel de la procédure de fenêtre lorsque le thread appelle
PeekMessage
ouGetMessage
. Poster un message, il ajoute au fil du message de la file d'attente, qui a un quota par défaut de 10 000 messages. Un thread avec un message de la file d'attente doit avoir une boucle de message pour traiter la file d'attente viaGetMessage
etDispatchMessage
. Threads dans une console processus n'ont généralement pas de file d'attente de messages. Cependant, la console de l'hôte processus, conhost.exe, n'est évidemment. Lorsque le bouton de fermeture est cliqué, ou lorsque le processus principal de la console est tué via le gestionnaire des tâches ou taskkill.exe, unWM_CLOSE
message est posté sur la file d'attente de message de la fenêtre de la console de fil. La console dans les virages envoie unCTRL_CLOSE_EVENT
à l'ensemble de ses processus attachés. Si un processus gère l'événement, c'est 5 secondes de fermer correctement avant d'être fermée.if sys.platform == 'win32': abandon_all_hope_ye_who_enter_here()
.signal.CTRL_BREAK_EVENT
pour arrêter un Python permet de faire appel de laatexit
gestionnaires donc je n'appellerais pas cela un arrêt normal de. Mais merci pour cet excellent résumé, il est vraiment utile.CREATE_NEW_PROCESS_GROUP
et ayant l'enfant à établir un gestionnaire poursignal.SIGBREAK
. Ce qui fonctionne pour un intentionnelle de signal de la part du parent. Malheureusement, il ne gère pas le cas lorsque la fenêtre de la console est fermée, en raison de la façon Python C gestionnaire de signal définit simplement un drapeau indiquant le signal et retourne immédiatement (les gestionnaires de signaux ne sont appelés dans le thread principal). Pour gérer ce cas, je devrais réviser cette réponse à fournir un ctypes exemple qui définit un gestionnaire de contrôle de console.