Die ganze Wahrheit über RTOS. Artikel Nr. 13. Aufgabendatenstrukturen und nicht unterstützte API-Aufrufe



In diesem dritten und letzten Aufgabenartikel werde ich die Nucleus SE-Datenstrukturen untersuchen und RTOS-API-Aufrufe beschreiben, die nicht in Nucleus SE implementiert sind, sowie andere Kompatibilitätsprobleme.

Frühere Artikel in der Reihe:
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.

Datenstrukturen


Aufgaben verwenden verschiedene Datenstrukturen (sowohl im RAM als auch im ROM), die wie andere Nucleus SE-Objekte eine Reihe von Tabellen sind, deren Größe der Anzahl der ausgewählten Aufgaben und Parameter entspricht.

Ich empfehle dringend, dass Anwendungscode über API-Funktionen und nicht direkt auf diese Datenstrukturen zugreift. Dies vermeidet unerwünschte Nebenwirkungen, Inkompatibilität mit zukünftigen Versionen von Nucleus SE und vereinfacht auch die Portierung der Anwendung auf Nucleus RTOS. Zum besseren Verständnis der Funktionsweise des Serviceabrufcodes und des Debugging-Prozesses wird nachfolgend eine detaillierte Beschreibung der Datenstrukturen gegeben.

Im RAM gehostete Kernel-Datenstrukturen


Diese Datenstrukturen umfassen:

NUSE_Task_Context [] [] - Ein zweidimensionales Array vom Typ ADDR enthält eine Zeile für jede Aufgabe. Die Anzahl der Spalten hängt von der Controller-Architektur ab und wird durch das Symbol NUSE_REGISTERS bestimmt , das in nuse_types.h definiert ist . Dieses Array wird vom Scheduler zum Speichern des Kontexts jeder Aufgabe verwendet und wurde im Abschnitt "Speichern des Kontexts" von Artikel 10 ausführlich beschrieben. Wird nicht erstellt, wenn der RTC-Scheduler verwendet wird.
NUSE_Task_Signal_Flags [] - Ein Array vom Typ U8 , das erstellt wird, wenn Signale aktiviert sind, und 8 Signalflags für jede Aufgabe enthält. Signale werden in einem der folgenden Artikel behandelt.
NUSE_Task_Timeout_Counter [] ist ein Array vom Typ U16 , besteht aus dem Subtrahieren von Zählern für jede Aufgabe und wird erstellt, wenn der Aufruf der API NUSE_Task_Sleep () aktiviert wird.
NUSE_Task_Status [] - Ein Array vom Typ U8 enthält die Status jeder Aufgabe - NUSE_READY oder Suspend-Status. Wird nur erstellt, wenn die Task-Suspendierung aktiviert ist.
NUSE_Task_Blocking_Return [] - Ein Array vom Typ U8, das erstellt wird, wenn die Blockierung von API-Aufrufen aktiviert ist. Es enthält einen Rückkehrcode, der nach dem Blockieren von API-Aufrufen verwendet wird. Es enthält normalerweise NUSE_SUCCESS oder einen Code, der angibt, dass das Objekt zurückgesetzt wurde (z. B. NUSE_MAILBOX_WAS_RESET ).
NUSE_Task_Schedule_Count [] - Ein Array vom Typ U16 , enthält einen Zähler für jede Aufgabe und wird nur erstellt, wenn die Anzahl der Scheduler aktiviert wurde.

