Multitask-Datenzugriffs-Synchronisation in der SPS
Wenn von mehreren Tasks auf dieselben Daten zugegriffen wird, kann es je nach Task‑/Echtzeitkonfiguration vorkommen, dass die Tasks gleichzeitig auf dieselben Daten zugreifen. Wenn die Daten dabei von mindestens einer der Tasks geschrieben werden, können die Daten während oder nach einer Änderung einen inkonsistenten Zustand haben. Um dies zu verhindern, müssen alle konkurrierenden Zugriffe synchronisiert werden, sodass zu einem Zeitpunkt nur von höchstens einer Task auf die gemeinsam genutzten Daten zugegriffen werden kann.
Zu diesen konkurrierenden Zugriffen aus mehreren Tasks, bei denen eine Synchronisation nötig ist, gehören beispielsweise die folgenden Fälle:
- Direkter Zugriff auf globale oder andere nicht-temporäre Variablen, beispielsweise mittels Operatoren
- Indirekter Zugriff auf globale oder andere nicht-temporäre Variablen, beispielsweise innerhalb von Funktionen, Methoden oder anderen POU-Aufrufen (z. B. besonders häufig, wenn eine Funktionsbausteininstanz global instanziiert ist)
Kurz: Wenn von mehreren Tasks auf dieselben Daten zugegriffen wird und bei mindestens einem dieser Zugriffe die Daten geschrieben werden, müssen alle lesenden und schreibenden Zugriffe synchronisiert werden. Dies gilt unabhängig davon, ob die Tasks auf einem oder mehreren CPU Kernen laufen.
WARNUNG | |
Verletzungsgefahr durch unvorhersehbare Achsbewegungen Werden konkurrierende Zugriffe nicht synchronisiert, so besteht die Gefahr eines inkonsistenten oder ungültigen Datensatzes. Je nachdem wie die Daten im weiteren Programmverlauf genutzt werden, kann dies ein Fehlverhalten des Programms, eine ungewünschte Achsbewegung oder auch den plötzlichen Programmstillstand zur Folge haben. Abhängig von der gesteuerten Anlage können Schäden an Anlage und Werkstücken entstehen oder Gesundheit und Leben von Personen gefährdet werden.
|
Synchronisationsmöglichkeiten
Zur Synchronisation der Zugriffe stehen u. a. die folgenden Möglichkeiten zur Verfügung:
- Mutex-Verfahren (TestAndSet, FB_IecCriticalSection) zum Absichern von kritischen Bereichen
- Die Anzahl der kritischen Bereiche ist immer möglichst klein zu halten.
- Die Länge der kritischen Bereiche ist kurz zu halten.
- Bei vergleichsweise kurzen kritischen Bereichen empfiehlt sich meist die Verwendung von FB_IecCriticalSection.
- Bei vergleichsweise langen kritischen Bereichen empfiehlt sich meist die Verwendung von TestAndSet.
- Austausch von Daten über das SPS-Prozessabbild
- Diese Variante ist nur möglich und empfiehlt sich, wenn nur von einer Task schreibend auf dieselben Daten zugegriffen wird.
- Aufgrund von notwendigen internen Kopieraktionen ist die mögliche Datenmenge eingeschränkt. Weitere Informationen hierzu entnehmen Sie bitte der Beschreibung „Austausch von Daten über das SPS-Prozessabbild“.
- Austausch von Daten über synchronisierte Puffer
- Diese Variante ist nur möglich, wenn nur von einer Task schreibend auf dieselben Daten zugegriffen wird.
- Hierbei handelt es sich um eine anwendereigene Implementierung, für die es unterschiedliche Möglichkeiten gibt.
- Der Zugriff auf die einzelnen Puffer muss, beispielsweise mit TestAndSet(), abgesichert werden.
- Aufgrund von durchgeführten Kopieraktionen ist die mögliche Datenmenge eingeschränkt. Weitere Informationen hierzu entnehmen Sie bitte der Beschreibung „Austausch von Daten übersynchronisierte Puffer“.
Synchronisation auch bei atomarem Zugriff
Die Notwendigkeit zur Synchronisation gilt normalerweise selbst dann, wenn ein einzelner Zugriff auf eine Variable (z. B. Integer schreiben) als atomar, d. h. ununterbrechbar, bezeichnet werden könnte.
Weil die Eigenschaft des atomaren Zugriffs u. a. von der eingesetzten Prozessorarchitektur abhängig ist, sollte der Einfachheit halber sowie zur Sicherheit jeder Zugriff als nicht-atomar betrachtet werden.
Zu beachten ist auch, dass sich selbst vermeintlich sichere Zugriffe bei näherer Betrachtung fast immer als unsicher herausstellen. Dies wird im Folgenden mithilfe von zwei Szenarien exemplarisch dargestellt:
- I.d.R. ist mehr als ein atomarer Zugriff für den gewünschten Funktionsausdruck erforderlich (z. B. Lesen, Ändern, Schreiben). Wenn ein solch mehrteiliger Funktionsausdruck in mehreren Tasks vorhanden ist, kann eine gleichzeitige Ausführung auf mehreren Tasks zu einem anderen Gesamtergebnis führen als eine sequenzielle Abarbeitung der Ausdrücke.
- Beispiel: Eine globale Zählvariable (initialisiert mit 0) soll jeweils (aus zwei Tasks heraus) um 1 inkrementiert werden (
nGlobal := nGlobal + 1;
). Wird die Inkrementierung gleichzeitig ausgeführt, wird beide Male0 + 1 = 1
gerechnet und der resultierende Wert der globalen Variablen ist 1, obwohl der Wert 2 beabsichtigt wäre. - Mehrere Lesezugriffe zu unterschiedlichen Zeitpunkten innerhalb einer Taskabarbeitung können unterschiedliche Variablenwerte liefern.
- Beispiel: Eine globale Variable wird aus einer Task heraus geschrieben. Ihr Wert war zuvor 50 und wird nun gleich 0 geschrieben (
nGlobal := 0;
). In einem anderen Taskkontext wird der Wert der globalen Variablen abgefragt (IF nGlobal>10 AND nGlobal<20 THEN
). Die Abfrage beinhaltet zwei Lesezugriffe. Findet zwischen diesen Lesezugriffen der obige Schreibzugriff aus der anderen Task statt, so ist die Bedingung erfüllt, obwohl die globale Variable zu keinem Zeitpunkt einen Wert zwischen 10 und 20 hatte.
Weitere Hinweise
- Systemoperatoren, wie z. B. ADD, SIZEOF oder __NEW, können von mehreren Tasks gleichzeitig aufgerufen werden. Diese Aussage bezieht sich nur auf den verwendeten Operator. Wenn bei den Aufrufen auf Daten zugegriffen wird, die wiederum von mehreren Tasks aus genutzt werden, müssen diese Datenzugriffe entsprechend synchronisiert werden.
- Eine Funktionsbausteininstanz, die intern ADS nutzt, darf nicht in unterschiedlichen Tasks verwendet werden. Wenn die Instanz trotzdem aus einer anderen Task aufgerufen wird, wird der Fehler ADSERR_DEVICE_INVALIDCONTEXT=0x709=1801 ausgegeben.
- Aufgrund der Ausführung auf dem Stack ist die Verwendung der STRING-Funktionen (Tc2_Standard-Bibliothek) in unterschiedlichen Tasks in TwinCAT 3 unkritisch (in TwinCAT 2 sind die STRING-Funktionen nicht standardmäßig sicher bei Taskwechsel).
- Beachten Sie auch die Möglichkeiten der Compiler-Erweiterung (Dokumentation TE1200 TC3 PLC Static Analysis, die Regeln zum Thema „gleichzeitige/konkurrierende Zugriffe“ zur Verfügung stellt. Dies ist als Hilfsmittel zu sehen, mit dem potentieller Synchronisationsbedarf aufgedeckt werden kann.