Ottimizzare un ciclo `while`


8

Ho creato un mini script per riavviare il mio Raspberry Pi semplicemente premendo un pulsante. Lo script utilizza semplicemente un cablaggioPi (comando gpio) per impostare il pin 0 (pin 17 nell'ordine di numerazione standard di Raspberry Pi) da inserire, quindi legge il valore fino a quando non lo è (ovvero quando il pulsante viene premuto o tenuto premuto).

Ecco la mia sceneggiatura:

gpio mode 0 in

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

La sceneggiatura funziona bene e tutto il resto.

Tuttavia, per quelli di voi che non hanno familiarità con il Pi, viene fornito con risorse hardware molto limitate (inclusi 512 MB di memoria) che possono essere facilmente consumate da un loop come quello che sto usando.

Quello che sto cercando di ottenere qui è trovare un altro modo per bash di scoprire quando il valore è cambiato da 0a 1senza dover dedicare un ciclo più simile a un ciclo incondizionato. È fattibile? Per favore, condividi le tue idee.


3
Hai preso in considerazione l'utilizzo di interrupt per gestire eventi hardware o è assolutamente impossibile nel tuo caso? adafruit.com/blog/2013/03/29/…
lgeorget il

3
Vorrei solo aggiungere una chiamata di sonno che dovrebbe limitare il consumo di memoria
strugee

Gli interrupt @lgeorget sarebbero l'ideale, probabilmente non gestiti da bash.
Giordania,

Questo loop non consuma memoria.
OrangeDog,

Risposte:


11

Analisi e soluzione moderna

Lo script è un circuito affollato: continua a leggere ripetutamente i pin GPIO. Non consuma molta memoria ma mantiene occupata la CPU.

È necessario impostare il pin GPIO in modalità edge. L' gpioutilità ha un wficomando (wait for interrupt) che puoi usare per reagire a un trigger di fronte. ( gpio wfinon esisteva quando è stata posta la domanda.)

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

Una soluzione Python

Esiste una libreria Python per l'accesso GPIO , che supporta la modalità edge. Ecco alcuni codici Python completamente non testati che dovrebbero fare quello che vuoi.

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

Ulteriori suggerimenti sulla shell

(true)potrebbe essere scritto solo true. Le parentesi creano un sottoprocesso, che è completamente inutile.

`gpio read 0`dovrebbe essere tra virgolette doppie. Senza virgolette, l'output del comando viene trattato come un elenco di modelli jolly di nomi di file. Con virgolette doppie, l'output del comando viene trattato come una stringa. Metti sempre le virgolette doppie tra le sostituzioni di comandi e le sostituzioni variabili: "$(some_command)", "$some_variable". Inoltre, è necessario utilizzare la sintassi $(…)anziché `…`: ha esattamente lo stesso significato, ma la sintassi del backquote ha alcune stranezze di analisi quando il comando è complesso. Così:if [ "$(gpio read 0)" -eq 1 ]

Non inserire la password di root nello script. Se lo script è in esecuzione come root, non è necessario sudo. Se lo script non è in esecuzione come root, autorizza l'utente a eseguirlo sudo rebootsenza fornire una password. Esegui visudoe aggiungi la seguente riga:

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

Nota che se nel file sudoers è presente una voce per lo stesso utente che richiede una password, la NOPASSWDvoce deve venire dopo.

Una volta attivato il riavvio, non è necessario interrompere il ciclo, il sistema si arresterà comunque.

Se decidi di continuare a utilizzare questo script di shell e la tua versione di gpioè troppo vecchia per avere il wfisottocomando, ecco una versione migliorata che controlla solo lo stato del pulsante ogni secondo. Si noti che poiché il pin viene letto solo una volta al secondo, ciò significa che è necessario tenere premuto il pulsante per almeno un secondo per essere sicuri che l'evento venga raccolto.

gpio mode 0 in
while sleep 1; do
    if [ "$(gpio read 0)" -eq 1 ]; then
        reboot
    fi
done &

1
Per il tuo ultimo esempio, potresti dormire per una frazione di secondo . Qualcosa di simile 0.1o forse 0.2dovrebbe essere in grado di rilevare pressioni molto brevi e lasciare comunque molto tempo della CPU per altri thread.
Bob,

@Bob: anche se la portabilità probabilmente non ha importanza in questo caso, sleep(1)l'accettazione di un numero frazionario di secondi non è standard.

1
Aggiornamento: esiste un tale comando wait: gpio wfi 0 risingaspetterebbe un fronte di salita sul pin zero, che non è occupato (secondo il sito di cablaggio più ).
Nome in codice

3

Quello che hai è noto come un ciclo occupato . Il tuo loop non consumerà quasi alcuna memoria, ma consumerà molta CPU. Questo è in genere mitigato aggiungendo sleepal corpo del loop.

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

Sbarazzarsi del circuito occupato dipenderà da cosa gpiofa. Esistono chiamate di sistema come select(), che possono bloccare fino a quando un descrittore di file non è pronto.

Per quanto riguarda l'efficienza, ()il truecomando around viene effettivamente eseguito truein una subshell. Ciò non è necessario e può essere espresso meglio con quanto segue:

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

-1

Prova quanto segue:

while ! gpio read 0 ; do
    sleep 1
done
echo password | sudo -S reboot
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.