Tối ưu hóa một vòng lặp `while`


8

Tôi đã tạo một tập lệnh nhỏ để khởi động lại Raspberry Pi của mình chỉ bằng cách ấn nút. Kịch bản chỉ đơn giản sử dụng hệ thống dây điện (lệnh gpio) để đặt chân 0 (chân 17 theo thứ tự đánh số tiêu chuẩn Raspberry Pi) để nhập, sau đó đọc giá trị cho đến khi nó (khi đó, khi nhấn hoặc giữ nút).

Đây là kịch bản của tôi:

gpio mode 0 in

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

Kịch bản hoạt động tốt và tất cả mọi thứ.

Tuy nhiên, đối với những người bạn không quen thuộc với Pi, nó đi kèm với tài nguyên phần cứng rất hạn chế (bao gồm 512 MB bộ nhớ) có thể dễ dàng sử dụng bởi một vòng lặp như vòng lặp tôi đang sử dụng.

Những gì tôi đang cố gắng để đạt được ở đây là để tìm một cách khác để bash để tìm hiểu khi giá trị đã thay đổi từ 0để 1mà không cần phải dành một giống như một vòng lặp vô điều kiện cho nó. Đây có phải là có thể làm được? Hãy chia sẻ ý tưởng của bạn.


3
Bạn đã cân nhắc sử dụng các ngắt để xử lý sự kiện phần cứng hay hoàn toàn không thể trong trường hợp của bạn? adafbean.com/blog/2013/03/29/ từ
lgeorget

3
Tôi chỉ cần thêm một cuộc gọi ngủ sẽ hạn chế mức tiêu thụ bộ nhớ
strugee

@lgeorget ngắt sẽ là lý tưởng, có lẽ không được xử lý từ bash.
jordanm

Vòng lặp này không tiêu thụ bộ nhớ.
OrangeDog

Câu trả lời:


11

Phân tích và giải pháp hiện đại

Kịch bản là một vòng lặp bận rộn: nó cứ đọc đi lặp lại các chân GPIO. Nó không tiêu tốn nhiều bộ nhớ nhưng nó khiến CPU bận rộn.

Bạn nên đặt chân GPIO ở chế độ cạnh. Các gpiotiện íchwfi(chờ ngắt) lệnh mà bạn có thể sử dụng để phản ứng với một kích hoạt cạnh. ( gpio wfikhông tồn tại trở lại khi câu hỏi được hỏi.)

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

Một giải pháp Python

Có một thư viện Python để truy cập GPIO , hỗ trợ chế độ cạnh. Đây là một số mã Python hoàn toàn chưa được kiểm tra sẽ làm những gì bạn muốn.

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

Mẹo bổ sung vỏ

(true)có thể được viết chỉ true. Các dấu ngoặc đơn tạo ra một quy trình con, điều này là hoàn toàn không cần thiết.

`gpio read 0`nên trong dấu ngoặc kép. Không có dấu ngoặc kép, đầu ra của lệnh được coi là một danh sách các mẫu ký tự đại diện tên tệp. Với dấu ngoặc kép, đầu ra của lệnh được coi là một chuỗi. Luôn đặt dấu ngoặc kép quanh thay thế lệnh và thay thế biến: "$(some_command)", "$some_variable". Ngoài ra, bạn nên sử dụng cú pháp $(…)chứ không phải `…`: nó có cùng một nghĩa chính xác, nhưng cú pháp backquote có một số quirks phân tích khi lệnh phức tạp. Như vậy:if [ "$(gpio read 0)" -eq 1 ]

Đừng đặt mật khẩu gốc trong tập lệnh. Nếu tập lệnh đang chạy bằng root, bạn hoàn toàn không cần sudo. Nếu tập lệnh không chạy dưới quyền root, thì hãy cho phép người dùng chạy tập lệnh để chạy sudo rebootmà không cần cung cấp mật khẩu. Chạy visudovà thêm dòng sau:

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

Lưu ý rằng nếu có một mục nhập cho cùng một người dùng trong tệp sudoers yêu cầu mật khẩu, NOPASSWDmục nhập đó phải xuất hiện sau.

Khi bạn kích hoạt khởi động lại, bạn không cần phải phá vỡ vòng lặp, hệ thống sẽ dừng lại.

Nếu bạn quyết định tiếp tục sử dụng tập lệnh shell này và phiên bản của bạn gpioquá cũ để có wfitiểu ban, thì đây là phiên bản cải tiến chỉ kiểm tra trạng thái nút mỗi giây. Lưu ý rằng vì pin chỉ được đọc một lần mỗi giây, điều này có nghĩa là bạn cần giữ nút này trong ít nhất một giây để chắc chắn rằng sự kiện đã được chọn.

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

1
Ví dụ cuối cùng của bạn, bạn có thể ngủ trong một phần của giây . Một cái gì đó giống 0.1hoặc có thể 0.2sẽ có thể phát hiện các lần nhấn rất ngắn và vẫn còn nhiều thời gian CPU cho các luồng khác.
Bob

@Bob: Mặc dù tính di động có thể không thành vấn đề trong trường hợp này, sleep(1)nhưng việc chấp nhận một số giây nhỏ là không chuẩn.

1
Cập nhật: Có một lệnh chờ như vậy: gpio wfi 0 risingsẽ chờ một cạnh tăng trên chân số 0, không bận (theo trang web nối dây ).
CodenameLambda

3

Những gì bạn có được gọi là một vòng lặp bận rộn . Vòng lặp của bạn sẽ không tiêu tốn hầu hết bộ nhớ, nhưng nó sẽ tiêu tốn rất nhiều CPU. Điều này thường được giảm nhẹ bằng cách thêm sleepvào phần thân của vòng lặp.

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

Thoát khỏi vòng lặp bận rộn sẽ phụ thuộc vào những gì gpio. Có các cuộc gọi hệ thống như select(), có thể chặn cho đến khi một bộ mô tả tệp đã sẵn sàng.

Về hiệu quả, lệnh ()xung quanh truethực sự thực thi truetrong một lớp con. Điều này là không cần thiết, và có thể được thể hiện tốt hơn với những điều sau đây:

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

-1

Hãy thử như sau:

while ! gpio read 0 ; do
    sleep 1
done
echo password | sudo -S reboot
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.