Synchronisation und Deadlocks

Motivation und Zielsetzung

Im ersten Teil dieses Tutorials wurden gemeinsame Ressourcen mithilfe eines Semaphors erfolgreich geschützt. Dadurch konnte verhindert werden, dass mehrere Tasks gleichzeitig auf die serielle Schnittstelle zugreifen. Synchronisation löst jedoch nicht automatisch alle Probleme paralleler Systeme. Sobald mehrere Ressourcen gleichzeitig benötigt werden, können neue Fehlerbilder entstehen. Besonders kritisch sind Situationen, in denen sich Tasks gegenseitig blockieren und keine der beteiligten Aufgaben ihre Arbeit fortsetzen kann. Dieses Problem bezeichnet man als Verklemmung oder Deadlock.

Deadlocks treten in der Praxis häufiger auf, als zunächst vermutet wird. Bereits einfache Embedded-Anwendungen verwenden oft mehrere gemeinsam genutzte Ressourcen gleichzeitig, beispielsweise: Sensoren, Kommunikationsschnittstellen, Speicherzugriffe, Dateien, oder Peripheriegeräte. Werden diese Ressourcen in unterschiedlicher Reihenfolge angefordert, kann es passieren, dass jeder Task bereits eine Ressource besitzt und gleichzeitig auf eine weitere wartet. Dadurch entsteht ein zyklisches Warten, aus dem keiner der Tasks selbstständig herauskommt. 

Zielsetzung

Nach Abschluss dieses Abschnitts sollen die Studierenden:

  • verstehen, wie Deadlocks entstehen,
  • zyklische Wartebedingungen erkennen können,
  • die Bedeutung einer konsistenten Reihenfolge beim Ressourcen-Zugriff verstehen,
  • Deadlocks in einfachen Multitasking-Anwendungen analysieren können,
  • Strategien zur Vermeidung von Deadlocks kennenlernen,
  • die Risiken paralleler Ressourcennutzung in Embedded-Systemen  einschätzen können.

Theorie Deadlocks

In parallelen Systemen benötigen Tasks häufig nicht nur eine einzelne Ressource, sondern mehrere Ressourcen gleichzeitig. Beispiele dafür sind: mehrere Sensoren, verschiedene Kommunikationsschnittstellen, Speicher und Dateioperationen, oder unterschiedliche Hardware-Komponenten. Damit ein Task seine Arbeit ausführen kann, müssen alle benötigten Ressourcen verfügbar sein.

Im folgenden Beispiel werden zwei Ressourcen verwendet:

eine Bohrmaschine
ein Bohrer

Erst wenn beide Ressourcen verfügbar sind, kann ein Loch in die Wand gebohrt werden.

Ablauf ohne Deadlock

Fordern alle Tasks die Ressourcen immer in derselben Reihenfolge an, funktioniert das System problemlos.

zuerst Bohrmaschine reservieren
danach Bohrer reservieren
Loch bohren
beide Ressourcen freigeben

Da beide Tasks dieselbe Reihenfolge verwenden, entsteht keine gegenseitige Blockierung.

Deadlock (Verklemmung)

Ein Deadlock entsteht, wenn mehrere Tasks dauerhaft aufeinander warten. Im Bohrer-Beispiel entsteht dies durch unterschiedliche Zugriffsreihenfolgen.

Task A
reserviert Bohrmaschine
wartet auf Bohrer
Task B
reserviert Bohrer
wartet auf Bohrmaschine

Nun besitzt jeder Task bereits eine Ressource und wartet gleichzeitig auf die jeweils andere Ressource.

Zyklisches Warten

Keiner der beiden Tasks kann fortfahren:

Task A gibt die Bohrmaschine nicht frei, solange der Bohrer fehlt.
Task B gibt den Bohrer nicht frei, solange die Bohrmaschine fehlt.

Das System befindet sich nun in einer dauerhaften Blockierung. Dieses Verhalten bezeichnet man als: Deadlock oder Verklemmung
 

Eigenschaften eines Deadlocks

Ein Deadlock besitzt typischerweise vier Bedingungen:

  1. Gegenseitiger Ausschluss: Ressourcen dürfen nur exklusiv genutzt werde
  2. Belegen und Warten: Ein Task hält Ressourcen und wartet auf weitere
  3. Keine erzwungene Freigabe: Ressourcen werden nicht automatisch entzogen
  4. Zyklisches Warten: Tasks warten gegenseitig aufeinander

Sind alle vier Bedingungen erfüllt, kann ein Deadlock entstehen.

Erkennen eines Deadlocks

Ein Deadlock zeigt sich häufig dadurch, dass: Tasks scheinbar „einfrieren“, keine neue Ausgabe mehr erscheint, LEDs nicht mehr reagieren, oder das System nicht mehr weiterarbeitet. Besonders problematisch ist, dass solche Fehler oft nur selten auftreten und schwer reproduzierbar sind. In Embedded-Systemen können Deadlocks kritische Folgen haben: Kommunikationsabbrüche, blockierte Steuerungen, eingefrorene Benutzeroberflächen, oder komplette Systemausfälle. 

Vermeidung von Deadlocks

Eine der wichtigsten Strategien zur Vermeidung von Deadlocks ist eine feste Reihenfolge beim Zugriff auf Ressourcen. Alle Tasks müssen Ressourcen immer identisch anfordern

zuerst Bohrmaschine, danach Bohrer.

Dadurch kann kein zyklisches Warten entstehen. Weitere Strategien sind: Zeitüberschreitungen (Timeouts), zentrale Ressourcenverwaltung, Vermeidung unnötiger Ressourcensperren, möglichst kurze kritische Abschnitte

Durchführung

In einem ersten Versuch werden wir beide Tasks die Ressourcen in der gleichen Reihenfolge anfordern lassen und die Ausgabe auf dem seriellen Monitor beobachten. 

Nun werden wir in Task 2 die Reihenfolge der Ressourcen ändern, also Bohrer- und Bohrmaschinenzugriff vertauschen. Das Resultat ist abhängig vom Timing, d.h. wann welcher Task tatsächlich zum laufen kommt. Aber schon nach kurzer Zeit stellt sich ein Deadlock ein. Die beiden Tasks blockieren sich gegenseitig, jeder wartet auf die Freigabe der Ressource durch den anderen.

Fazit

Mit zunehmender Komplexität moderner Embedded-Systeme steigt auch die Anzahl paralleler Prozesse. Dadurch wächst die Gefahr von Synchronisationsproblemen erheblich.

Das Verständnis von:

  • Ressourcenschutz,
  • Semaphoren,
  • Race Conditions,
  • sowie Deadlocks

ist deshalb eine grundlegende Voraussetzung für die Entwicklung stabiler und zuverlässiger IoT- und Embedded-Anwendungen.

 

FreeRTOSTM is a trademark of Amazon.com, Inc. or its affiliates.

back-to-top nach oben