Mit Hardware-Beschleunigung und schnellem Netz hilft Desktop-Virtualisierung, Administrationsaufwand und Kosten sparen, ADMIN 04/2013 verrät, wie die ... (mehr)

Besser, aber immer noch nicht gut

Obwohl das Programm jetzt eigentlich recht brauchbar aussieht, weist es noch einen echten Schwachpunkt auf. Dieser wird aber erst klar, wenn man im Detail versteht, wie die sichere Signal-Behandlung in Perl funktioniert. Dabei wird sich zeigen, dass sie für unseren Fall eigentlich nicht tauglich ist.

Welches Problem versuchen die sicheren Signale in Perl eigentlich zu lösen? Wenn ein Signal eintrifft, wird der Signal-Handler aufgerufen. Das kann zu einem beliebigen Zeitpunkt passieren. Wenn nun das Programm gerade dabei ist, bestimmte globale Variablen zu verändern, und der Signal-Handler mit denselben Variablen hantieren will, ist das Chaos vorprogrammiert.

Das Paradebeispiel hierfür ist »malloc« . Das eigentliche Programm versucht gerade, über diese Funktion an mehr Speicher zu gelangen. Je nach Implementation verwaltet sie dazu mehrere globale Listen oder ähnliche Strukturen. Wenn nun aber der Signal-Handler auch Speicher braucht, ist genau diese Situation eingetreten. Sprachen wie Perl arbeiten sehr viel mit der Speicherverwaltung. Der Platz für Variablen in Perl wird von der Speicherverwaltung angefordert und auch wieder freigegeben. Daher ist es kaum möglich, einen Signal-Handler in Perl zu schreiben, der nicht mit globalen Strukturen arbeitet.

Verzögert verarbeitet

Für die sichere Signal-Behandlung verwaltet Perl nun intern eine Anzahl von Flags. Installiert ein Skript für ein Signal einen Handler, ruft der Interpreter nicht etwa die Perl-Funktion auf, sobald das Signal eintrifft. Der eigentliche Signal-Handler ist nämlich eine interne Funktion, die nur eines der Flags setzt. An geeigneten Stellen prüft der Perl-Interpreter dann mit Hilfe des Makros »PERL_ASYNC_CHECK« , ob eines der Flags gesetzt ist und ruft gegebenenfalls den Signal-Handler in Perl auf. Abbildung 5 stellt dies grafisch dar. Da der eigentliche Signal-Handler jetzt nur noch seine eigenen Datenstrukturen verändert, treten keine unerwarteten Programmabstürze mehr auf. In diesem Sinne ist die Signal-Behandlung sicher.

Abbildung 5: Sichere Signale: Der C-Signal-Handler setzt nur die Flags. Der Perl-Signal-Handler läuft später separat ab, um beim Zugriff auf globale Variablen nicht in Konflikt zu geraten.

Leider kann dabei die zeitnahe Zustellung der Signale auf der Strecke bleiben. Das Problem resultiert aus einer Race Condition. Der »can_read« -Aufruf in Zeile  26 ruft irgendwann den Perl-Befehl »select« auf. Innerhalb dieses Befehls passiert auf C-Ebene ungefähr Folgendes:

PERL_ASYNC_CHECK;
/* einige weitere C-Kommandos */
select(...);       <-- Syscall

Es wird geprüft, ob Signale vorliegen. Dann folgt eine Reihe von Kommandos, um die Parameter des Perl-Befehls »select« in solche zu übersetzen, die der Kernel-Aufruf »select« versteht. Schließlich wird der Kernel aufgerufen und der Prozess blockiert.

Was passiert aber, wenn das Signal zwischen »PERL_ASYNC_CHECK« und dem Kernel-Aufruf eintrifft? In dem Fall setzt der C-Level-Signal-Handler wie gewohnt sein Bit. Er kann jedoch nicht mehr verhindern, dass der Prozess im System-Aufruf blockiert. Das Signal wird also erst an den Perl-Handler zugestellt, wenn der System-Aufruf zurückkehrt. Für das CGI-Programm sieht es dann so aus, als gehe das SIGTERM verloren. Der Apache sendet kurz darauf das SIGKILL, und das CGI-Programm bricht ab. Der Datenbank-Prozess läuft jedoch weiter.

Ähnliche Artikel

comments powered by Disqus
Einmal pro Woche aktuelle News, kostenlose Artikel und nützliche ADMIN-Tipps.
Ich habe die Datenschutzerklärung gelesen und bin einverstanden.

Konfigurationsmanagement

Ich konfiguriere meine Server

  • von Hand
  • mit eigenen Skripts
  • mit Puppet
  • mit Ansible
  • mit Saltstack
  • mit Chef
  • mit CFengine
  • mit dem Nix-System
  • mit Containern
  • mit anderer Konfigurationsmanagement-Software

Ausgabe /2023