Experience Embedded

Professionelle Schulungen, Beratung und Projektunterstützung

Linux-Echtzeit - Weckt der Kernel mein Programm zu spät auf?

Autor: Dr. Carsten Emde, Open Source Automation Development Lab (OSADL) eG

Beitrag - Embedded Software Engineering Kongress 2017

 

Dem Linuxkernel mögliche Echtzeiteigenschaften (RT) zu geben, ist nicht schwer:

Kernel herunterladen,
RT-Patch herunterladen,
RT-Patch einspielen,
Kernel übersetzen,
Computer neu starten und
RT-Kernel auswählen.

Auch die Überprüfung, inwieweit sich das Antwortverhalten des neu hergestellten Kernels tatsächlich tatsächlich verbessert hat, ist recht einfach: Das Programm cyclictest starten und ein paar Stunden warten & Ergebnis beurteilen.

Was kann man tun, wenn Latenzen gefunden werden, d.h. wenn der Kernel das Userspace-Programm hin und wieder mal zu spät aufweckt? Hierfür gibt es verschiedene Messmethoden; aber ganz so einfach wie die Herstellung des RT-Kernels ist es nun nicht mehr. Daher werden die einzelnen Messmethoden

• Breaktrace mit anschließender Trace-Analyse
• Kontinuierliche Latenzaufzeichnung mit Spitzendetektion

im Detail erklärt und mit Beispielen vorgeführt. Darüber hinaus gibt es häufig vorkommende Latenzquellen wie z.B.

• Frequenzmodulation und
• Schlafstadien,

die ausgeschlossen werden müssen.

 

Rekapitulation: Wie erzeuge ich einen echtzeitfähigen Linuxkernel – z.B. auf einem Intel-PC mit einer Standard-Distribution?

In einem ersten Schritt wählt man Patchlevel und Sublevel des RT-Kernels, dessen Nummer möglichst nahe am Kernel der jeweiligen Distribution liegt. Dies ist in der Regel möglich, da es mit wenigen Ausnahmen für jeden zweiten Patchlevel einen RT-Patch gibt. Wenn also z.B. die Debian-Distribution 9 (stretch) verwendet wird, kann man durch Eingabe des Kommandos

dpkg -l linux-image-`uname -r`

die Information

||/ Name Version
+++-=======================---========
ii linux-image-4.9.0-4-amd 4.9.51-1

erhalten, dass die aktuelle Installation den Linuxkernel in der Version 4, Patchlevel 9 und Sublevel 51 verwendet. Auf der Downloadseite des Linux-RT-Projekts - https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.9/older/ - erfährt man dann, dass dort der RT-Patch

patch-4.9.47-rt37.patch.xz

verfügbar ist, der dem aktuellen Nicht-RT-Kernel am ähnlichsten ist. Mit den folgenden Kommandos wird

• der dazu passende Quellcode heruntergeladen,
• der Patch heruntergeladen,
• die Archive ausgepackt und
• der Patch eingespielt:

mkdir -p /usr/src/kernels
cd /usr/src/kernels
wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-
4.9.47.tar.xz
wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/
4.9/older/patch-4.9.47-rt37.patch.xz
tar xf linux-4.9.47.tar.xz
mv linux-4.9.47 linux-4.9.47-rt37
cd linux-4.9.47-rt37
xz -d ../patch-4.9.47-rt37.patch.xz
patch -p1 <../patch-4.9.47-rt37.patch

Damit der RT-Kernel die aktuelle Distribution möglichst gut unterstützt, übernimmt man im ersten Schritt deren Kernel-Konfiguration, die sich bei Debian wie bei vielen anderen Distributionen im Verzeichnis /boot befindet und in der Versionsnummer mit dem Kernel übereinstimmt. Im vorliegenden Fall ist es die Datei /boot/config-4.9.0-4-amd64, die nun unter dem Namen .config in das Root-Verzeichnis des Kernel-Quellcodes kopiert wird:

