优化`while`循环


8

我创建了一个迷你脚本,只需按一下按钮即可重新启动Raspberry Pi。该脚本仅使用wireingPi(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找出值何时从更改为01而不必为此专门指定一个无条件循环。这可行吗?请分享您的想法。


3
您是否考虑过使用中断来处理硬件事件,还是绝对不可能?adafruit.com/blog/2013/03/29/...
lgeorget

3
我只需要添加一个睡眠调用即可限制内存消耗
13年

@lgeorget中断将是理想的,尽管可能不是从bash处理。
jordanm

此循环不占用内存。
OrangeDog

Answers:


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条目必须位于后面。

触发重新启动后,无需中断循环,系统仍将停止。

如果您决定继续使用此Shell脚本,并且您的的版本gpio太旧而无法使用wfi子命令,则此版本为改进版本,仅每秒检查一次按钮状态。请注意,由于该引脚每秒只能读取一次,因此这意味着您需要按住按钮至少一秒钟,以确保事件被拾取。

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

1
对于最后一个示例,您可以睡一秒钟的时间。类似0.1或也许0.2应该能够检测到短按的情况,而仍然为其他线程留出大量CPU时间。
鲍勃,

@Bob:虽然在这种情况下可移植性无关紧要,但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()可能会阻塞,直到文件描述符准备好为止。

就效率而言,()around true命令实际上true在子shell中执行。这不是必需的,可以用以下更好地表达:

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.