Skip to content
On this page

10. RT Kernel

Linux Preemption Models

Linux lässt wie Windows ein so genannt "preemptives Multitasking" zu, d.h. der Scheduler kann ein Threads von Usersspace-Prozessen oder Kernel Threads an beliebigen Stellen unterbrechen und die betreffende CPU einem anderen Thread zur Verfügung stellen - also bevor der ursprüngliche Thread die CPU-Kontrolle "freiwillig" abgibt (engl. preempt = zuvorkommen).

Selbst bei Preemptive Multitasking ist aber nicht zwingend der Kernel selbst durchgehend preemptiv! Insbesondere in kritischen Codeabschnitten innerhalb eines Systemcalls oder z.B. in Kernel Threads. Um den CPU-Verwaltungsaufwand in Grenzen zu halten sperrt der Kernel deswegen an einigen kritischen Stellen mehr oder weniger lange ein anstehendes Rescheduling und/oder die Interrupt-Behandlung. Ein Kernel ist also "echtzeittauglicher", wenn er möglichst durchgehend (an allen Stellen) unterbrechbar ist.

Da eine Unterbrechung an kritischen Stellen jedoch ein erheblicher zusätzlicher Verwaltungsaufwand erfordert und damit zusätzliche CPU-Zeit, wird der gesamte Systemdurchsatz geringer, je besser der Betriebssystem-Kern auf Realtime optimiert wird. Die Kernel-Entwickler versucht deshalb einen guten Kompromiss einzugehen. Um hierbei einen gewissen Spielraum zu haben, kennt der Linux-Kernel die folgenden drei "Preemption Models":

CONFIG_...PreemptionEinsatzzweck
PREEMPT_NONENo Forced PreemptionServer (zwecks Optimierung des Gesamtdurchsatzes), weder Hard noch Soft-RealtimeServer (zwecks Optimierung des Gesamtdurchsatzes)
PREEMPT_VOLUNTARYVoluntary Kernel Preemption, moderates Soft-Realtime VerhaltenDesktop (Soft-Realtime für Audio-/Video nötig)
PREEMPTPreemptible Kernel,gutes Soft-Realtime Verhalten (aber immer noch kein Hard-Realtime)Low-Latency Desktop
PREEMPT_RTDieses Preemption Model ist nur nach Einspielen des "Linux Realtime Patches" verfügbar. Im Idealfall sollte man damit Hard-Realtime Fähigkeit erreichenNur anwenden wenn zwingend harte Echtzeitbedingungen eingehalten werden sollen!

Aber welche Stufe hat Buildroot beim Generieren des Linux-Kernels denn nun ausgewählt? Wir haben den Kernel bereits selber für das Raspberry Pi OS kompiliert. Dort ist auch die hierbei verwendete Kernel-Konfigurationsdatei .config einsehbar. Diese Datei kann folglich nach PREEMPT durchsucht werden mit:

bash
# Je nach location des Kernel Sources
grep PREEMPT ~/ebssd/linux-rpi/.config