cp /boot/config-4.9.0-4-amd64 .config

Im letzten Schritt, bevor der Kernel übersetzt werden kann, muss nun der neue Kernel konfiguriert werden, so dass die mit dem RT-Patch eingespielte Funktionalität auch genutzt wird. Dazu wird das Kommando

make menuconfig

aufgerufen, die Seite

Processor type and features --->
Preemption Model --->

aufgerufen und Fully Preemptible Kernel (RT) ausgewählt wie in Abbildung 1 (siehe PDF) ausgeführt. Der Kernel wird sodann in üblicher Weise übersetzt, installiert, und das System muss danach neu gestartet werden.

make -j4
make modules_install install
reboot

Beim Neustart wird dann der RT-Kernel im Boot-Menu ausgewählt.

Überprüfung des Antwortverhaltens des Systems

Als erster wichtiger Schritt nach dem Neustart sollte man sich vergewissern, ob der neue Kernel tatsächlich – zumindest formal – richtig konfiguriert wurde. Dies erkennt man daran, ob die Flags PREEMPT und RT in der Ausgabe des Programms uname enthalten sind:

uname -v | cut -d“ “ -f1-4
#1 SMP PREEMPT RT

was hier offensichtlich der Fall ist. Ein viel besserer Beweis ist aber natürlich, asynchron einfallende Ereignisse zu erzeugen und zu überprüfen, wie lange das System höchstens benötigt, damit ein auf dieses Ereignis wartender Userspace-Prozess mit Echtzeit-Priorität darauf reagieren kann. Die zu erwartende Zeitspanne lässt sich mit der Faustformel

Maximale Latenz = Taktintervall x 10⁵

abschätzen. Demnach kann man also zum Beispiel bei einem System mit einer Taktfrequenz von 1 GHz und damit einem Taktintervall von 1 ns eine maximale Latenz von weniger als 100 μs erwarten. Für die Messung der maximalen Reaktionszeit eines Userspace-Prozesses hat sich das Testprogramm cyclictest bewährt, das in der RT-Test Suite rt-tests enthalten ist, die vom folgenden Repository bezogen werden kann:

git clone git://git.kernel.org/pub/scm/utils/rt-tests/rttests.git

Das Programm cyclictest enthält auch gleich eine Histogramm-Funktion, mit der sich sogenannte Latenz-Plots in der Standardform herstellen lassen. In dieser Standardform werden auf der x-Achse Latenzklassen in Mikrosekunden mit einer Granularität von einer Mikrosekunde aufgetragen, und auf der y-Achse stehen die Häufigkeiten an Messungen pro Klasse in logarithmischer Darstellung. Es handelt sich also um ein Histogramm mit der Besonderheit, dass durch die logarithmische y-Achse sowohl sehr geringe als auch sehr große Häufigkeiten abgelesen werden können. Ein Shell-Skript, mit dem sich cyclictest ausführen und sich aus den Messwerten unmittelbar ein Standard-Latenzplot erzeugen lässt, kann von der OSADL-Webseite unter https://www.osadl.org/uploads/media/mklatencyplot.bash heruntergeladen werden. Es erfordert die vorherige Installation der Programmpakets gnuplot, das in den meisten Distributionen enthalten ist. Außerdem wird ein Programm benötigt, um den vom Skript erzeugten Latenzplot plot.png anzuzeigen.

# 1. Run cyclictest
cyclictest -l100000000 -m -Sp90 -i200 -h400 -q >output

# 2. Get maximum latency
max=`grep "Max Latencies" output | tr " " " " | sort -n | tail -1 | sed s/^0*//`

# 3. Grep data lines, no empty lines a common field separator
grep -v -e "^#" -e "^$" output | tr " " " " >histogram

# 4. Set the number of cores, for example
cores=4

# 5. Create two-column data sets
for i in `seq 1 `
do
column=`expr 1 + 1`
cut -f1, histogram >histogram1
done

# 6. Create plot command header
echo -n -e "set title \"Latency plot\" \
set terminal png \
set xlabel \"Latency (us), max us\" \
set logscale y \
set xrange [0:400] \
set yrange [0.8:*] \
set ylabel \"Number of latency samples\" \
set output \"plot.png\" \
plot " >plotcmd

# 7. Append plot command data references
for i in `seq 1 `
do
if test 1 != 1
then
echo -n ", " >>plotcmd
fi
cpuno=`expr 1 - 1`
if test -lt 10
then
title=" CPU"
else
title="CPU"
fi
echo -n "\"histogram1\" using 1:2 title \"\" with
histeps" >>plotcmd
done

# 8. Execute plot command
gnuplot -persist <plotcmd

 

Wird das Skript unverändert übernommen, so hat es eine Laufzeit von 5 Stunden und 33 Minuten und erzeugt dabei Daten von 100 Millionen Zyklen. Für ein aussagekräftiges Ergebnis müssen während der Messung geeignete Stress-Szenarien eingerichtet werden. Ein typisches Ergebnis ist in Abbildung 2 (siehe PDF) wiedergegeben; die Skalierung der x-Achse ist bewusst sehr hoch gewählt, um das Ergebnis mit langsameren Prozessoren bzw. mit Systemen, die unbefriedigende Echtzeiteigenschaften besitzen, vergleichen zu können. Wünschenswert ist eine möglichst steile rechtsseitige Flanke der Kurve. Da es sich um einen Prozessor mit einer Taktfrequenz von 2,5 GHz und damit einem Taktintervall von 0,4 ns handelt, wäre nach oben genannter Faustformel eine maximale Latenz von 40 μs zulässig. Der hier gemessene Wert in Höhe von 19 μs liegt deutlich darunter, so dass eine Forschung nach den Ursachen dieser Latenz und der Versuch, deren weitere Reduktion zu erreichen, wohl nicht sinnvoll sind.

Analyse eines Systems mit unbefriedigenden Echtzeiteigenschaften

Beim in Abbildung 3 (siehe PDF)wiedergegebenen Latenzplot handelt es sich um ein Uniprozessorsystem mit x86-Architektur. Diese Architektur benötigt nicht selten sogenannte System Management Interrupts (SMIs), mit denen zum Beispiel bestimmte Kommunikations-Protokolle emuliert, thermische Kontrollmaßnahmen organisiert und Microcode-Patche ausgeführt werden. Da das Betriebssystem keinerlei Möglichkeit hat, SMIs zu verhindern, kann es durchaus sein, dass SMIs mit einer Ausführungsdauer, die länger als die akzeptable Latenz des Systems ist, dazu führen, dass ein solches System grundsätzlich nicht für echtzeitpflichtige Aufgaben eingesetzt werden kann. Mitunter kann eine Nachbesserung des BIOS durch den Hersteller die SMIs beseitigen oder zumindest soweit verkürzen, dass diese nicht die Echtzeiteigenschaften des Systems beeinträchtigen.

Der Latenzplot in Abbildung 3 (siehe PDF) legt den Verdacht nahe, dass es sich um SMI-bedingte Latenzen handelt. Um dies näher zu überprüfen, wurde das Programm hwlatdetect verwendet, das genau wie cyclictest in der bereits erwähnten RT-Test Suite rt-tests enthalten ist. Ab Kernel-Patchlevel 4.9 kann dieses Programm allerdings nicht mehr verwendet werden, da seit diesem Kernel die Funktionalität im Mainline-Kernel enthalten ist und mit Hilfe des tracing-Subsystems verwaltet wird. Unabhängig von der Implementierungs-Variante besteht die Messung aus einer einmal pro Sekunde stattfindenden halbsekündigen Halt-Operation des Prozessors, während der normalerweise keine Instruktionen ausgeführt werden. Passiert dies dennoch, was durch regelmäßige Abfrage des Time Stamp Counters des Prozessors festgestellt werden kann, handelt es sich um eine extern ausgelöste Aktivität, die zu Latenzen führen und deren Dauer bestimmt werden kann. Das Ergebnis einer solchen Hardware-Latenz-Messung kann dann wiederum in Form eines Histogramms dargestellt werden.

