Die ganze Wahrheit über RTOS. Artikel Nr. 19. Semaphoren: Einführung und Grundversorgung



Semaphoren wurden in einem der vorherigen Artikel (Nr. 5) erwähnt. Ihre Hauptaufgabe besteht darin, den Zugriff auf Ressourcen zu kontrollieren.

Frühere Artikel in der Reihe:
Artikel Nr. 18. Ereignisflag-Gruppen: Hilfsdienste und Datenstrukturen
Artikel Nr. 17. Ereignisflag-Gruppen: Einführung und Basisdienste
Artikel 16. Signale
Artikel Nr. 15. Speicherpartitionen: Dienste und Datenstrukturen
Artikel # 14. Speicherbereiche: Einführung und Grundversorgung
Artikel Nr. 13. Aufgabendatenstrukturen und nicht unterstützte API-Aufrufe
Artikel 12. Dienstleistungen für die Arbeit mit Aufgaben
Artikel 11. Aufgaben: Konfiguration und Einführung in die API
Artikel 10. Scheduler: Erweiterte Funktionen und Kontexterhaltung
Artikel 9. Scheduler: Implementierung
Artikel 8. Nucleus SE: Internes Design und Bereitstellung
Artikel 7. Nucleus SE: Einführung
Artikel 6. Andere RTOS-Dienste
Artikel 5. Aufgabeninteraktion und Synchronisation
Artikel 4. Aufgaben, Kontextwechsel und Interrupts
Artikel 3. Aufgaben und Planung
Artikel 2. RTOS: Struktur und Echtzeitmodus
Artikel 1. RTOS: Einführung.

Semaphoren verwenden


In Nucleus SE werden Semaphoren in der Assemblierungsphase definiert. Eine Anwendung kann bis zu 16 Semaphoren enthalten. Wenn keine Semaphoren angegeben sind, ist der Code für Serviceaufrufe und Datenstrukturen nicht in der Anwendung enthalten.

Ein Semaphor ist ein Zähler vom Typ U8 , dessen Zugriff so gesteuert wird, dass mehrere Aufgaben ihn verwenden können. Eine Aufgabe kann den Wert des Semaphorzählers verringern (erfassen) oder erhöhen (freigeben). Der Versuch, ein Semaphor mit dem Wert Null zu erfassen, kann abhängig von den ausgewählten API-Aufrufparametern und der Nucleus SE-Konfiguration zu einem Fehler oder einer Unterbrechung der Aufgabe führen.

Semaphore einrichten


Anzahl der Semaphoren


Wie bei den meisten Nucleus SE-Objekten wird die Einstellung von Semaphoren durch die Direktiven #define in nuse_config.h bestimmt . Der Hauptparameter ist NUSE_SEMAPHORE_NUMBER , der die Anzahl der Semaphoren in der Anwendung bestimmt. Standardmäßig ist der Parameter auf 0 gesetzt (Semaphoren werden in der Anwendung nicht verwendet) und kann einen beliebigen Wert bis 16 annehmen. Ein falscher Wert führt zu einem Kompilierungsfehler, der durch Einchecken in nuse_config_check.h generiert wird (diese Datei ist in nuse_config.c enthalten , dh sie wird kompiliert zusammen mit diesem Modul) wird daher die Direktive #error ausgelöst .

Die Auswahl eines Werts ungleich Null dient als Hauptaktivator für Semaphoren. Dieser Parameter wird beim Definieren von Datenstrukturen verwendet und ihre Größe hängt von ihrem Wert ab (weitere Einzelheiten finden Sie weiter unten in diesem Artikel). Darüber hinaus aktiviert ein Wert ungleich Null die API-Einstellungen.

API-Aufrufe aktivieren


Jede API-Funktion (Dienstprogrammaufruf) in Nucleus SE wird durch die Direktive #define in nuse_config.h aktiviert. Zu Semaphoren gehören:

