Optimieren einer "while" -Schleife

8

Ich habe ein Mini-Skript erstellt, um meinen Raspberry Pi auf Knopfdruck neu zu starten. Das Skript verwendet einfach wiringPi (Befehl gpio), um Pin 0 (Pin 17 in der Raspberry Pi-Standardnummerierungsreihenfolge) für die Eingabe festzulegen, und liest dann den Wert, bis er eins ist (dh wenn die Taste gedrückt oder gedrückt gehalten wird).

Hier ist mein Skript:

gpio mode 0 in

while (true)
do
        if [ `gpio read 0` -eq 1 ]
        then
                echo password | sudo -S reboot
                break
        fi
done &

Das Skript funktioniert gut und alles.

Für diejenigen unter Ihnen, die mit dem Pi nicht vertraut sind, sind jedoch nur sehr begrenzte Hardwareressourcen (einschließlich 512 MB Speicher) verfügbar, die von einer Schleife wie der von mir verwendeten problemlos verbraucht werden können.

Was ich hier erreichen möchte, ist, einen anderen Weg für bash zu finden, um herauszufinden, wann sich der Wert von 0zu geändert hat , 1ohne dafür eine eher bedingungslose Schleife zu reservieren. Ist das machbar? Bitte teilen Sie Ihre Ideen.

Fadi Hanna AL-Kass
quelle
3
Haben Sie darüber nachgedacht, Interrupts zur Behandlung von Hardwareereignissen zu verwenden, oder ist dies in Ihrem Fall absolut unmöglich? adafruit.com/blog/2013/03/29/…
lgeorget
3
Ich würde nur einen Schlafaufruf hinzufügen, der den Speicherverbrauch einschränken sollte
strugee
@ lgeorget Interrupts wären ideal, würden aber wahrscheinlich nicht von Bash behandelt.
Jordan
Diese Schleife verbraucht keinen Speicher.
OrangeDog

Antworten:

11

Analyse und moderne Lösung

Das Skript ist eine Besetztschleife: Es liest immer wieder die GPIO-Pins. Es verbraucht nicht viel Speicher, hält aber die CPU beschäftigt.

Sie sollten den GPIO-Pin in den Edge-Modus versetzen. Das gpioDienstprogramm verfügt über einen wfiBefehl (Warten auf Interrupt), mit dem Sie auf einen Flankentrigger reagieren können. ( gpio wfiexistierte nicht zurück, als die Frage gestellt wurde.)

set -e
gpio mode 0 in
gpio wfi 0 rising
echo password | sudo -S reboot

Eine Python-Lösung

Es gibt eine Python-Bibliothek für den GPIO-Zugriff , die den Edge-Modus unterstützt. Hier ist ein völlig ungetesteter Python-Code, der das tun soll, was Sie wollen.

#!/usr/bin/env python
import os
from RPi import GPIO
GPIO.wait_for_edge(0, GPIO.RISING)
system("sudo reboot")

Zusätzliche Shell-Tipps

(true)könnte gerade geschrieben werden true. Die Klammern erstellen einen Unterprozess, der völlig unnötig ist.

`gpio read 0`sollte in doppelten Anführungszeichen stehen. Ohne Anführungszeichen wird die Ausgabe des Befehls als Liste von Platzhaltermustern für Dateinamen behandelt. Bei doppelten Anführungszeichen wird die Ausgabe des Befehls als Zeichenfolge behandelt. Setzen Sie immer doppelte Anführungszeichen um Befehlssubstitutionen und Variablensubstitutionen: "$(some_command)", "$some_variable". Sie sollten auch die Syntax verwenden $(…)und nicht `…`: Sie hat genau die gleiche Bedeutung, aber die Backquote-Syntax weist einige Parsing-Macken auf, wenn der Befehl komplex ist. Somit:if [ "$(gpio read 0)" -eq 1 ]

Fügen Sie das Root-Passwort nicht in das Skript ein. Wenn das Skript als root ausgeführt wird, benötigen Sie sudo überhaupt nicht. Wenn das Skript nicht als root ausgeführt wird, erteilen Sie dem Benutzer, der das Skript ausführt, die Berechtigung zum Ausführen, sudo rebootohne ein Kennwort anzugeben. Führen visudoSie die folgende Zeile aus und fügen Sie sie hinzu:

userwhorunsthescript ALL = (root) NOPASSWD: /sbin/reboot ""

Beachten Sie, dass der Eintrag folgen NOPASSWDmuss , wenn in der sudoers-Datei ein Eintrag für denselben Benutzer vorhanden ist, für den ein Kennwort erforderlich ist .

Sobald Sie einen Neustart auslösen, müssen Sie die Schleife nicht mehr unterbrechen, das System stoppt trotzdem.

Wenn Sie dieses Shell-Skript weiterhin verwenden möchten und Ihre Version von gpiozu alt ist, um den wfiUnterbefehl zu verwenden, finden Sie hier eine verbesserte Version, die nur den Schaltflächenstatus jede Sekunde überprüft. Da der Pin nur einmal pro Sekunde gelesen wird, müssen Sie die Taste mindestens eine Sekunde lang gedrückt halten, um sicherzustellen, dass das Ereignis erfasst wird.

gpio mode 0 in
while sleep 1; do
    if [ "$(gpio read 0)" -eq 1 ]; then
        reboot
    fi
done &
Gilles 'SO - hör auf böse zu sein'
quelle
1
In Ihrem letzten Beispiel könnten Sie einen Bruchteil einer Sekunde schlafen . So etwas wie 0.1oder 0.2sollte vielleicht in der Lage sein, sehr kurze Druckvorgänge zu erkennen und dennoch viel CPU-Zeit für andere Threads zu lassen.
Bob
@Bob: Während Portabilität in diesem Fall wahrscheinlich keine Rolle spielt, ist sleep(1)die Akzeptanz einer gebrochenen Anzahl von Sekunden nicht Standard.
1
Update: Es gibt einen solchen Wartebefehl: gpio wfi 0 risingwürde auf eine ansteigende Flanke an Pin Null warten, die nicht belegt ist (gemäß der Verdrahtungs-Pi-Site ).
CodenameLambda
3

Was Sie haben, ist als Besetztschleife bekannt . Ihre Schleife verbraucht kaum Speicher, aber viel CPU. Dies wird normalerweise durch Hinzufügen sleepzum Schleifenkörper gemildert .

while (true)
do
        if [ `gpio read 0` -eq 1 ]
        then
                echo passowrd | sudo -S reboot
                break
        fi
        sleep 1
done &

Die Beseitigung der Besetztschleife hängt davon ab, was sie gpiotut. Es gibt Systemaufrufe wie select(), die blockieren können, bis ein Dateideskriptor bereit ist.

In Bezug auf die Effizienz wird der Befehl ()um den trueBefehl herum tatsächlich truein einer Unterschale ausgeführt. Dies ist nicht erforderlich und kann wie folgt besser ausgedrückt werden:

while (( $(gpio read 0) != 1 )); do
    sleep 1
done
echo passowrd | sudo -S reboot
Jordanm
quelle
-1

Versuche Folgendes:

while ! gpio read 0 ; do
    sleep 1
done
echo password | sudo -S reboot
Gast
quelle