Mengoptimalkan loop `while`

8

Saya telah membuat skrip mini untuk mem-boot ulang Raspberry Pi saya dengan menekan satu tombol. Script hanya menggunakan wiringPi (perintah gpio) untuk mengatur pin 0 (pin 17 dalam urutan penomoran Raspberry Pi standar) untuk memasukkan, dan kemudian membaca nilainya sampai satu (ketika tombol ditekan atau ditekan).

Ini skrip saya:

gpio mode 0 in

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

Script berfungsi dengan baik dan semuanya.

Namun, bagi Anda yang tidak terbiasa dengan Pi, ia datang dengan sumber daya perangkat keras yang sangat terbatas (termasuk memori 512 MB) yang dapat dengan mudah dikonsumsi oleh loop seperti yang saya gunakan.

Apa yang saya coba capai di sini adalah menemukan cara lain untuk bash untuk mengetahui kapan nilainya telah berubah dari 0menjadi 1tanpa harus mendedikasikan lebih seperti loop tanpa syarat untuk itu. Apakah ini bisa dilakukan? Silakan bagikan ide Anda.

Fadi Hanna AL-Kass
sumber
3
Sudahkah Anda mempertimbangkan untuk menggunakan interupsi untuk menangani acara perangkat keras atau apakah benar-benar tidak mungkin dalam kasus Anda? adafruit.com/blog/2013/03/29/…
lgeorget
3
Saya hanya akan menambahkan panggilan tidur yang harus membatasi konsumsi memori
strugee
@lgeorget interupsi akan ideal, mungkin tidak ditangani dari bash sekalipun.
jordanm
Loop ini tidak mengkonsumsi memori.
OrangeDog

Jawaban:

11

Analisis dan solusi modern

Script adalah loop sibuk: ia terus membaca pin GPIO berulang kali. Tidak memakan banyak memori tetapi membuat CPU sibuk.

Anda harus mengatur pin GPIO dalam mode tepi. The gpioutilitas memiliki wfi(menunggu interrupt) perintah yang dapat Anda gunakan untuk bereaksi terhadap pemicu tepi. ( gpio wfiTidak ada kembali ketika pertanyaan diajukan.)

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

Solusi Python

Ada perpustakaan Python untuk akses GPIO , yang mendukung mode tepi. Berikut adalah beberapa kode Python yang belum teruji yang harus melakukan apa yang Anda inginkan.

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

Kiat shell tambahan

(true)bisa dituliskan saja true. Kurung membuat subproses, yang sama sekali tidak perlu.

`gpio read 0`harus dalam tanda kutip ganda. Tanpa tanda kutip, output perintah diperlakukan sebagai daftar pola wildcard nama file. Dengan tanda kutip ganda, output dari perintah diperlakukan sebagai string. Selalu beri tanda kutip ganda di sekitar pergantian perintah dan pergantian variabel: "$(some_command)", "$some_variable". Juga, Anda harus menggunakan sintaks $(…)daripada `…`: itu memiliki arti yang persis sama, tetapi sintaks backquote memiliki beberapa kebiasaan parsing ketika perintah kompleks. Jadi:if [ "$(gpio read 0)" -eq 1 ]

Jangan letakkan kata sandi root di skrip. Jika skrip berjalan sebagai root, Anda tidak perlu sudo sama sekali. Jika skrip tidak berjalan sebagai root, maka beri pengguna izin menjalankan skrip sudo reboottanpa memberikan kata sandi. Jalankan visudodan tambahkan baris berikut:

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

Perhatikan bahwa jika ada entri untuk pengguna yang sama dalam file sudoers yang memerlukan kata sandi, NOPASSWDentri tersebut harus muncul setelah itu.

Setelah Anda memicu reboot, Anda tidak perlu memutus loop, sistem akan tetap berhenti.

Jika Anda memutuskan untuk tetap menggunakan skrip shell ini, dan versi Anda gpioterlalu tua untuk memiliki wfisub- perintah , inilah versi yang disempurnakan yang hanya memeriksa status tombol setiap detik. Perhatikan bahwa karena pin hanya dibaca sekali per detik, ini berarti Anda harus terus menekan tombol setidaknya selama satu detik untuk memastikan bahwa acara tersebut diambil.

gpio mode 0 in
while sleep 1; do
    if [ "$(gpio read 0)" -eq 1 ]; then
        reboot
    fi
done &
Gilles 'SANGAT berhenti menjadi jahat'
sumber
1
Sebagai contoh terakhir Anda, Anda bisa tidur selama sepersekian detik . Sesuatu seperti 0.1atau mungkin 0.2harus dapat mendeteksi penekanan yang sangat singkat dan masih menyisakan banyak waktu CPU untuk utas lainnya.
Bob
@ Bob: Walaupun portabilitas sepertinya tidak masalah dalam hal ini, sleep(1)penerimaan fraksional detik tidak standar.
1
Pembaruan: Ada perintah tunggu seperti itu: gpio wfi 0 risingakan menunggu kenaikan tepi pada pin nol, yang tidak sibuk (menurut situs pi kabel ).
CodenameLambda
3

Apa yang Anda miliki dikenal sebagai loop sibuk . Loop Anda tidak akan menghabiskan hampir semua memori, tetapi akan mengkonsumsi banyak CPU. Ini biasanya dikurangi dengan menambahkan sleepke tubuh loop.

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

Menyingkirkan loop yang sibuk akan tergantung pada apa yang gpiodilakukan. Ada panggilan sistem seperti select(), yang dapat memblokir hingga deskriptor file siap.

Sejauh efisiensi, perintah ()di sekitar truesebenarnya dijalankan truedalam subkulit. Ini tidak diperlukan, dan dapat diungkapkan dengan lebih baik dengan yang berikut:

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

Coba yang berikut ini:

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