NUSE_SEMAPHORE_OBTAIN
NUSE_SEMAPHORE_RELEASE
NUSE_SEMAPHORE_RESET
NUSE_SEMAPHORE_INFORMATION
NUSE_SEMAPHORE_COUNT


Standardmäßig sind sie auf FALSE gesetzt , wodurch jeder Serviceabruf deaktiviert und die Aufnahme von Code blockiert wird, der sie implementiert. Um Semaphoren einzurichten, müssen Sie die erforderlichen API-Aufrufe auswählen und die entsprechenden Anweisungen auf TRUE setzen .

Das Folgende ist ein Auszug aus der Standarddatei nuse_config.h .

 #define NUSE_SEMAPHORE_NUMBER 0 /* Number of semaphores in the system - 0-16 */ #define NUSE_SEMAPHORE_OBTAIN FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_RELEASE FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_RESET FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_INFORMATION FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_COUNT FALSE /* Service call enabler */ 

Eine aktivierte API-Funktion, wenn die Anwendung keine Semaphoren enthält, führt zu einem Kompilierungsfehler (mit Ausnahme von NUSE_Semaphore_Count () , das immer aktiviert ist). Wenn Ihr Code einen API-Aufruf verwendet, der nicht aktiviert wurde, tritt ein Layoutfehler auf, da der Implementierungscode nicht in der Anwendung enthalten war.

Utility-Semaphoraufrufe


Nucleus RTOS unterstützt acht Serviceaufrufe mit folgenden Funktionen:

  • Semaphor-Erfassung. Nucleus SE ist in der Funktion NUSE_Semaphore_Obtain () implementiert.
  • Semaphor freigeben. In Nucleus SE ist es in der Funktion NUSE_Semaphore_Release () implementiert.
  • Zurücksetzen des Semaphors in einen nicht verwendeten Zustand mit Freigabe aller angehaltenen Aufgaben (Neustart). Nucleus SE ist in NUSE_Semaphore_Reset () implementiert.
  • Bereitstellung von Informationen zu einem bestimmten Semaphor. Nucleus SE ist in NUSE_Semaphore_Information () implementiert.
  • Gibt die Anzahl der konfigurierten Semaphoren in der Anwendung zurück. Nucleus SE in NUSE_Semaphore_Count () implementiert.
  • Hinzufügen eines neuen Semaphors zur Anwendung. Nucleus SE ist nicht implementiert.
  • Semaphor aus der Anwendung entfernen. Nucleus SE ist nicht implementiert.
  • Rückgabe von Zeigern auf alle Semaphoren. Nucleus SE ist nicht implementiert.

Die Implementierung jedes Serviceabrufs wird nachstehend ausführlich beschrieben.

Dienstprogrammaufrufe zum Erfassen und Freigeben von Semaphoren


Die grundlegenden Operationen, die an Semaphoren ausgeführt werden können, sind das Erfassen und Freigeben (Verringern und Erhöhen des Werts). Nucleus RTOS und Nucleus SE bieten zwei grundlegende API-Aufrufe für diese Operationen.

Semaphor-Erfassung


Der Aufruf des Nucleus RTOS-Dienstprogramms zum Erfassen eines Semaphors ist sehr flexibel und ermöglicht es Ihnen, Aufgaben implizit oder mit einem bestimmten Zeitlimit anzuhalten, wenn der Vorgang derzeit nicht ausgeführt werden kann, z. B. wenn Sie versuchen, ein Semaphor mit einem Wert von Null zu erfassen. Nucleus SE bietet dieselben Funktionen, nur die Unterbrechung der Aufgabe ist optional und eine Zeitüberschreitung ist nicht implementiert.

Herausforderung, Semaphor in Nucleus RTOS zu erfassen
Prototyp eines Serviceabrufs:
STATUS NU_Obtain_Semaphore (NU_SEMAPHORE * -Semaphor, UNSIGNED suspend);

Parameter:

