Otimizando um loop 'while'

7

Eu criei um mini script para reiniciar o meu Raspberry Pi ao apertar um botão. O script simplesmente usa wiringPi (comando gpio) para definir o pino 0 (pino 17 na ordem de numeração padrão do Raspberry Pi) como entrada e depois lê o valor até que seja um (isto é, quando o botão é pressionado ou pressionado).

Aqui está meu script:

gpio mode 0 in

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

O script funciona bem e tudo mais.

No entanto, para aqueles que não estão familiarizados com o Pi, ele vem com recursos de hardware muito limitados (incluindo 512 MB de memória) que podem ser facilmente consumidos por um loop como o que estou usando.

O que estou tentando alcançar aqui é encontrar outra maneira de o bash descobrir quando o valor mudou de 0 para 1 sem precisar dedicar mais um loop incondicional a ele. Isso é factível? Por favor, compartilhe suas idéias.

    
por Fadi Hanna AL-Kass 17.07.2013 / 02:06

3 respostas

9

Análise

O script é um loop ocupado: ele continua lendo os pinos do GPIO repetidamente. Não consome muita memória, mas mantém a CPU ocupada.

Você deve definir o pino GPIO no modo de borda. O autor do WiringPi está considerando adicionar um comando wait ao gpio utility, mas atualmente não há como reagir a um trigger de borda a partir de um script de shell.

Uma solução Python

Existe uma biblioteca Python para acesso ao GPIO , que suporta o modo de borda. Aqui está um código Python completamente não testado que deve fazer o que você quiser.

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

Dicas adicionais sobre o shell

(true) pode ser escrito apenas true . Os parênteses criam um subprocesso, o que é completamente desnecessário.

'gpio read 0' deve estar entre aspas duplas. Sem aspas, a saída do comando é tratada como uma lista de padrões de caractere curinga de nome de arquivo. Com aspas duplas, a saída do comando é tratada como uma string. Sempre coloque aspas duplas em torno das substituições de comandos e substituições de variáveis: "$(some_command)" , "$some_variable" . Além disso, você deve usar a sintaxe $(…) em vez de '…' : ela tem exatamente o mesmo significado, mas a sintaxe de backquote tem algumas peculiaridades de análise quando o comando é complexo. Assim: if [ "$(gpio read 0)" -eq 1 ]

Não coloque a senha de root no script. Se o script estiver sendo executado como root, você não precisará do sudo. Se o script não estiver sendo executado como root, conceda ao usuário que executa o script a permissão para executar sudo reboot sem fornecer uma senha. Execute visudo e adicione a seguinte linha:

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

Observe que, se houver uma entrada para o mesmo usuário no arquivo sudoers que requer uma senha, a entrada NOPASSWD deve vir depois.

Quando você acionar uma reinicialização, não será necessário interromper o loop, o sistema parará de qualquer maneira.

Se você decidir continuar usando este script de shell, aqui está uma versão melhorada que só verifica o estado do botão a cada segundo. Observe que, como o pino é lido apenas uma vez por segundo, isso significa que você precisa manter o botão pressionado por pelo menos um segundo para ter certeza de que o evento foi selecionado.

gpio mode 0 in
while sleep 1; do
    if [ "$(gpio read 0)" -eq 1 ]; then
        reboot
    fi
done &
    
por 17.07.2013 / 03:12
3

O que você tem é conhecido como loop ocupado . Seu loop não consumirá quase nenhuma memória, mas consumirá bastante CPU. Isso normalmente é mitigado pela adição de sleep ao corpo do loop.

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

Livrar-se do loop ocupado dependerá do que o gpio faz. Existem chamadas do sistema, como select() , que podem ser bloqueadas até que um descritor de arquivo esteja pronto.

Em termos de eficiência, o comando () em torno do true na verdade executa true em um subshell. Isso não é necessário e pode ser melhor expresso com o seguinte:

while (( $(gpio read 0) != 1 )); do
    sleep 1
done
echo passowrd | sudo -S reboot
    
por 17.07.2013 / 02:22
-1

Tente o seguinte:

while ! gpio read 0 ; do
    sleep 1
done
echo password | sudo -S reboot
    
por 29.09.2014 / 13:22