`while`ループの最適化


8

ボタンを押すとRaspberry Piを再起動するミニスクリプトを作成しました。スクリプトは、wiringPi(gpioコマンド)を使用してピン0(Raspberry Piの標準の番号付け順序ではピン17)を入力に設定し、値が1になるまで(つまり、ボタンが押されるか押し下げられるまで)読み取ります。

これが私のスクリプトです:

gpio mode 0 in

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

スクリプトはすべて正常に機能します。

ただし、Piに慣れていない方のために、非常に限られたハードウェアリソース(512 MBのメモリを含む)が付属しているため、私が使用しているようなループで簡単に消費できます。

ここで達成しようとしているのは、bashが値がから0に変わったときに1、無条件ループのように専用にする必要なしに、それを見つける別の方法を見つけることです。これは可能ですか?あなたのアイデアを共有してください。


3
割り込みを使用してハードウェアイベントを処理することを検討しましたか、それともあなたの場合は絶対に不可能ですか?adafruit.com/blog/2013/03/29/...
lgeorget

3
メモリ消費を制限するスリープコールを追加するだけです
strugee 2013

@lgeorget割り込みは理想的ですが、おそらくbashからは処理されません。
ヨルダン2013

このループはメモリを消費しません。
OrangeDog 2013

回答:


11

分析と最新のソリューション

スクリプトはビジーループです。GPIOピンを繰り返し読み取り続けます。多くのメモリを消費しませんが、CPUをビジー状態に保ちます。

GPIOピンをエッジモードに設定する必要があります。gpioユーティリティがありwfiますが、エッジトリガに反応するために使用できるコマンド(割り込み待ちを)。(gpio wfi質問されたときは存在しませんでした。)

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

Pythonソリューション

エッジモードをサポートするGPIOアクセス用のPythonライブラリがあります。以下は、完全にテストされていないPythonコードです。

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

追加のシェルのヒント

(true)ただ書くことができますtrue。括弧はサブプロセスを作成しますが、これは完全に不要です。

`gpio read 0`二重引用符で囲む必要があります。引用符がないと、コマンドの出力はファイル名のワイルドカードパターンのリストとして扱われます。二重引用符を使用すると、コマンドの出力は文字列として扱われます。コマンドの置換と変数の置換は常に二重引用符で囲みます:"$(some_command)""$some_variable"。また、構文$(…)ではなく構文を使用する必要があります`…`。これはまったく同じ意味ですが、コマンドが複雑な場合、バッククォート構文には解析の癖があります。したがって:if [ "$(gpio read 0)" -eq 1 ]

スクリプトにrootパスワードを含めないでください。スクリプトがrootとして実行されている場合、sudoはまったく必要ありません。スクリプトがrootとして実行されていない場合は、スクリプトを実行しているユーザーsudo rebootに、パスワードを指定せずに実行する権限を与えます。visudo次の行を実行して追加します。

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

sudoersファイルに同じユーザーのエントリがあり、パスワードが必要な場合は、NOPASSWDエントリを後にする必要があることに注意してください。

再起動をトリガーしたら、ループを解除する必要はありません。システムはとにかく停止します。

このシェルスクリプトを使い続けることに決め、バージョンがgpio古すぎてwfiサブコマンドを使用できない場合は、ボタンの状態を毎秒チェックするだけの改良版を次に示します。ピンは1秒に1回しか読み取られないため、イベントが確実にピックアップされるようにするには、ボタンを少なくとも1秒間押し続ける必要があることに注意してください。

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

1
最後の例では、ほんの一瞬だけ眠ることができます。非常に短いプレスを検出するような、0.1または多分0.2それは他のスレッドのために十分なCPU時間を残せるはずです。
ボブ

@ボブ:この場合、移植性は重要ではない可能性がありますがsleep(1)、秒の小数部の受け入れは非標準です。

1
更新:このような待機コマンドがあります。gpio wfi 0 risingピン0の立ち上がりエッジを待機しますが、これはビジーではありません(配線piサイトによると)。
CodenameLambda

3

あなたが持っているものはビジーループとして知られています。ループはほとんどメモリを消費しませんが、CPUを大量に消費します。これは通常sleep、ループの本体に追加することで軽減されます。

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

ビジーループを解消する方法は、何をするかによって異なりますgpio。などのシステムコールがありselect()、ファイル記述子の準備ができるまでブロックできます。

効率に関しては()trueコマンドのまわりは実際にtrueサブシェルで実行されます。これは不要であり、次のように表現することができます。

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

-1

以下を試してください:

while ! gpio read 0 ; do
    sleep 1
done
echo password | sudo -S reboot
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.