Semaphor - Zeiger auf den vom Benutzer bereitgestellten Semaphor-Steuerblock;
suspend - Task Suspension-Parameter kann die Werte NU_NO_SUSPEND oder NU_SUSPEND sowie den Timeout-Wert annehmen .

Rückgabewert:

NU_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NU_UNAVAILABLE - Das Semaphor hatte einen Nullwert.
NU_INVALID_SEMAPHORE - ungültiger Zeiger auf ein Semaphor;
NU_INVALID_SUSPEND - Versuch, von einem nicht aufgabenbezogenen Thread aus anzuhalten ;
NU_SEMAPHORE_WAS_RESET - Das Semaphor wurde zurückgesetzt, während die Aufgabe angehalten wurde.

Herausforderung, Semaphor in Nucleus SE zu erfassen
Dieser API-Aufruf unterstützt die Kernfunktionalität der Nucleus RTOS-API.

Prototyp eines Serviceabrufs:

STATUS NUSE_Semaphore_Obtain (NUSE_SEMAPHORE-Semaphor, U8-Suspend);

Parameter:

Semaphor - Index (ID) des verwendeten Semaphors;
suspend - Der Parameter für die Task-Suspendierung kann NUSE_NO_SUSPEND oder NUSE_SUSPEND sein .

Rückgabewert:

NUSE_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NUSE_UNAVAILABLE - Das Semaphor hatte einen Nullwert.
NUSE_INVALID_SEMAPHORE - ungültiger Semaphorindex;
NUSE_INVALID_SUSPEND - ein Versuch, von einem nicht aufgabenbezogenen Thread aus anzuhalten oder wenn die API-Blockierungsfunktion deaktiviert ist;
NUSE_SEMAPHORE_WAS_RESET - Das Semaphor wurde zurückgesetzt, während die Aufgabe angehalten wurde.

Implementierung der Semaphor-Erfassung in Nucleus SE
Die Codevariante der Funktion NUSE_Semaphore_Obtain () (nach Überprüfung der Parameter) wird mithilfe der bedingten Kompilierung ausgewählt, je nachdem, ob die Unterstützung für das Blockieren (Anhalten) von Aufgaben aktiviert ist oder nicht. Betrachten Sie beide Optionen.

Wenn die Sperre nicht aktiviert ist, ist die Logik dieses API-Aufrufs ziemlich einfach:

 if (NUSE_Semaphore_Counter[semaphore] != 0) /* semaphore available */ { NUSE_Semaphore_Counter[semaphore]--; return_value = NUSE_SUCCESS; } else /* semaphore unavailable */ { return_value = NUSE_UNAVAILABLE; } 

Der Wert des Semaphorzählers wird überprüft und nimmt ab, wenn er nicht gleich Null ist.

Wenn die Task-Sperre aktiviert ist, wird die Logik komplexer:

 do { if (NUSE_Semaphore_Counter[semaphore] != 0) /* semaphore available */ { NUSE_Semaphore_Counter[semaphore]--; return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } else /* semaphore unavailable */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_UNAVAILABLE; } else { /* block task */ NUSE_Semaphore_Blocking_Count[semaphore]++; NUSE_Suspend_Task(NUSE_Task_Active, semaphore << 4) | NUSE_SEMAPHORE_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } } while (suspend == NUSE_SUSPEND); 

Einige Erläuterungen können hilfreich sein.

Der Code wird in eine do ... while-Schleife gestellt , die ausgeführt wird, während der suspend- Parameter NUSE_SUSPEND lautet .

Wenn das Semaphor einen Wert ungleich Null hat, nimmt es ab. Die Suspend- Variable wird auf NUSE_NO_SUSPEND gesetzt , und der API-Aufruf wird beendet und gibt NUSE_SUCESS zurück .

