RTOS: Warum brauchen wir spezielle Post-Versionen für ISRs?

Wenn Sie in einigen RT-Kerneln an ein Semaphor oder Flag von einer ISR posten möchten, müssen Sie eine spezielle Version der Methode anstelle der regulären aufrufen, die von einer normalen Aufgabe aufgerufen wird. Beispiele:

Es gibt jedoch andere Kernel, bei denen dies nicht erforderlich ist. Zum Beispiel:

  • OSSemPost()in uC/OS kann sowohl von Tasks als auch von ISRs aufgerufen werden.
  • release()in mbed-rtos, kann von Threads und ISRs aufgerufen werden.
  • semGive()in VxWorks scheint laut Dokumentation von einem ISR aus aufrufbar zu sein, und auf den ersten Blick existiert keine spezielle Methode für ISRs.

Warum sollten Sie spezielle Versionen erstellen? Ich verstehe, dass das Setzen eines Semaphors oder einer Flagge dazu führen kann, dass eine blockierte Aufgabe mit höherer Priorität geweckt wird. Und deshalb wird der Scheduler höchstwahrscheinlich ausgeführt, wenn der ISR-Exit-Callback aufgerufen wird (ich spreche von den Funktionen, die aufgerufen werden müssen, wenn Sie Kernel-Code innerhalb der ISR verwenden möchten, wie portEXIT_SWITCHING_ISR()in FreeRTOS oder OSIntExitin uC/OS ). Warum also die spezielle Post-Methode zusätzlich zum ISRexit-Aufruf?

Antworten (2)

Ich habe den Quellcode von FreeRTOS und CoOS gelesen.

In FreeRTOS (dem Port, den ich zuerst in Google gefunden habe) xSemaphoreGiveFromISR()ruft die Funktion auf xQueueGenericSendFromISR()und diese Funktion hatte diesen Kommentar:

/* Similar to xQueueGenericSend, except we don't block if there is no room
    in the queue.  Also we don't directly wake a task that was blocked on a
    queue read, instead we return a flag to say whether a context switch is
    required or not (i.e. has a task with a higher priority than us been woken
    by this post). */

Wie bei CoOS stellt die isr_PostSemvon einem ISR aufgerufene Funktion einfach eine Anfrage in die sogenannte "Service Request Queue", indem sie die Funktion verwendet InsertInSRQ. Ich habe keine Ahnung, was diese Warteschlange ist, wahrscheinlich ist es eine Systemaufgabe, um Arbeit von ISRs abzuladen und so Interrupts schnell abschließen zu lassen.

Was mbed betrifft, das nur über eine Version der Methode verfügt, basiert es auf CMSIS RTOS, und es gibt einen Aufruf der Funktion osSemaphoreRelease(). Es gibt einen Kommentar zu dieser und ähnlichen Funktionen:

The following CMSIS-RTOS functions can be called from threads and interrupt
service routines (ISR):
  - osSignalSet
  - osSemaphoreRelease
  - osPoolAlloc, osPoolCAlloc, osPoolFree
  - osMessagePut, osMessageGet
  - osMailAlloc, osMailCAlloc, osMailGet, osMailPut, osMailFree

Functions that cannot be called from an ISR are verifying the interrupt
status and return in case that they are called from an ISR context the status
code osErrorISR. In some implementations this condition might be caught using
the HARD FAULT vector.

Es scheint also nur Kernel-Design zu sein, einige Designer entschieden sich für separate Funktionen und überließen es dem Programmierer, die Unterscheidung zu treffen, und einige andere zogen es vor, nur eine Methode zu haben und intern zu prüfen, ob der Aufruf von einem ISR erfolgte.

+1 für das tatsächliche Finden der Quelle und der Kommentare, die erklären, was der Unterschied ist und warum!

Auf vielen kleineren Mikrocontroller-Architekturen gibt es keinen echten Datenstapel für automatische Variablen. Stattdessen werden solche Variablen statisch zugewiesen, wobei eine Analysefunktion verwendet wird, die den gesamten "Baum" von Funktionsaufrufen berücksichtigt, um sicherzustellen, dass (die meisten) der Funktionalität eines Stacks erhalten bleiben.

Daraus folgt, dass einzelne Funktionen zumindest nicht ohne großen Overhead ablaufinvariant sein können. Wenn Sie versucht haben, eine Funktion innerhalb einer ISR aufzurufen, könnten Sie dieselbe Funktion unterbrechen, die vom Hauptleitungscode aufgerufen wurde. Im Wesentlichen hat jede ISR ihren eigenen "Baum" von Funktionsaufrufen (normalerweise ein sehr kleiner Baum), und alle diese Bäume müssen hinsichtlich des von ihnen verwendeten Daten-RAM physikalisch verschieden sein.

Daher muss jede Funktion dem einen oder anderen Baum zugewiesen werden, und Sie benötigen separate Kopien von Funktionen, die möglicherweise von mehreren Bäumen aufgerufen werden.

Ich denke, Sie werden feststellen, dass RTOS-Implementierungen mit separaten Funktionen auf diese kleineren Prozessorarchitekturen ausgerichtet sind, während diejenigen, die dies nicht tun, vollständig auf Architekturen mit echten Datenstapeln ausgerichtet sind.

Guter Punkt, aber ich glaube nicht, dass dies das Problem ist, da die meisten RTOS für 32-Bit-Mikrocontroller ausgelegt sind. Ich kann nichts über einige FreeRTOS-Ports sagen, aber ich bin sicher, dass ich rekursive Funktionen auf CoOS aufgerufen habe (bis mir der Stapel ausgegangen ist, lol).
„Die meisten“ ist nicht „alle“. Ich weiß, dass einige dieser RTOS Ports zu 8-Bit-PICs und 8051s haben, die zwei Beispielarchitekturen sind, die keine echten Datenstapel haben. Möglicherweise haben Sie CoOS auf einer Architektur (und einem Compiler) verwendet, die diese Einschränkung nicht hatte.