(Wobei Zeilen beginnend mit # bloss Kommentarzeilen sind.) Der generierte Kernel verwendet also das Preemption Model ______________.

Um das Preemption Model zu ändern, könnte man die Kernel menuconfig bei aktivem buildroot Ordner per make linux-menuconfig öffnen, wonach unter General setup> Preemption Model ein anderes Preemption Model auswählbar wäre. Danach müsste Buildroot (resp. der Kernel) natürlich noch neu generiert und auf die SD-Karte "geflashed" werden.

Wir ersparen uns dies aber, denn Wunder darf man auch mit CONFIG_PREEMPT nicht erwarten, denn abgesehen von möglichst durchgehender Unterbrechbarkeit sind komplexe Hardware- und Software-Interrupt-Routinen in den Gerätetreibern ein weiteres Problem: bei grosser IO-Last z.B. aufgrund komplexer Protokolle und hohem Datendurchsatz treten auch dadurch lange Unterbrüche resp. Latenzzeiten in Anwendungen auf.

Beide Probleme versucht der Linux Realtime Patch (RT-Patch) möglichst kompromisslos zu lösen, also sowohl eine möglichst durchgehende Unterbrechbarkeit des Kernels als auch das Interrupt-Problem, indem die Inhalte der Interrupt-Routinen in normalen Kernel-Threads resp. sogenannte "Interrupt-Threads" abgearbeitet werden, sodass diese wie all Threads priorisierbar sind. Nur ganz kurze Interrupt-Routinen werden dann noch als solche ausgeführt, alle anderen aktivieren bloss noch den jeweiligen Interrupt-Thread.

Ein zeitkritischer Realtime-Thread einer Anwendung kann dadurch bei entsprechender Konfiguration eine höhere Priorität haben, wie die ursprünglichen Interrupt-Routinen! Natürlich bewirkten die "Interrupt-Threads" erheblichen Rescheduling-Aufwand und damit CPU-Zeitaufwand und die Zuweisung der Prioritäten ist ebenfalls recht kritisch: im ungünstigsten Fall wird die betreffende Peripherie (z.B. ein SD-Karten-Treiber oder der Treiber des Ethernet- oder WLAN-Interfaces) nicht mehr rechtzeitig behandelt wodurch sich die betreffende Peripherie unstabil verhält und/oder das System sogar in einen "Dead Lock" gerät.

Latenzzeit-Tests per "rt-tests"

Zuerst wollen wir jedoch die Raltime-Fähigkeit im Userspace auf unserem Zielsystem mit dem ungepatchten Kernels testen, wozu es das Open Source Projekt rt-tests gibt, welches einige Tools enthält.

Wechseln Sie auf dem Linux-Entwicklungssystem in den Ordner ~/ebssd/workspace/

  1. Und clonen Sie das rt-tests Projekt per:

    bash
    git clone https://git.kernel.org/pub/scm/utils/rt-tests/rt-tests.git/
    
  2. Nach erfolgreichem clonen wechseln Sie in den geclonten Ordner rt-tests .

  3. Vergewissern Sie sich mit git tag, welcher Release der neueste ist.

  4. Erstellen Sie einen lokalen Release basierend auf die dem tag:

    bash
    git checkout -b stable-v1.0 origin/stable/v1.0
    
  5. Für die Architektur des Entwicklungssystems könnten Sie rt-tests mit einem einfachen make builden. Da wir aber das rt-tests Projekt für unser Zielsystem Cross-compilieren möchten, geben Sie den zu verwendenden Cross-Compiler unmittelbar vor make über eine Umgebungsvariable CC an:

    bash
    CC=arm-none-linux-gnueabihf-gcc make NUMA=0
    

    Per Option NUMA=0 wird der NUMA-Support ausgeschaltet, andernfalls es einen Compilerfehler gibt, denn die ARM-32bit Architektur resp. die verwendete Toolchain scheint NUMA nicht zu unterstützen. NUMA steht für Non-Uniform Memory Access und hat das Ziel, bei einer grossen Anzahl CPUs den Bus-Zugriff zu optimieren.

    vgl. https://de.wikipedia.org/wiki/Non-Uniform_Memory_Access und http://lse.sourceforge.net/numa/faq/

  6. Nach kurzer Zeit sollte make erfolgreich beendet werden wonach ein ls einige Executables anzeigt.

  7. Für uns wichtig sind cyclictest sowie hackbench. Kopieren Sie beide per scp auf das Zielsystem z.B. in den Ordner /usr/bin/

  8. Führen Sie danach auf dem Zielsystem folgendes aus:

    bash
    cyclictest -S -m -p99 -l 10000 -q
    

    cyclictest startet damit für die Dauer von 10'000ms (also 10 Sekunden) auf jeder der 4 CPU-Cores einen Thread. Diese Threads wecken sich selbst periodisch in einem vordefinierten Intervall auf und ermitteln dann jeweils die Latenzzeit zwischen geplanter und der tatsächlicher Aufweckzeit. Interessant ist jeweils die maximale Latenzzeit über die Testdauer, also die letzte Zahl jeder Zeile. Diese Latenzzeiten sollte theoretisch unabhängig von sonstiger CPU-Last stets recht klein sein, denn mit der Angabe -p99 werden die Threads mit maximal möglicher Realtime-Priorität (99) gestartet. In welchem Bereich liegt die Maxima wenn Sie einige Durchläufe ausführen: ______________

  9. Um den cyclictest Command nicht immer manuell neu starten zu müssen, umschliessen Sie diesen in eine while Schleife, wobei nach jedem Durchlauf auch noch eine Sekunden gewartet werden soll:

    bash
    while true; do cyclictest -S -m -p99 -l 10000 -q ; sleep 1; done
    

    (Dank sleep 1 lässt sich die Endlosschleife mittels zweier kurz aufeinander folgenden Ctrl-C wieder unterbrechen: das erste Ctrl-C unterbricht den laufenden cyclictest und das 2. unterbricht den sleep Befehl und damit den Loop.)

    Es sollte nun alle 11s eine cyclictest Ausgabe erfolgen.

    (Natürlich könnten Sie diese Befehlszeile auch in ein Shell-Script packen.)

  10. Lassen Sie den cyclictest Loop laufen und loggen Sie sich über ein weiteres Terminal Window ein zweites mal per ssh auf das Zielsystem ein!

Mittels hackbench können Sie nun Prozessorlast generieren: hackbench startet dabei einige hundert Threads, welche sich dann gegenseitig kurze Datenpakete zusenden und damit eine hohe Rescheduling-Last und damit CPU-Last generieren.

  • Führen Sie also in der 2. ssh Session hackbench ein paarmal aus, wonach Sie in die erste ssh Session zurück wechseln und auf das Ergebnis der laufenden cyclictest Ausgabe warten. Welchen Einfluss hatte hackbench auf die maximale Latenzzeiten? ______________ (Vermutlich einen recht hohen!)
  • Wechseln Sie nun nochmals zurück in den die 2. ssh Session, in welcher hackbench lief. Wie lange dauerte die Ausführung von hackbench in etwa jeweils? (wurde auch ausgegeben): ___________ Nun soll noch ermittelt werden, wie hoch die CPU Last ohne und mit hackbench ist:
  • Starten Sie in einer weiteren dritten ssh Shell auf dem Zielsystem das Programm top. In der 2. Zeile von oben wird die Gesamt-CPU-Last angezeigt, aufgeteilt nach:
    bash
    x% usr (CPU-Zeit im User-Mode durch "normale" Usermode-Programme)
    x% sys (CPU-Zeit während Abarbeitung von Systemcalls und Kernel Threads)
    x% nic ("nice" → mit niedriger Priorität laufende Usermode Prozesse)
    x% idle (Zeit in welcher CPUs im Idle Mode sind, d.h. ohne Last)
    x% io ("iowait" → Blockaden aufgrund von IO-Vorgänge, z.B. bei Auslagerung auf Swap Space?)
    x% irq (CPU-Zeit in Hardware Interrupt Routinen)
    x% sirq (CPU-Zeit in softirq Routinen)
    

Ohne hackbench mit nur cyclictest sind alle CPUs offensichtlich nahezu permanent im idle Mode.

  • Damit ein hackbench Durchlauf ca. 10x länger läuft, rufen Sie selbiges in der 2. ssh Session mit Option -l 1000 auf (also mit 10000 statt 100 Loops).

Wo wird nun wieviel CPU-Last "verbraten"? _______________.

hackbench lastet die CPUs also vollständig aus und nahezu alles im Kernel-Mode und nicht im Userspace!

Offensichtlich verursacht das Rescheduling beim Wechsel zwischen den sendenden und empfangenden Threads viel Prozessorlast im Kernel!

CPU Last

Dass die CPU-Last sich auf alle CPUs bezieht wird klar wenn nur auf einer CPU 100% ausgelastet wird, was z.B. wie folgt generiert werden kann: while true; do echo >/dev/null; done In diesem Fall zeigt top bloss 25% Last an.

Latenzzeit ist aber nicht bloss von der CPU-Last abhängig sonder v.a. auch von IO-Last (wie Netzwerk und Disk-IO), denn diese bewirken typisch viele Interrupts, welche dann das Rescheduling der cyclictest Threads verzögern. Im Folgenden soll deshalb auch noch massive IO-Last generiert werden, einerseits Netzwerklast, andererseits durch Schreibzugriffe auf die SD-Karte.

Um Netzwerklast zu generieren, verwenden wir das Programm iperf3, welches Sie bei der Buildroot menuconfig im 1. Versuch zugefügten. Mit iperf3 lässt sich der maximal mögliche Durchsatz einer Netzwerkverbindung einfach testen, wozu iperf3 auf der einen Seite im Server-Mode und auf der anderen Seite im Client Mode gestartet wird.

  • Starten Sie auf dem Zielsystem in der 2. Session iperf3 im Servermode, also gemäss iperf3 --help mit Option -s, wodurch iperf3 auf Port 5201 auf eine eingehende TCP oder UDP Verbindung wartet...
  • Auf dem Linux (Gast-)Entwicklungssystem starten Sie iperf3 (nach Installation) im Clientmode, also mit Option -c und Angabe der Zielsystem-IP-Adresse und der Option -t30 l.

    DNS

    Oder alternativ mit Angabe des DNS-Namens des Zielsystems: sofern Sie das Raspi über Ihr LAN angeschlossen haben und in /etc/network/interfaces die Registrierung eines DNS-Namens in konfigurierten sowie Ihr Router die Namensregistrierung auch korrekt verarbeitet...

  • Je nach Verbindungsart (Ethernet oder WLAN und im Falle von WLAN der Distanz zum Accesspoint) sollte ein "recht flotter" Durchsatz erreicht werden: beim Dozenten via Ethernet nahezu 1Gbps, also fast "wire-speed" wogegen per 5GHz WLAN nur etwa 50Mbps erreicht wurde (kein 'b'=bit nicht Byte!).
  • In welchem Bereich liegen nun die max. cyclictest Latenzzeiten während dem Netzwerktest: __________________. (Abhängig hohem Durchsatz vermutlich im ähnlichen Bereich wie beim hackbench Test)
  • Gemäss top ist diesmal aber nicht die CPU-Zeit im User- oder Kernel-Space das Problem. Beim Dozenten wird bei Ethernet und 1GBps folgende Werte angezeigt: CPU: 0% usr ….% sys 0% nic …..% idle 0% io 0% irq ….% sirq.

Wie ersichtlich ist im Gegensatz zu hackbench die CPU-Zeit in Softirqs recht hoch. Offenbar erfolgt ein Grossteil der Netzwerk-Verarbeitung in "Softirqs" (Interrupt-Routinen können mit wenig Overhead eine Nachverarbeitung (sog. "Bottom Half") in "Softirqs" auslagern. Diese werden dann mit niedrigster Interrupt-Priorität ebenfalls in einem Interrupt-Kontext ausgeführt, sodass einerseits während deren Abarbeitung weitere Interrupts bearbeitet werden können, andererseits kein aufwändiges Resheduling nötig ist.

Damit bei zu hoher Softirq Last ein "Dead Lock" entsteht, erfolgt bei zu hoher Softirq Last die "Bottom Half" Bearbeitung statt dessen in Kernel Threads ([ksoftirq/n]).

Nun soll noch getestet werden, welche max. Latenzzeiten ein Schreiben auf die SD-Karte bewirkt:

Hierzu werden in der 2. ssh Session per dd von /dev/zero Null-Bytes gelesen und diese auf eine Datei, z.B. xyz geschrieben. Im Gegensatz zum normalen cp Befehl kann bei dd mit Angabe von bs=... die Blockgrösse explizit angegeben werden, also in welchen Einheiten gelesen und geschrieben wird.

Wir verwenden eine Blockgrösse von 4MB:

bash
dd if=/dev/zero of=xxx bs=4M

Betrachten Sie während dem Schreibtransfer den Output von top in der 3. ssh Session wiederum die CPU-Zeiten: CPU: ….% usr ….% sys 0% nic ….% idle ….% io 0% irq 0% sirq

Sowie die max. Latenzzeiten in der 1. ssh Session: (!)

Offensichtlich steigt durch ein permanentes Schreiben auf die SD-Karte die Latenzzeit auch von hoch priorisierten Realtime-Tasks erheblich - diesmal nicht durch Interrupt-Last sondern durch iowait, also weil auf IO-Transfers gewartet wird. Erstaunlicherweise obwohl da durch cyclictest gar keine Schreib-/Lesezugriffe auf die SD-Karte stattfinden!

Übrigens: beim Lesen von der SD-Karte treten trotz deutlich höherer Transferrate trotzdem deutlich weniger Latenzzeitprobleme auf. ( Falls Sie es selbst nachvollziehen möchten, müssen Sie beachten, dass Dateiinhalte im Page-Cache zwischengespeichert werden – jedenfalls solange genügend freier DRAM Speicher vorhanden ist. Wenn also eine Datei zuvor gelesen oder geschrieben wurde, wird beim erneuten Lesen der Dateiinhalte aus dem Page Cache rekonstruiert und nicht von der SD-Karte gelesen! Man muss also schon jeweils vor dem Lesen der Datei den Page Cache explizit wieder löschen, was mit echo 3 > /proc/sys/vm/drop_caches wonach das Lesen z.B. per dd if=xxx of=/dev/null bs=4M durchführbar ist.)

Löschen Sie zuletzt die Datei xyz, sodass sie wieder Platz auf dem Rootfilesystem haben, denn das Schreiben per dd aus /dev/null "frisst" ja den gesamten freien Diskspace der Rootpartition auf!

Flash Speicher überschreiben

Und noch eine wichtige Bemerkung: Überschreiben Sie nie in einer Endlosschleife wieder und wieder die gleiche Datei auf der SD-Karte, denn dadurch findet ein übermässiges "wear-out" statt, wodurch die SD- Karte schon nach wenigen Stunden defekt sein wird!

Nachrechnung: Der Schreibtest zeigte, dass der ca. 350MB freie Diskspace ohne explizite Angabe der Blocksize in 25 Sekunden resp. mit dd Option bs=4M in 16 Sekunden auf die SD-Karte geschrieben wird, woraus sich eine Transferrate von 13 Mbps (ohne explizite Blocksizeangabe) resp. 21MBps (mit 4MB Blocksize) errechnen lässt. Angenommen die 32GB SD hat ca. 5% Reserveblocks (was 2.2GB entspricht) sowie eine "Lebensdauer" von 1000 Schreibvorgänge pro Eraseblock, so würde nach 1000 * 2.2GB / 21MBps ≈100'000 Sekunden also nach gut einem Tag die SD-Karte "ausgebrannt" sein!

Linux Realtime Patch

Standardmässig ist mit Linux also wie die Tests zeigten bloss Soft-Realtime fähig, d.h. es ist nicht möglich, unter allen Lastbedingungen harte (in etwa gleich bleibende) Echtzeitbedingung einzuhalten. Mit dem Linux Realtime Patch (resp. RT-Patch) sollte dies möglich sein - wie Sie zu Beginn des Versuches erfahren haben aber auf Kosten des CPU-Durchsatzes und damit auch des Energieverbrauches.

Die Wiki Seite zum Linux RT-Patch finden Sie auf: https://wiki.linuxfoundation.org/realtime/start

Der RT-Patch Release muss exakt zum Kernel-Release passen, denn dieser verändert ja die Kernel Source an sehr kritischen Stellen!

Welcher Kernel-Release wir installiert haben, ist entweder auf dem laufenden Zielsystem mittels cat /proc/version einsehbar oder auf dem Entwicklungssystem nach Wechsel in das Kernel Source Verzeichnis des Kernels einsehbar entweder mittels make kernelrelease oder indem die ersten paar Zeilen des Makefile betrachtet (z.B. per less Makefile oder head Makefile).

Welche Version des Kernels verwenden wir aktuell? Finden Sie auf http://cdn.kernel.org/pub/linux/kernel/projects/ rt einen passenden RT Patch?

Der Kernel wurde bereits im Versuch 'Kernel Compilen' selber gecloned, konfiguriert und kompiliert. Um die RT_PREEMPT Config Option zu erhalten, wird der Kernel nun gepatcht.

Wechseln Sie ins Kernel Source Verzeichnis und cleanen Sie den Kernel mit:

bash
make clean

Beachten Sie, dass die alte Kernel Config beibehlaten wird bei make clean. Der aktuelle Kernel sollte 6.6.y sein. Unter https://cdn.kernel.org/pub/linux/kernel/projects/rt/6.6/ finden Sie direkt leider nicht die richtige Sublevel Version. Schauen Sie unter older, ob eine passende Version existiert.

Laden Sie die Version herunter mit und vergewissern Sie sich, dass Sie sich im Kernel Source Verzeichnis befinden.

bash
wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/6.6/patch-6.6.30-rt30.patch.gz

## entpacken

gunzip patch-6.6.30-rt30.patch.gz

Wonach die Kernel Source mit dem Realtime-Patch per patch Command gepatched wird ... Werfen Sie aber zuerst noch einen kurzen Blick in die Patchdatei, welche Sie im übergeordneten Order entpackten:

bash
less patch-6.6.30-rt30.patch

Die Patch-Datei ist also bloss eine Textdatei im POSIX diff-Format, wobei für jede zu patchende Datei jene Zeilen angegeben sind, welche - zu entfernen oder + zuzufügen sind.

Da das Patchen direkt aus dem Source-Tree geschieht, ist die patch Option -p1 nötig, wodurch die oberste Ebene der in der Patchdatei enthaltenen Pfadangaben (also a/ resp. b/ ) entfernt wird. Weiter muss die patch-Datei per Standard Input Redirection in den patch Command eingelesen werden!

Und um sicher zu sein, dass das Patchen keinen Ärger macht, testen Sie zuerst mittels Option --dry-run wodurch der Patchvorgang bloss simuliert und (noch) nicht durchgeführt wird:

bash
patch --dry-run -p1 < patch-6.6.30-rt30.patch

Treten dabei keine "Errors" auftreten, führen Sie den Command nichmals ohne --dry-run durch.

Nun haben Sie also eine gepatchte Kernel Source. Um den Kernel für das Raspi 4B und ARM 32-bit Architektur zu builden, muss als Basis die korrekte Default-Konfig ausgewählt werden:

bash
export ARCH=ARM
export CROSS_COMPILE=..... #Pfad zum Toolchain Prefix
make menuconfig

Deselektieren Sie auch noch MODULE_COMPRESS_XZ.

Es ist nun Zeit den Kernel neu zu kompilieren:

bash
make zImage dtbs modules

Kopieren Sie das neue Kernel Image sowie die Module gemäss Lab "Kernel Compilen" auf die SD-Karte.

RT-Kernel mit rt-tests

Nach einsetzen der SD in Raspi und Booten prüfen Sie zuerst per 'cat /proc/version' oder 'uname -r', ob nun wirklich der 6.6.y Kernel gebootet wurde und per uname -v ob PREEMPT_RT aktiv ist!

Nun können Sie den Schreibtest auf die SD bei laufendem cyclictest nochmals durchführen. In wie fern verändert/verbessert sich nun die Worst-Case Latenzzeiten

Und hat der rt-Patch einen Einfluss auf die Laufzeit von hackbench?

Fazit: Auf dem Raspberry Pi 4B scheint der RT-Patch tatsächlich eine immense Verbesserung der Realtime-Eigenschaften zu bringen – auf Kosten des Systemdurchsatzes! Damit der Latenzzeittest einigermassen repräsentativ ist, müsste er jedoch tagelang und mit wechselnden Last-Szenarien durchgeführt werden!

CPU Last

Interessant in diesem Zusammenhang ist noch, dass der -rt Kernel offensichtlich die CPU-Last von nicht-Realtime-Prozessen wie hackbench auf ca. 50% begrenzt, wogegen der normale Kernel nicht. Die 3-fach längere Ausführungszeit ist also nur teilweise durch CPU-Verwaltungsoverhead begründet.

Weiter sind alle CPU Takte bloss 600MHz, einsehbar in /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq . Grund dafür ist eine Kernel-Config welche die default CPUfreq Governor auf powersave setzt. Dies ist aber dynamisch änderbar via /sys/devices/system/cpu/cpufreq/policy0/scaling_governor. Einfluss hat dies sowohl auf den Energiebedarf als auch die CPU-Temperatur, welche wiederum in /sys/class/thermal/thermal_zone0/temp einsehbar ist (Angabe in milli °C). Im powersave Idle Mode war diese beim Dozenten ca. 53°, bei Vollast ca. 63° und bei "performance" Vollast (bei einer max. Taktfrequenz von 1.5GHz) ca. 80°. Darüber wird der Takt autom. reduziert.

Beim Erstellen von RT-Applikationen beachten

  • Wahl der Programmiersprache: mit Programmiersprachen wie C oder Rust sowie mit gewissen Einschränkungen auch mit C++ lassen sich zeitlich deterministische Programme erstellen. Hingegen sind Sprachen welche eine automatische Garbage Collection beinhalten wie Java oder C# zeitlich nicht deterministisch und deshalb ungeeignet. Ebenfalls problematisch sind Scriptsprachen wie Python, da einerseits langsam, andererseits (im Falle von Python) der Interpreter keine wirklich gleichzeitige Ausführung von mehreren Threads im selben Prozess zulässt (vgl. → "Python’s GIL Global Interpreter Lock").
  • Wahl des RT-Schedulers und Priorität: Linux unterstützt mehrere Scheduler – sogar gleichzeitig! Defaultmässig wird der "Completely Fair Scheduler" (CFS) verwendet, welcher nicht realtime-tauglich ist! Soft- oder Hard-Realtime Anwendungen müssen deshalb einen zeitlich deterministischen Scheduler wie SCHED_FIFO, SCHED_RR oder SCHED_DEADLINE verwenden! (vgl. man 7 sched) . Cyclictest nutzt dies!
  • On-Demand-Paging & Swapping sind Echtzeit-Killer: Abhilfe schafft mlockall(), womit einmal eingelagerte Pages im betreffenden Prozess auch bei Speicherknappheit nicht mehr freigegeben werden!
  • Cgroups/cpuset/CPU Affinität: ermöglichen fixe Zuweisung resp. Reservation von CPU(s) zu speizifischen RT-Task(s).
  • Power-saving States: Verhindern von tiefen CPU Power States und ev. Speed Stepping, was über /sys/class/cpu möglich ist.
  • ACPI / SMI Interrupts: Interrupts des BIOS - können auch unvorhergesehene Latenzen bewirken! Bei den meisten Embedded Boards kein Problem, da diese kein ACPI- und SMI-Firmware beinhalten, d.h. die CPUs sind durchgehend unter der Kontroller des Linux-Kernels.

Literaturhinweise: