Forum: PC-Programmierung Raspberry pi - eigener Scheduler


von fuzzy (Gast)


Lesenswert?

Hi,

auf einem raspberry pi benötige ich einen Scheduler.
Mit diesem Objekt soll ein einfacher Timer gekapselt werden. In der 
Regel handelt es sich einfach um eine Interruptserviceroutine, die alle 
Millisekunde aufgerufen wird, und den Zeitzähler um eins zu 
inkrementiert.

Auf einem STM32 hab ich das bereits umgesetzt. Gibt es auf dem raspberry 
pi auch so ein Hardwareinterrupt der jede Millisekunde ausgelöst wird?

von Andreas B. (bitverdreher)


Lesenswert?

Nur mit einem Echtzeit OS, die es auch für den Raspi gibt.

von fuzzy (Gast)


Lesenswert?

Das bedeutet? Ich habe ja das raspberry pi OS drauf.

von fuzzy (Gast)


Lesenswert?

Kann ich nicht einen Thread aufsetzen und dadurch einen 1ms Tick 
erzeugen?

von Andreas B. (bitverdreher)


Lesenswert?

fuzzy schrieb:
> Das bedeutet? Ich habe ja das raspberry pi OS drauf.
Es gibt kein "Raspberry OS". Das ist Linux. Und Linux ist in der 
Normalversion eben kein Echtzeit OS.

fuzzy schrieb:
> Kann ich nicht einen Thread aufsetzen und dadurch einen 1ms Tick
> erzeugen?
Kannst Du machen. Ob der dann allerdings pünktlich kommt, sei 
dahingestellt.

von Jack V. (jackv)


Lesenswert?

Andreas B. schrieb:
> Es gibt kein "Raspberry OS". Das ist Linux.

„Raspberry Pi OS“ ist nun der Name für das, was früher „Raspbian“ hieß – 
damit ist nix verkehrt. Man könnte sicher auch einen RT-Kernel dafür 
bauen, ±1ms sind damit dann sicher sicher (intended) drin. Auf der 
anderen Seite gibt’s auch dedizierte RT-Systeme, die, je nach sonstigen 
Anforderungen, die bessere Wahl wären. Natürlich nicht, wenn’s um eine 
Linux-Anwendung geht ….

von Andreas B. (bitverdreher)


Lesenswert?

Jack V. schrieb:
> Man könnte sicher auch einen RT-Kernel dafür
> bauen, ±1ms sind damit dann sicher sicher (intended) drin.
Das haben schon andere gemacht. Gockel hilft.

von Jack V. (jackv)


Lesenswert?

Andreas B. schrieb:
> Gockel hilft.

Du meinst Meinolf Gockel (http://www.gockel.de)? Ich bin mir nicht 
sicher, wie ’n Laden, der heute noch nicht mal TLS, sondern nur 
unverschlüsseltes HTTP für einen Onlineshop(!) anbietet, bei doch recht 
speziellen IT-Fragen helfen können soll.

von Bronko (Gast)


Lesenswert?

Hi fuzzy,

Soweit ich weiss ist der RT Teil seit ca. 1-2 im RPi OS drin. Glaube die 
Timer sind vom OS genutzt. Mit threads sollte das gehen - pthread ist 
das Zauberwort. Musst du dann mal schaun, ob der Jitter ok ist.

von Jemand (Gast)


Lesenswert?

Jack V. schrieb:
> Andreas B. schrieb:
>> Gockel hilft.
>
> Du meinst Meinolf Gockel (http://www.gockel.de)?

Er meint vermutlich Google, leidet aber unter einem ganz besonders 
lustigem Humor oder womöglich einer Tastaturlegasthenie.

> Ich bin mir nicht
> sicher, wie ’n Laden, der heute noch nicht mal TLS, sondern nur
> unverschlüsseltes HTTP für einen Onlineshop(!) anbietet, bei doch recht
> speziellen IT-Fragen helfen können soll.

Also bei mir hat https://shop.gockel.de ein TLS-Zertifikat von Let's 
encrypt.

von Mikro 7. (mikro77)


Lesenswert?

fuzzy schrieb:
> auf einem raspberry pi benötige ich einen Scheduler.
> Mit diesem Objekt soll ein einfacher Timer gekapselt werden. In der
> Regel handelt es sich einfach um eine Interruptserviceroutine, die alle
> Millisekunde aufgerufen wird, und den Zeitzähler um eins zu
> inkrementiert.
>
> Auf einem STM32 hab ich das bereits umgesetzt. Gibt es auf dem raspberry
> pi auch so ein Hardwareinterrupt der jede Millisekunde ausgelöst wird?

Der Rpi ist kein uC; sondern eher ein PC. Da benutzt man in der Regel 
ein OS (Raspbian bzw. Nachfolger; aka Linux). Es gibt auch RTOS wie oben 
geschrieben. Man kann auch Bare Metal machen (also Ansatz a la uC), das 
ist aber kein einfacher Einstieg. Was genau hast du denn vor?

von Rolf M. (rmagnus)


Lesenswert?

Bronko schrieb:
> Soweit ich weiss ist der RT Teil seit ca. 1-2 im RPi OS drin. Glaube die
> Timer sind vom OS genutzt. Mit threads sollte das gehen - pthread ist
> das Zauberwort. Musst du dann mal schaun, ob der Jitter ok ist.

Man muss allerdings auch daran denken, in seinem Programm 
Realtime-Scheduling zu aktivieren. Ansonsten ist OSADL immer eine gute 
Anlaufstelle, um zu sehen, was möglich ist.

Zum Beispiel der ursprüngliche RPi:
https://www.osadl.org/Latency-plot-of-system-in-rack-7-slot.qa-latencyplot-r7s3.0.html?shadow=0

Der RPi4B:
https://www.osadl.org/Latency-plot-of-system-in-rack-7-slot.qa-latencyplot-r7s2.0.html?shadow=1

: Bearbeitet durch User
von Lothar (Gast)


Lesenswert?

fuzzy schrieb:
> raspberry pi OS

Was ist denn die Anwendung? Wenn es ein GUI werden soll, GUI Callback 
Timer schaffen ohne Probleme 10 msec präzise - da braucht es kein RTOS - 
z.B. für LXDE FLTK

https://www.fltk.org/doc-1.3/classFl.html#ae5373d1d50c2b0ba38280d78bb6d2628

> Thread aufsetzen und dadurch einen 1ms Tick erzeugen

Thread is asynchron - wird nicht funktionieren. Zudem ist der Context 
Switch 10 msec

Oder mit dem Pi Imager anderes OS z.B. RISC OS drauf machen und direkt 
wie beim uC den SysTick oder SP804 Timer Interrupt nehmen:

https://www.riscosopen.org/wiki/documentation/show/Hardware%20Vectors

SysTick : 0x7E003000

SP804 Timer : 0x7E00B000

https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

von Rolf M. (rmagnus)


Lesenswert?

Lothar schrieb:
>> Thread aufsetzen und dadurch einen 1ms Tick erzeugen
>
> Thread is asynchron - wird nicht funktionieren. Zudem ist der Context
> Switch 10 msec

Diese Aussage ergibt irgenwie wenig Sinn.

von PittyJ (Gast)


Lesenswert?

Wenn ich "Echtzeit" auf dem Raspi brauche, dann packe ich meist eine 
extra Hardware dazu. Wie wäre es mit einem billigen Arduino, der macht 
das Millisekunden-Timing, und der Raspi hat genug Zeit, mal etwas aus 
der lahmen SD-Karte zu holen. Einfach per USB anschliessen. Läuft bei 
mir seit Jahren.
Für noch mehr Echtzeit nehme ich die kleinen FPGAs von Lattice. Die 
lasse sich vom Raspi über SPI befüllen, und machen dann Microsekunden 
Timing.

Ein Gerät, welches als Massenspeicher eine SD-Karte hat, dem würde ich 
keine Echtzeit anvertrauen.

von fuzzy (Gast)


Lesenswert?

Vielen Dank für eure Antworten. Ich brauche ja keinen Realtime 
Systemtakt von 1ms. Mir reicht nur ein 1ms Takt für gewisse Anwendungen. 
Dieser kann auch schwanken. In Windows habe ich bereits auch einen mit C 
entwickelt und einem Thread. Das ganze müsste doch quasi auch mit dem 
raspberry pi OS realisierbar sein.

Für Threads gibt es ja die Möglichkeit mit pthreads oder std::thread.

von Rolf M. (rmagnus)


Lesenswert?

fuzzy schrieb:
> Für Threads gibt es ja die Möglichkeit mit pthreads oder std::thread.

Genau. Und für den Millisekunden-Takt kannst du z.B. clock_nanosleep() 
verwenden.

https://man7.org/linux/man-pages/man2/clock_nanosleep.2.html

von Lothar (Gast)


Lesenswert?

fuzzy schrieb:
> In Windows habe ich bereits auch einen mit C entwickelt und einem Thread

Kannst Du den Code mal zeigen? Grade in Windows ist doch ein .NET 
Callback Timer die naheliegende Lösung für 1 msec

Timer_sampling.Interval = 1
Timer_sampling.Start()

Private Sub TimerSampling_Tick(sender As Object, e As EventArgs) Handles 
Timer_sampling.Tick

Und das geht unter Linux mit z.B. FLTK genau so.

von Lothar (Gast)


Lesenswert?

Rolf M. schrieb:
> clock_nanosleep()

Das ist doch blocking = der main thread steht dann so lange?

Die Lösung ist doch einfach ein Timer Callback Handler der genau einer 
Timer ISR beim uC entspricht ...

Klar man könnte noch mit signals anrücken aber wozu kompliziert?

von fuzzy (Gast)


Lesenswert?

Hi Lothar, genau sowas mit einem Timer Callback Handler. Dazu müsste ich 
erstmal einen Thread danach ein Callback aufsetzen.

von Lothar (Gast)


Lesenswert?

fuzzy schrieb:
> erstmal einen Thread danach ein Callback aufsetzen

Nochmal - was hat Callback mit Thread zu tun?

Callback ist wie ISR und kann direkt erzeugt werden. Ist aber etwas 
Aufwand:

https://man7.org/linux/man-pages/man2/timer_create.2.html

Oder ist z.B. fertig in FLTK schon enthalten und kann einfach so genutzt 
werden:

https://www.fltk.org/doc-1.3/classFl.html#ae5373d1d50c2b0ba38280d78bb6d2628

von Rolf M. (rmagnus)


Lesenswert?

Lothar schrieb:
> Rolf M. schrieb:
>> clock_nanosleep()
>
> Das ist doch blocking = der main thread steht dann so lange?

Der Thread, in dem man es aufruft, steht so lange.

> Die Lösung ist doch einfach ein Timer Callback Handler der genau einer
> Timer ISR beim uC entspricht ...

Ein Callback muss aber auch erstmal von irgendwo aufgerufen werden.

von Sheeva P. (sheevaplug)


Lesenswert?

Andreas B. schrieb:
> fuzzy schrieb:
>> Das bedeutet? Ich habe ja das raspberry pi OS drauf.
> Es gibt kein "Raspberry OS". Das ist Linux. Und Linux ist in der
> Normalversion eben kein Echtzeit OS.

Das ist natürlich nur... teilweise richtig. Richtig ist, daß man jedem 
Linuxkernel auf Multicores mit dem Bootparameter "isolcpus" mitteilen 
kann daß der Kernelscheduler die angegebenen Cores nicht mit "normalen" 
Tasks beschicken soll. Dann laufen im Betrieb auf diesem/n Core/s nur 
die minimalen Interrupts, sonst nichts. Mit dem CLI-Werkzeug taskset(1) 
können dann aber trotzdem Prozesse fest an den "isolierten" Core 
gebunden werden. Das funktioniert auch ohne RT-Patches.

Nebenbei bin ich etwas... verunsichert hinsichtlich der Frage, ob die 
Damen und Herren in diesem Thread sich darüber bewußt sind, was 
"Echtzeit" bedeutet. Leider scheinen die meisten Menschen -- 
insbesondere IT-Fachleute -- zu glauben, daß es sich dabei um besonders 
schnelle Reaktions- oder Laufzeiten handele. Richtig ist, daß es sich 
dabei allerdings lediglich um eine Vereinbarung handelt, wie lange das 
Prozessieren eines Ereignisses dauert. Ein Beispiel: die Simulationen 
des Deutschen Wetterdienstes laufen in Echtzeit, auch wenn sie bis zu 
acht Stunden zum Prozessieren brauchen.

von Lothar (Gast)


Lesenswert?

Rolf M. schrieb:
> Ein Callback muss aber auch erstmal von irgendwo aufgerufen werden

Wie das unter Linux geht sieht man doch in den beiden verlinkten 
Beispielen. Direkt mit timer_create() eine ISR machen die den Callback 
periodisch aufruft - oder über eine Library die das intern macht - wie 
FLTK:

int main() {
  Fl::add_timeout(0.001, callback);
  return Fl::run();
}
void callback(void *) {
  // do stuff < 0.001 sec
  Fl::repeat_timeout(0.001, callback);
}

Der Callback ruft sich also einfach selbst wieder auf. Wie unter Windows 
.NET auch.

In einem RTOS könnte man dagegen den Callback direkt in den SysTick 
Vektor einhängen. Darf man halt nicht vergessen, vor Programmende wieder 
auszuhängen :-)

https://www.riscosopen.org/wiki/documentation/show/OS_CallEvery

https://www.riscosopen.org/wiki/documentation/show/OS_AddCallBack

https://www.riscosopen.org/wiki/documentation/show/OS_Claim

https://www.riscosopen.org/wiki/documentation/show/OS_RemoveTickerEvent

von Rolf M. (rmagnus)


Lesenswert?

Lothar schrieb:
> Wie das unter Linux geht sieht man doch in den beiden verlinkten
> Beispielen. Direkt mit timer_create() eine ISR machen die den Callback
> periodisch aufruft

Das läuft dann aber auch in einem separaten Thread. Die man-Page klingt 
mir auch nicht so vertrauenerweckend:

"Notify the process by invoking sigev_notify_function "as if" it were 
the start function of a new thread. (Among the implementation 
possibilities here are that each timer notification could result in the 
creation of a new thread, or that a single thread is created to receive 
all notifications.)

Kann also sein, dass dann jede Millisekunde ein neuer Thread erstellt 
und wieder zerstört wird, was nicht sehr performant klingt und für's 
Debugging auch nicht grad toll ist.

Da erstelle ich mir den Thread doch lieber selber, mach da eine 
Endlosschleife rein, in der er halt sein Ding macht und sich dann mit 
clock_nanosleep() schlafen legt, bis der nächste Zyklus dran ist. Das 
ist nun auch kein Hexenwerk.

 - oder über eine Library die das intern macht - wie
> FLTK:

Da wird aber kein Signal oder Thread verwendet, sondern dein Callback 
wird über die Eventloop von FLTK aufgerufen. Über diese Eventloop läuft 
dann aber auch alles andere. Ob man da auch nur halbwegs sauber eine 
Millisekunde halten kann, wenn da zwischendurch irgendwo ein Fenster neu 
gezeichnet oder eine XML-Datei eingelesen werden soll?

: Bearbeitet durch User
von Mikro 7. (mikro77)


Lesenswert?

fuzzy schrieb:
> Vielen Dank für eure Antworten. Ich brauche ja keinen Realtime
> Systemtakt von 1ms. Mir reicht nur ein 1ms Takt für gewisse Anwendungen.
> Dieser kann auch schwanken. In Windows habe ich bereits auch einen mit C
> entwickelt und einem Thread. Das ganze müsste doch quasi auch mit dem
> raspberry pi OS realisierbar sein.
>
> Für Threads gibt es ja die Möglichkeit mit pthreads oder std::thread.

Also hat die Thematik nichts mit dem Rpi zu tun; du möchtest in einem 
Linux (userland) Process lediglich (ungefähr) jede Millisekunde einmal 
alarmiert werden und eine Funktion f() aufrufen?!

Wenn du eh schon nebenläufig arbeitest, warum nicht einen separaten 
Thread starten der abwechselnd f() aufruft und dann 1ms schläft?! (Also 
prinzipiell was Rolf vorschlägt.)

Wenn du tatsächlich einen "Scheduler" willst (ohne Multi-Threading), 
dann realisiert man das in der Regel basierend auf poll/sellect (damit 
hat man die FD Events; und an den Timeout koppelt man die zeigesteuerten 
Jobs).

von Lothar (Gast)


Lesenswert?

Rolf M. schrieb:
> Callback wird über die Eventloop von FLTK aufgerufen

Probier es doch selbst mal aus. Dem ist nicht so. Das würde ja dazu 
führen, dass wenn der User ein Menü festhält, während dessen keine 
Callbacks aufgerufen werden:

int main() {
  // do stuff there pre-emtively - like acquiring data from GPIO
  Fl::add_timeout(0.001, callback);
  while (RUNNING) {
    // do stuff here co-operatively - like reading value from slider 
widget
    // yield()
    RUNNING = Fl::check(); }
  return RUNNING;
}

Threads dagegen haben genau dieses Problem. Um den Zugriff auf 
Ressourcen zu synchronisieren, braucht es ein Spinlock, und das zerstört 
die Echtzeit:

https://www.fltk.org/doc-1.3/advanced.html

von Rolf M. (rmagnus)


Lesenswert?

Lothar schrieb:
> Rolf M. schrieb:
>> Callback wird über die Eventloop von FLTK aufgerufen
>
> Probier es doch selbst mal aus. Dem ist nicht so. Das würde ja dazu
> führen, dass wenn der User ein Menü festhält, während dessen keine
> Callbacks aufgerufen werden:

Warum? Was macht FLTK denn während dieser Zeit?

> int main() {
>   // do stuff there pre-emtively - like acquiring data from GPIO
>   Fl::add_timeout(0.001, callback);
>   while (RUNNING) {
>     // do stuff here co-operatively - like reading value from slider
> widget
>     // yield()
>     RUNNING = Fl::check(); }
>   return RUNNING;
> }

Und wsa denkst du jetzt, von wo aus dein Callback aufgerufen wird? Ich 
kann's dir sagen: Das wird innerhalb von Fl::check() gemacht, wie auch 
die Doku schon sagt. Nix asynchron oder präemptiv, sondern alles schön 
nacheinander. Und wenn deine GUI zu lange braucht, um ein Fenster neu zu 
pinseln, fehlt halt dein Miiisekunden-Timer solange. Wie sollte es das 
ohne Threads auch sonst machen?

> Threads dagegen haben genau dieses Problem. Um den Zugriff auf
> Ressourcen zu synchronisieren, braucht es ein Spinlock, und das zerstört
> die Echtzeit:

Einen Spinlock verwendet man deshalb für sowas nur in besonderen 
Ausnahmefällen. Und wie viel man synchornisieren muss, hängt sehr stark 
von der Aufgabe ab. Deshalb sind Threads nicht überall sinnvoll.

> https://www.fltk.org/doc-1.3/advanced.html

Da steht überhaupt nix von Spinlocks.

von Lothar (Gast)


Lesenswert?

Rolf M. schrieb:
> Callback ... wird innerhalb von Fl::check()
> gemacht, wie auch die Doku schon sagt

Die Doku ist so eine Sache. Probier es doch selbst mal aus. Dann wirst 
Du sehen, dass Fl::check() nicht zurückkommt, bis alle Pending Events 
abgearbeitet sind. Wenn der User ein Menü festhält, kommt der Event erst 
mit dem Loslassen, und bis dahin kommt Fl::check() nicht zurück.

Die Callbacks werden an einen System Timer gehängt. Der läuft weiter, 
auch wenn sich Fl::check() oder main() aufgehängt haben.

Das ist übrigens bei allen Toolboxen die ich kenne so. Auch auf RTOS wo 
es gar keine Unterstützung für Threads gibt.

Kennst Du das nicht aus der uC Programmierung? Dort werden doch auch die 
Callbacks in eine Timer ISR eingehängt, und laufen unabhängig von main() 
- ganz ohne OS und Threads.

> Spinlock ... nur in besonderen Ausnahmefällen

Ein Spinlock ist doch schon nötig, wenn es für die Einstellung eines 
Parameters in der GUI ein Textfeld und einen Slider gibt, und beides mit 
Threads gemacht werden soll. Dann müssen die gegeneinander gelockt 
werden, weil sonst Unsinn passiert.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.