In Abbildung 4 (siehe PDF) erkennt man eine Vielzahl von Hardware-Latenzen, deren Dauer von knapp über 300 μs gut zum Latenzplot des gleichen Systems in Abbildung 3 passt.

Weitere Testmethoden bei Vorliegen unbefriedigender Echtzeiteigenschaften

Wenn ein System erhöhte Latenzen aufweist, sich diese aber nicht mit SMIs erklären lassen, muss versucht werden, die Ursache der Latenzen zu ermitteln. Möglicherweise gibt es einen anderen Prozess, der mit einer höheren Priorität zur Latenz des beobachteten Prozesses führt; es ist aber auch möglich, dass eine Systemkomponente für einen zu langen Zeitraum die Bearbeitung von Interrupts oder das Neustarten von Prozessen ausgesetzt hat. Während ersteres sich in der Regel durch eine Analyse der aktuell laufenden Userspace-Prozesse klären lässt, erfordert letzteres eine Analyse von Kernelprozessen. Für eine solche Analyse von Kernelprozessen enthält das bereits erwähnte und verwendete Testprogramm cyclictest eine spezielle Option, mit der sich Kernel-Tracing zu Beginn der Operation einschalten und in dem Moment, in dem eine Latenz aufgetreten ist, wieder abschalten lässt. Als Argument muss dieser Option, die breaktrace genannt wird, ein Schwellenwert mitgegeben werden, bei dessen Überschreitung das Kernel-Tracing beendet wird. Wenn sogenanntes Function Tracing als Trace-Methode gewählt wird, muss berücksichtigt werden, dass das System dadurch etwa um den Faktor 4 langsamer wird. Dies behindert aber die Suche nach der Ursache einer Latenz nicht grundsätzlich, da die Verlangsamung des Systems in der Regel auch zu einer Verlängerung der Latenzen führt.

Beispiel für die Verwendung des Testprogramm cyclictest mit breaktrace

Das folgende Beispiel geht von einem Mehrkernsystem aus, das eigentlich eine maximale Latenz von unter 100 μs aufweisen sollte, tatsächlich aber Latenzen von über 1000 μs gemessen werden. Damit die zu erwartende Latenz unter Berücksichtigung des durch das Function Tracing hinzunehmenden Leistungsverlusts des Systems nicht zu einem falsch positiven Abbruch der Messung führt, sollte die Schwelle mehr als viermal höher, also zum Beispiel 600 μs, betragen. Da die zu analysierende Latenz von 1000 μs deutlich darüber liegt, führt diese Schwelle immer noch zum gewünschten Messabbruch. Der Aufruf des Programms cyclictest könnte in diesem Fall also lauten

cyclictest -m -Sp90 -i600 -d0 -fb600

und bei Abbruch der Messung durch Überschreiten der Latenzschwelle würde cyclictest dann die folgende Meldung ausgeben (einzelne Werte zur besseren Übersicht entfernt):
 

T: 0 P:90 I:600 C:12049 Min:19 Act: 79 Avg:120 Max: 312
T: 1 P:90 I:600 C:11940 Min:29 Act: 111 Avg:118 Max: 291
T: 2 P:90 I:600 C:11928 Min:31 Act: 189 Avg:141 Max: 295
T: 3 P:90 I:600 C:11911 Min:28 Act:3120 Avg:296 Max:3120
# Thread Ids: 18116 18118 18124 18127
# Break thread: 18127
# Break value: 3120