Wenn das Semaphor null ist und die Suspend- Variable auf NUSE_NO_SUSPEND gesetzt ist , gibt der API-Aufruf NUSE_UNAVAILABLE zurück . Wenn die Aussetzung auf NUSE_SUSPEND gesetzt wurde , wird die Aufgabe angehalten . Wenn der Aufruf nach Abschluss des Aufrufs (z. B. wenn die Aufgabe fortgesetzt wird) NUSE_SUCCESS lautet (was darauf hinweist, dass die Aufgabe nach dem Freigeben des Semaphors und nicht nach dem Zurücksetzen wieder aufgenommen wurde), beginnt der Zyklus von vorne .

Semaphor-Veröffentlichung


Der Dienstprogrammaufruf an die Nucleus RTOS-API zum Freigeben des Semaphors ist recht einfach: Der Wert des Semaphorzählers steigt und eine Erfolgsmeldung wird zurückgegeben. Nucleus SE bietet dieselben Funktionen, jedoch mit zusätzlicher Überlaufprüfung.

Herausforderung, Semaphoren in Nucleus RTOS freizugeben
Prototyp eines Serviceabrufs:

STATUS NU_Release_Semaphore (NU_SEMAPHORE * -Semaphor);

Parameter:

Semaphor - Ein Zeiger auf einen vom Benutzer bereitgestellten Semaphor-Steuerblock.

Rückgabewert:

NU_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NU_INVALID_SEMAPHORE - Ungültiger Semaphorzeiger.

Herausforderung, Semaphor in Nucleus SE freizugeben
Dieser API-Aufruf unterstützt die Kernfunktionalität der Nucleus RTOS-API.

Prototyp eines Serviceabrufs:

STATUS NUSE_Semaphore_Release (NUSE_SEMAPHORE-Semaphor);

Parameter:

Semaphor - Der Index (ID) des freigegebenen Semaphors.

Rückgabewert:

NUSE_SUCCESS - Der Anruf wurde erfolgreich abgeschlossen.
NUSE_INVALID_SEMAPHORE - ungültiger Semaphorindex;
NUSE_UNAVAILABLE - Das Semaphor hat einen Wert von 255 und kann nicht erhöht werden.

Implementierung der Semaphorfreigabe in Nucleus SE
Der Funktionscode NUSE_Semaphore_Release () (nach Überprüfung der Parameter) ist üblich, unabhängig davon, ob die Task-Sperre aktiviert ist oder nicht. Der Wert des Semaphorzählers wird überprüft, und wenn er kleiner als 255 ist, erhöht er sich.

Weiterer Code wird mithilfe der bedingten Kompilierung ausgewählt, wenn die Unterstützung für API-Blockierungsaufrufe (Task-Suspendierung) aktiviert ist:

 NUSE_CS_Enter(); if (NUSE_Semaphore_Counter[semaphore] < 255) { NUSE_Semaphore_Counter[semaphore]++; return_value = NUSE_SUCCESS; #if NUSE_BLOCKING_ENABLE if (NUSE_Semaphore_Blocking_Count[semaphore] != 0) { U8 index; /* check whether a task is blocked */ /* on this semaphore */ NUSE_Semaphore_Blocking_Count[semaphore]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_SEMAPHORE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == semaphore)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } #endif } else { return_value = NUSE_UNAVAILABLE; } NUSE_CS_Exit(); return return_value; 

Wenn Aufgaben für dieses Semaphor angehalten werden, wird die erste fortgesetzt.

Der folgende Artikel beschreibt zusätzliche API-Aufrufe, die Semaphoren und ihren Datenstrukturen zugeordnet sind.

Über den Autor: Colin Walls ist seit über dreißig Jahren in der Elektronikindustrie tätig und widmet sich die meiste Zeit der Firmware. Heute ist er Firmware-Ingenieur bei Mentor Embedded (einer Abteilung von Mentor Graphics). Colin Walls spricht häufig auf Konferenzen und Seminaren, Autor zahlreicher technischer Artikel und zweier Bücher über Firmware. Lebt in Großbritannien. Colins professioneller Blog , E-Mail: colin_walls@mentor.com.

Source: https://habr.com/ru/post/de429156/


All Articles