NUSE_Task_Context [] [] wird hauptsächlich durch Nullen initialisiert, mit Ausnahme von Einträgen, die dem Statusregister (Statusregister, SR), dem Programmzähler (Programmzähler, PC) und dem Stapelzeiger (Stapelzeiger, SP) entsprechen, denen Anfangswerte zugewiesen sind (siehe "Daten" im ROM "unten) und allen anderen Datenstrukturen NUSE_Init_Task () werden beim Starten von Nucleus SE Nullen zugewiesen. Einer der folgenden Artikel enthält eine vollständige Liste der Nucleus SE-Startverfahren mit ihrer Beschreibung.

Im Folgenden finden Sie Definitionen der Datenstrukturen, die in der Datei nuse_init.c enthalten sind.



RAM-Benutzerdaten


Der Benutzer muss für jede Aufgabe einen Stapel definieren (wenn der RTC-Scheduler nicht verwendet wird). Dies sollten ADDR- Arrays sein, die normalerweise in nuse_config.c definiert sind . Adressen und Stapelgrößen sollten in den Aufgabeneinträgen NUSE_Task_Stack_Base [] bzw. NUSE_Task_Stack_Size [] platziert werden (siehe Daten im ROM).

ROM-Daten


Ein ROM speichert eine bis vier Datenstrukturen, die sich auf Aufgaben beziehen. Die genaue Menge hängt von den ausgewählten Parametern ab:

NUSE_Task_Start_Address [] ist ein Array vom Typ ADDR mit einem Eintrag für jede Aufgabe, der ein Zeiger auf den Codeeintrittspunkt für die Aufgabe ist.
NUSE_Task_Stack_Base [] ist ein Array vom Typ ADDR mit einem Eintrag für jede Aufgabe, der ein Zeiger auf die Basisadresse des Stapels für die Aufgabe ist. Dieses Array wird erstellt, wenn ein anderer Scheduler als RTC verwendet wird.
NUSE_Task_Stack_Size [] ist ein Array vom Typ U16 mit einem Eintrag für jede Aufgabe, der die Größe des Stapels für die Aufgabe (in Worten) angibt . Dieses Array wird erstellt, wenn ein anderer Scheduler als RTC verwendet wird.
NUSE_Task_Initial_State [] ist ein Array vom Typ U8 mit einem Eintrag für jede Aufgabe, der den Anfangszustand der Aufgabe anzeigt. Dies kann NUSE_READY oder NUSE_PURE_SUSPEND sein . Dieses Array wird erstellt, wenn die Unterstützung für den Anfangszustand der Aufgabe ausgewählt ist.

Diese Datenstrukturen werden in nuse_config.c deklariert und (statisch) initialisiert :



Die Speichermenge zum Speichern von Aufgabendaten (Task Data Footprint)


Wie bei allen Nucleus SE-Kernobjekten ist die zum Speichern von Daten erforderliche Speichermenge vorhersehbar.

ROM-Größe (in Byte) für alle Anwendungsaufgaben erforderlich:
NUSE_TASK_NUMBER * sizeof (ADDR)

Wenn ein anderer Scheduler als RTC ausgewählt ist:
NUSE_TASK_NUMBER * (Größe von (ADDR) +2)

Wenn die Unterstützung für den Anfangszustand der Aufgabe ausgewählt ist:
NUSE_TASK_NUMBER

Um Daten im RAM zu speichern, wird die Speichermenge (in Bytes) durch die ausgewählten Parameter bestimmt und kann einen Nullwert haben, wenn keiner der Parameter ausgewählt ist.
Wenn ein anderer Scheduler als RTC ausgewählt ist:
NUSE_TASK_NUMBER * NUSE-REGISTER * sizeof (ADDR)

Plus, wenn Signalunterstützung ausgewählt ist:
NUSE_TASK_NUMBER

Wenn der Aufruf der API NUSE_Task_Sleep () aktiviert ist:
NUSE_TASK_NUMBER * 2

Plus, wenn die Aufgabensperre aktiviert ist:
NUSE_TASK_NUMBER

Wenn die Blockierung von API-Aufrufen aktiviert ist:
NUSE_TASK_NUMBER

Plus, wenn der Scheduler-Zähler aktiviert ist:
NUSE_TASK_NUMBER * 2

API-Aufrufe in Nucleus SE nicht implementiert


Nachfolgend sind sieben API-Aufrufe aufgeführt, die in Nucleus RTOS verfügbar sind und nicht in Nucleus SE implementiert sind.

Aufgabe erstellen


Dieser API-Aufruf erstellt eine Anwendungsaufgabe. Nucleus SE benötigt diese Funktion nicht, da Aufgaben statisch erstellt werden.

Prototyp aufrufen:

STATUS NU_Create_Task (NU_TASK * task, CHAR * name, VOID (* task_entry) (UNSIGNED, VOID *), UNSIGNED argc, VOID * argv, VOID * stack_address, UNSIGNED stack_size, OPTION priority, UNSIGNED time_slice, OPTION priority, UNSIGNED time_slice

Parameter:

task - Ein Zeiger auf einen Steuerblock für Benutzeraufgaben, der als Handle / Link („Handle“) einer Task in anderen API-Aufrufen verwendet werden kann.
name - Zeiger auf den Namen der Aufgabe, eine 7-stellige Zeichenfolge mit einer abschließenden Null;
task_entry - gibt die Eingabefunktion für die Aufgabe an;
argc - UNSIGNED- Datenelement, mit dem Anfangsinformationen an die Aufgabe übergeben werden können;
argv - ein Zeiger, mit dem Informationen an die Aufgabe übertragen werden können;
stack_address - legt den Anfangssektor des Speichers für den Taskstack fest ;
stack_size - gibt die Anzahl der Bytes im Stapel an;
Priorität - gibt den Prioritätswert der Aufgabe an: von 0 bis 255, wobei niedrigere Zahlen der höchsten Priorität entsprechen;
time_slice - Gibt die maximale Anzahl von Zeitscheiben an , die während dieser Aufgabe vergehen können. Der Wert "0" deaktiviert das Time Slicing für diese Aufgabe.
preempt - Gibt an, ob die Aufgabe ersetzt wurde oder nicht. Kann Werte NU_PREEMPT und NU_NO_PREEMPT haben ;
auto_start - Zeigt den Anfangszustand der Aufgabe an. NU_START bedeutet, dass die Aufgabe zur Ausführung bereit ist, und NU_NO_START bedeutet, dass die Aufgabe angehalten ist.

Rückgabewert:

NU_SUCCESS - zeigt einen erfolgreichen Abschluss des Dienstes an;
NU_INVALID_TASK - gibt an, dass der Zeiger auf die Tasksteuereinheit NULL ist ;
NU_INVALID_ENTRY - Gibt an, dass der Zeiger auf die Eingabefunktion der Task NULL ist .
NU_INVALID_MEMORY - Gibt an, dass der durch den Parameter stack_address zugewiesene Speichersektor Null ( NULL ) ist.
NU_INVALID_SIZE - Gibt an, dass die angegebene Stapelgröße nicht ausreicht.
NU_INVALID_PREEMPT - Gibt an, dass der Preempt- Parameter falsch eingestellt ist.
NU_INVALID_START - Zeigt an, dass der Parameter auto_start falsch eingestellt ist.

Aufgabe löschen


Dieser API-Aufruf löscht eine zuvor erstellte Anwendungsaufgabe, die abgeschlossen oder beendet werden muss. Dieser Aufruf ist auch für Nucleus SE nicht erforderlich, da Aufgaben statisch erstellt werden und nicht gelöscht werden können.

Prototyp aufrufen:

STATUS NU_Delete_Task (NU_TASK * Aufgabe);

Parameter:

task - Zeiger auf den Task Control Block

Rückgabewert:

NU_SUCCESS - zeigt einen erfolgreichen Abschluss des Dienstes an;
NU_INVALID_TASK - zeigt an, dass der Zeiger auf die Aufgabe falsch gesetzt ist;
NU_INVALID_DELETE - Zeigt an, dass sich die Aufgabe nicht im Status "Fertig" oder "Beendet" befindet.

Aufgabenzeiger abrufen


Dieser API-Aufruf enthält eine sequentielle Liste von Zeigern auf alle Aufgaben im System. Es wird in Nucleus SE nicht benötigt, da Aufgaben anhand eines einfachen Index und nicht anhand eines Zeigers identifiziert werden.

Prototyp aufrufen:

UNSIGNED NU_Task_Pointers (NU_TASK ** Zeigerliste, UNSIGNED Maximum_Pointers);

Parameter:

pointer_list - Zeiger auf ein Array von NU_TASK- Zeigern. Dieses Array wird mit Zeigern auf die im System installierten Aufgaben gefüllt.
Maximum_Pointers - Die maximale Anzahl von Zeigern, die im Array platziert werden können.

Rückgabewert:

Die Anzahl der im Array platzierten NU_TASK- Zeiger.

Aufgabenpriorität ändern


Dieser API-Aufruf gibt der Aufgabe eine neue Priorität. In Nucleus SE ist dies nicht erforderlich, da die Aufgabenprioritäten konstant sind.

Prototyp aufrufen:

OPTION NU_Change_Priority (Task NU_TASK *, OPTION new_priority);

Parameter:

task - ein Zeiger auf einen Task-Steuerblock;
new_priority - Setzt die Priorität von 0 auf 255.

Rückgabewert:
Der vorherige Wert für die Aufgabenpriorität.

Ändern Sie den Task Preemption-Algorithmus


Dieser API-Aufruf ändert die Reihenfolge, in der die laufende Aufgabe verdrängt wird. Nucleus SE benötigt es nicht, da es einen einfacheren Planungsalgorithmus verwendet.

Prototyp aufrufen:
OPTION NU_Change_Preemption (OPTION preempt);

Parameter:
preempt - neuer präemptiver Algorithmus, akzeptiert NU_PREEMPT oder NU_NO_PREEMPT

Rückgabewert:
Der vorherige Algorithmus zum Verdrängen einer Aufgabe.

Task Time Slice ändern


Dieser API-Aufruf ändert die Zeitscheibe einer bestimmten Aufgabe. Nucleus SE benötigt es nicht, da Task-Zeitscheiben festgelegt sind.

Prototyp aufrufen:
UNSIGNED NU_Change_Time_Slice (NU_TASK * -Aufgabe, UNSIGNED time_slice);

Parameter:
task - ein Zeiger auf einen Task-Steuerblock;
time_slice - Die maximale Anzahl von Zeitscheiben , die während dieser Aufgabe vergehen können. Ein Nullwert dieses Felds deaktiviert die Zeitquantisierung für diese Aufgabe.

Rückgabewert:
Der vorherige Wert des Task-Zeit-Quanten.

Aufgabe beenden


Dieser API-Aufruf führt eine bestimmte Aufgabe aus. Nucleus SE benötigt dies nicht, da der Status " Beendet" nicht unterstützt wird.

Prototyp aufrufen:
STATUS NU_Terminate_Task (NU_TASK * -Aufgabe);

Parameter:
task - ein Zeiger auf einen Task-Steuerblock.

Rückgabewert:
NU_SUCCESS - zeigt einen erfolgreichen Abschluss des Dienstes an;
NU_INVALID_TASK - Zeigt an, dass der Taskzeiger falsch ist.

Nucleus RTOS-kompatibel


Bei der Entwicklung von Nucleus SE bestand eines der Hauptziele darin, ein hohes Maß an Codekompatibilität mit Nucleus RTOS sicherzustellen. Aufgaben sind keine Ausnahme und werden aus Anwendersicht ähnlich wie in Nucleus RTOS implementiert. Es gibt einige inkompatible Bereiche, in denen ich zu dem Schluss gekommen bin, dass eine solche Inkompatibilität akzeptabel wäre, da der endgültige Code leichter zu verstehen ist und den Speicher effizienter nutzen kann. Zusätzlich zu diesen Inkompatibilitäten können die restlichen Nucleus RTOS-API-Aufrufe jedoch fast direkt als Nucleus SE-Aufrufe verwendet werden. In einem der folgenden Artikel werden weitere Einzelheiten zum Übergang von Nucleus RTOS zu Nucleus SE beschrieben

Objektkennungen


In Nucleus RTOS werden alle Objekte durch eine Datenstruktur (Steuereinheiten) beschrieben, die von einem bestimmten Typ sind. Ein Zeiger auf diese Steuereinheit dient als Kennung für die Aufgabe. Bei Nucleus SE entschied ich, dass ein anderer Ansatz für die effiziente Nutzung des Speichers erforderlich ist. Alle Kernelobjekte werden durch eine Reihe von Tabellen im RAM und / oder ROM beschrieben. Die Größe dieser Tabellen wird durch die Anzahl der Objekttypen bestimmt. Die Kennung eines bestimmten Objekts ist der Index in diesen Tabellen. Also habe ich NUSE_TASK als das Äquivalent von U8 definiert . Eine Variable dieses Typs (kein Zeiger) dient als Kennung für Aufgaben. Dies ist eine kleine Inkompatibilität, die leicht herauszufinden ist, ob der Code auf oder von Nucleus RTOS portiert ist. Objektkennungen werden normalerweise unverändert gespeichert und übertragen.

Nucleus RTOS unterstützt auch die Benennung von Aufgaben. Diese Namen werden nur zum Debuggen verwendet. Ich habe sie von Nucleus SE ausgeschlossen, um Speicherplatz zu sparen.

Aufgabenzustände


In Nucleus RTOS können sich Aufgaben in einem von mehreren Zuständen befinden: Ausführen , Bereit , Angehalten (was zu Unsicherheit führt: Die Aufgabe befindet sich im Standby-Modus oder wird durch einen API-Aufruf blockiert), Beendet oder Abgeschlossen.

Nucleus SE unterstützt auch Ausführungs- und Bereitschaftszustände . Alle drei angehaltenen Optionen werden optional unterstützt. Beendet und beendet werden nicht unterstützt. Keine API-Aufrufe zum Abschließen von Aufgaben. Eine externe Taskfunktion sollte niemals explizit oder implizit einen Wert zurückgeben (dies führt zu einem Status " Fertig" in Nucleus RTOS).

Nicht realisierte API-Aufrufe


Nucleus RTOS unterstützt 16 Büroanrufe für die Arbeit mit Aufgaben. Davon sind 7 nicht in Nucleus SE implementiert. Ihre Beschreibung sowie der Grund für ihren Ausschluss sind oben beschrieben.

Im nächsten Artikel beginnen wir mit der RTOS-Speicherverwaltung.

Ü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/de425353/


All Articles