Der Thread mit der PID 18127 wurde offensichtlich mit einer Verzögerung von 3,12 ms aufgeweckt, was über der Schwelle von 600 μs liegt. Diese Situation passt gut zu den beobachteten Latenzen. Im nächsten Schritt muss dann der Zeitpunkt im KernelTracing aufgesucht werden, an dem der erwartete Start des verzögerten cyclictest-Threads ausgeblieben ist, und es muss die Ursache untersucht werden. Nicht selten liegt es daran, dass ein anderer Prozess zu diesem Zeitpunkt Interrupts gesperrt hat, so dass der Timer-Interrupt des von cyclictest aufgezogenen Timers nicht zur Bearbeitung gelangt. Dieser Programmfehler muss dann behoben werden, weil der gleiche Sachverhalt, der in diesem Fall das Platzhalter-Programm cyclictest getroffen hat, unter Produktionsbedingungen die Bearbeitung eines wichtigen Interrupts behindern und damit zu einem schwerwiegenden Maschinenfehler führen könnte.

Beispiel für die Verwendung des internen Latenz-Histogramme des Kernels

Ähnlich wie das Programm cyclictest im Userspace die verzögerte Ausführung eines Interrupts misst, kann man dies auch im Kernel tun und jedes Mal, wenn ein Timer abläuft, die Differenz zwischen geplantem und tatsächlichem Aufwachzeitpunkt bestimmen und in einem Histogramm speichern. Außerdem kann man die Zeit zwischen dem Beginn des Scheduling-Vorgangs eines aufzuweckenden Prozesses und dessen tatsächlichen Ausführungsbeginn messen und ebenfalls in einem Histogramm speichern. Dies gilt auch für die Summe der Dauer der beiden Vorgänge, die jeweils in einem dritten Histogramm abgespeichert wird. Während das Programm cyclictest niemals zusammen mit einem unter Produktionsbedingungen laufenden Echtzeitprogramm ablaufen darf – zumindest nicht auf dem gleichen Prozessorkern – kann man die Messung mit den internen Latenz-Histogrammen durchaus parallel laufen lassen und sogar Idle-Bedingungen erfassen. Damit die internen Latenz-Histogramme des Kernels zur Verfügung stehen, müssen diese konfiguriert und zur Laufzeit eingeschaltet werden. Die Konfiguration erfolgt im Menu Kernel Hacking/Tracers mit den einzelnen Komponenten Scheduling Latency Tracer, Scheduling Latency Histogram und Missed Timer Offsets Histogram. Zum Einschalten der Histogramme muss ein Wert ungleich Null im enable-Verzeichnis in die jeweilige Datei geschrieben werden.

Abbildung 5 (siehe PDF) zeigt eine 30-stündige Aufzeichnung der Summe von Timer- und Aufwach-Verzögerung eines Vierkernsystems; es sind die Maxima aufeinanderfolgender 5-Minuten-Intervalle angegeben. Dies ist möglich, weil die internen Latenz-Histogramme des Kernels über eine Reset-Funktion verfügen, die in diesem Fall alle fünf Minuten ausgelöst wurde. Deutlich erkennt man die sehr niedrige Latenz in der Zeit von 5:10 bis 6:40 Uhr und von 7:10 bis 12:43 und jeweils 12 Stunden später, in der die Taktfrequenzen der Prozessoren auf Maximum gestellt und alle Schlafstadien abgeschaltet waren. In der übrigen Zeit liegen die Latenzwerte deutlich darüber, da es sich in diesem Fall um einen relativ modernen Prozessor (Intel Bay Trail) handelt, der über ein breites Spektrum an Energiesparmethoden verfügt. Wenn man dem Prozessor erlaubt, diese zu nutzen, muss man als Nebenwirkung eine deutlich verlängerte Latenz bzw. den kompletten Verlust jeglicher Echtzeitfähigkeit hinnehmen.

