Interrupts – Python Tutorial Teil 21

Python Logo (CC-BY-SA The people from the Tango! project / Wikipedia)
Python Logo (CC-BY-SA The people from the Tango! project / Wikipedia)
1Python Programmierkurs
2Python: Methoden
3Kontrollstrukturen
4Strings in Python
5Container
6Objekte in Python
7Module
8Exceptions in Python
9Typkonvertierung
10Python und Dateien
11Datum und Zeit mit Python verarbeiten
12Multithreading
13Netzwerk in Python
14Logging in Python
15GPIO
16Automatische Tests
17Datenbanken mit Python
18Python: Generatoren und List Comprehension
19Python: Webseiten mit Flask
20Python virtuelle Umgebungen
21Interrupts & Signale
22NumPy
23Matplotlib
24match

Interrupts

Interrupts werden üblicherweise in Computersystemen benutzt, um den üblichen Ablauf zu unterbrechen und einen besonderen Zustand zu bearbeiten. So erzeugt jeder Tastendruck auf der Tastatur einen Interrupt, der dem Betriebssystem signalisiert, dass Daten vorliegen, die verarbeitet werden müssen. Gleiches gilt für alle Daten, die über das Netzwerk eingehen.

Interrupts in Python

In Python werden Interrupts durch die signal Library zur Verfügung gestellt. Die dort definierten Signale sind größtenteils kompatibel zu denen, die unter Linux benutzt werden. Diese können wir uns einfach mit kill -l anzeigen lassen.

Interrupts und Signale Liste von kill -l
Interrupts und Signale Liste von kill -l

Signale

Ich kann hier unmöglich auf alle oben gezeigten Signale eingehen, daher beschränke ich mich auf einige häufig benutzte, denn das Verfahren ist in Python immer sehr ähnlich.

NameNummerBeschreibung
SIGHUP1Ursprünglich: Verbindung zum Terminal wird beendet (HangUP), Heute: Signal zum Neuladen der Konfigurationsdateien.
SIGINT2Programm wird mit CTRL-C beendet.
SIGUSR110benutzerdefiniert.
SIGUSR212benutzerdefiniert.
SIGTERM15Programm soll sich beenden.
SIGCONT18Programm fortsetzen
SIGTSTP19Programm wird mit CTRL-Z in den Hintergrund gelegt

Signale benutzen

Wie man den Programmabbruch durch den KeyboardInterrupt als Exception abfängt, habe ich schon verwendet. In diesem Fall wird das zugehörige Signal (SIGTERM) vom Python Interpreter verarbeitet und als Exception an dein Programm weitergegeben. Du kannst aber deinen eigenen Interrupt-Handler in die Kette einklinken.

Dazu brauchst du eine Methode, die beim signalisierten Ereignis aufgerufen wird und dieses verarbeitet:

#Behandlung von SIGINT (CTRL-C)
def handle_sigint(signum, frame) :
  print(f'Handling signal {signum} ({signal.Signals(signum).name}).')

  if signum == signal.SIGINT:
    print('SIGINT wird behandelt.')
    time.sleep(1)
    sys.exit(0)

Jede Signal Handler Methode erwartet zwei Parameter signum mit der Nummer des Signals (siehe oben bei kill -l) und frame, dieser enthält ein Objekt vom Typ signal.FrameType und enthält den „Rahmen“, in dem das Signal empfangen wurde.

Den frame Parameter brauchst du vermutlich eher selten, den signum eigentlich nur dann, wenn du eine gemeinsame Methode für alle Signale benutzt.Zur besseren Übersicht benutze ich hier allerdings separate Methoden für jedes Signal.

Als nächstes muss deine Methode als Signal-Handler registriert werden:

signal.signal(signal.SIGINT, handle_sigint)

Diese Zeile sagt nichts anderes aus als: Immer, wenn das Signal SIGINT auftaucht, rufe die Methode handle_sigint() auf. In der Beispielmethode beenden wir das Programm einfach nur. Es wäre aber möglich, dort noch eine Sicherheitsabfrage einzubauen oder Berechnungsergebnisse abzuspeichern. Ich bin allerdings der Meinung, dass so eine Methode keine langwierigen Aufgaben durchführen sollte sondern schnellstens nach der Abarbeitung des Signals wieder zum normalen Programmablauf zurückgeben sollte. Für alles andere gibt’s das Multithreading.

Das Ganze hab ich in einem Demonstrationsprogramm eingebunden:

#! /usr/bin/python3

import time
import signal
import sys

#Behandlung von SIGINT (CTRL-C)
def handle_sigint(signum, frame) :
  print(f'Handling signal {signum} ({signal.Signals(signum).name}).')

  if signum == signal.SIGINT:
    print(f'SIGINT wird behandelt. {frame}')
    time.sleep(1)
    sys.exit(0)

#Behandlung von SIGTSTP (CTRL-Z)

def handle_sigtstp(signum,frame):
  print(f'Behandle signal {signum} ({signal.Signals(signum).name}).')

  print('Programm in Hintergrund')

# Behandlung von SiGCONT
def handle_sigcont(signum,frame):
  print(f'Behandle signal {signum} ({signal.Signals(signum).name}).')

  print('Programm im Vordergrund')

if __name__ == '__main__':
# Interrupt Handler registrieren
  signal.signal(signal.SIGINT, handle_sigint)
  signal.signal(signal.SIGTSTP, handle_sigtstp)
  signal.signal(signal.SIGCONT, handle_sigcont)

  for i in range(0,10000000):
    print(f'Schleife: {i}')
    time.sleep(0.5)
  print('Schleifenende')

Das Programm registriert neben SIGINT auch noch Handler für SIGCONT und SIGTSTP und gibt in einer Schleife nur die ersten 10 Mio.

Integerzahlen aus. Wenn du während des Programablaufs CTRL-C drückst, passiert folgendes:

Wie du siehst, wird nach dem CTRL-C unsere Handler-Methode aufgerufen. Im Frameobjekt steht neben dem Namen des Pythonfiles auch die Zeilennummer, die zum Zeitpunkt des Signals aktuell war.

zeitgesteuerte Signale

Mit SIGALRM steht dir ein Signal zu Verfügung, mit dem du dein Programm nach einer definierten Zeit unterbrechen lassen kannst.

#! /usr/bin/python3
# encoding:utf-8

import signal
import time


def handle_alarm(signum, frame):
 print(f'Alarm ausgelöst bei {time.ctime()}')

signal.signal(signal.SIGALRM,handle_alarm)

signal.alarm(3)

print(f'aktuelle Zeit Start: {time.ctime()}')

time.sleep(13)
print(f'aktuelle Zeit Ende: {time.ctime()}')

Die Handler-Methode handle_alarm() wird wie oben beschrieben registriert und danach wird mit signal.alarm(3) die Zeit in Sekunden gesetzt, nach der der Alarm Interrupt ausgelöst werden soll. Das Programm selbst tut nichts anderes als 13 Sekunden warten. Die Ausgabe sieht dann so aus:

Ausgabe des Alarm Programms.
Ausgabe des Alarm Programms.

Das war unser kurzer Ausflug in die Welt der Interrupts und Signale. Im Repository ist der Programmcode hinterlegt.

Im nächsten Teil schauen wir uns NumPy mal an.

Schreibe einen Kommentar

Cookie Consent Banner von Real Cookie Banner