Neben den Histogrammen werden auch die Daten der am Scheduling beteiligten Prozesse erfasst, und es werden die Prozesse, die jeweils zur bis dahin höchsten Latenz geführt haben zusammen mit Prioritäten, Prozess-ID und Latenzen abgespeichert. Auf diese Weise kann es gelingen, den Prozess, Thread oder Treiber zu ermitteln, der in einem bestimmten Fall für eine erhöhte Latenz verantwortlich ist. Dies erleichtert es in der Regel sehr, den zu Grunde liegenden Fehler zu beheben.

Das Beispiel in Tabelle 1 (siehe PDF) zeigt eine Auflistung der Scheduling-Daten der acht höchsten Latenzen während eines Tests mit cyclictest. Wie man sieht, war in jedem Fall das Programm meminfo für die beobachtete erhöhte Latenz verantwortlich. Dies ist ein starker Hinweis darauf, dass meminfo einen Kernelaufruf vornimmt, der den Linuxkernel daran hindert, cyclictest in der normalerweise kurzen Zeit von höchstens 20 μs aufzuwecken.

Die weitere Analyse ergab, dass bei diesem Kernel die Konfiguration

CONFIG_SLABINFO

eingeschaltet war und während der Messung der Aufruf

cat /proc/slabinfo

wiederholt ausgeführt wurde. Beim Auslesen der Debugdaten des SLAB Allocators kann der Kernel je nach Speicherbelegung und Speicherkonfiguration für einen längeren Zeitraum nicht unterbrochen werden. Der folgende Patch sorgt dafür, dass diese Kernel-Konfiguration nicht eingeschaltet wird, wenn gleichzeitig Real-Time gewählt ist.

 

---
init/Kconfig | 1 +
1 file changed, 1 insertion(+)
Index: linux-4.9.47-rt37/init/Kconfig
==============================================================
--- linux-4.9.47-rt37.orig/init/Kconfig
+++ linux-4.9.47-rt37/init/Kconfig
@@ -1946,6 +1946,7 @@ config SLABINFO
bool
depends on PROC_FS
depends on SLAB || SLUB_DEBUG
+ depends on !PREEMPT_RT_FULL
default y
config RT_MUTEXES

 

Nach Einspielen dieses Patches, Übersetzen des Kernels, Installation und Neustart traten die beobachteten Latenzen selbst bei schnell und häufig wiederholtem Auslesen der virtuellen Dartei /proc/slabinfo nicht mehr auf.

Wenn die entsprechende Funktion beim Auslesen von /proc/slabinfo mit eingeschaltetem CONFIG_SLABINFO-Flag in Zukunft an die Erfordernisse des Real-Time-Kernels angepasst sein sollte, kann dieser zur Zeit nur lokal eingesetzte Patch natürlich wieder entfallen.

Autor

Carsten Emde blickt auf eine mehr als 25-jährige Tätigkeit als Software-Entwickler, System-Integrator und Trainer zurück. Seine Spezialgebiete sind graphische Bedienoberflächen, maschinelle Bilderkennung und Echtzeit-Betriebssysteme. Seit der Gründung der Open Source Automation Development Lab (OSADL) eG im Jahre 2005 ist er deren Geschäftsführer.

 

Beitrag als PDF downloaden


Open Source - unsere Trainings & Coachings

Wollen Sie sich auf den aktuellen Stand der Technik bringen?

Dann informieren Sie sich hier zu Schulungen/ Seminaren/ Trainings/ Workshops und individuellen Coachings von MircoConsult zum Thema Open Source / Embedded Software Engineering.

 

Training & Coaching zu den weiteren Themen unseren Portfolios finden Sie hier.


Open Source - Fachwissen

Wertvolles Fachwissen zum Thema Open Source / Embedded Software Engineering steht hier für Sie zum kostenfreien Download bereit.

Zu den Fachinformationen

 
Fachwissen zu weiteren Themen unseren Portfolios finden Sie hier.