0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Raspberry Piでプログラムを自動起動(1)_systemd

Last updated at Posted at 2023-06-02

Raspberry Piで自動起動する方法

参考の記事によると大きく5つがあるようです。
本記事はsystemedを使った自動起動を試行し
print関数の内容表示やstop時に例外処理を実行する方法も併せてメモしておきます。

  • /etc/rc.local
  • autostart     ←別の記事
  • crontab @reboot
  • /etc/init.d
  • systemd      ←今回の記事

紹介内容

  • RaspberryPiを起動した際に、プログラムをsystemedで自動起動させる。
  • print関数の内容を表示
  • stopコマンド時に例外処理実行

1.自動起動の手段

systemdが主流、推奨のようなのでsystemdで実装。
ココのサイトの手順で実行。

systemed
[Unit]
Description=Play sound effects at startup
After=network.target

[Service]
ExecStart=/usr/bin/python3 start_app.py
WorkingDirectory=/home/pi/myapp

[Install]
WantedBy=multi-user.target

2.応用編 

 上記のファイルを追記、改変して

  1. print関数の内容をstatusで表示。 →.serviceに記載
  2. stop時に例外処理を実行。 → .pyに記載
     (リソースの開放などができず、再度起動した際にエラーになることを防ぐため)
  3. 実行条件の指定
      マウントが完了した後に実行など

をやってみます。

追加したsystemファイルは下記のようになります。

systemed
[Unit]
Description = A sample unit file.
After=local-fs.target #ローカルファイルシステムへのマウントが完了した後に実行
ConditionPathExists=/opt/RasPiSample/bin

[Service]
ExecStart=/opt/RasPiSample/bin/autoexec.sh
Environment=PYTHONUNBUFFERED=1 #print関数の内容をログに残す
Restart=no
Type=simple
StandardOutput=journal+console #コンソールにも出力を表示させる

[Install]
WantedBy=multi-user.target

解説をしていきます。

1.print関数の内容をログに残す

 [Service]セクションに以下の行を追加すると print 関数の内容をログに残すことができます。

systemed
[Service]
Environment=PYTHONUNBUFFERED=1

systemdで実行中のPythonのprint関数の出力をログに出力させてみた

2.stopコマンドを検知し、例外処理を実行

リファクタリングはしてません。

Ctl+C で.pyプログラムを終了できるコード例
start_app.py
import subprocess
import time
import signal

class TerminatedExecption(Exception):
    pass

def raise_exception(*_):
    raise TerminatedExecption()

def end_process():
    try:
        print('--- start end process ---')
        time.sleep(2)
    except Exception as e:
        raise
    print('--- success end process ---')

if __name__ == '__main__':
    # set signal handler to detect to be stopped by systemd
    signal.signal(signal.SIGTERM, raise_exception)

    print('--- start main process ---')
    subprocess.run(['aplay', 'start.wav'])
    try:
        print('--- start main loop ---')
        count=0
        while True:
            count+=1
            print('count= ',count)
            time.sleep(1)

    # (1) if ctrl-C is pushed, stop program nomally
    except KeyboardInterrupt:
        print("KeyboardI]nterrupt: stopped by keyboard input (ctrl-C)")
        time.sleep(1)
        pass

    # (2) if stopped by systemd, stop program nomally
    except TerminatedExecption:
        print("TerminatedExecption: stopped by systemd")
        time.sleep(1)
        pass
    end_process()

main関数の最初で、SIGTERMを受信した際の動作を、raise_exception関数を呼び、TerminatedExecptionをraiseするように変更。
下記サイトがわかりやすくまとめられていました。
 ※【systemd】Pythonでstopを検知する&例外時に自動で再起動する

3.実行条件の指定

3.1 ローカルファイルシステムへのマウントが完了した後に実行

通常, 組込Linuxの場合は, サードパーティーのプログラムを導入して自動起動させるはずですので, /opt/パッケージ名のディレクトリは, 存在することを期待して良いと考えられますが, 念のため, ローカルファイルシステムへのマウントが完了した後に実行されることを保証するため, [unit]内に下記を追記します。

systemed
[unit]
After=local-fs.target

ローカルファイルシステムへのマウントが完了した後に実行されることを保証する
After=には, 以下のような実行条件を記載することが出来ます.

  • local-fs.target 装置に直接繋がったストレージのマウントが完了した後で実行する
  • network-online.target ネットワークを利用可能になった後で実行する
  • remote-fs.target ネットワークファイルシステム(NFSなど)を利用可能になった後で実行する
  • nss-lookup.target DNSなどのホスト/ネットワークサービスを利用可能になった後で実行する
    上記の場合, local-fs.targetの完了後, つまり, 装置に直接繋がったストレージのマウントが完了した後でプログラムを実行する指示になります.

3.2 ファイルシステムのマウントに失敗した場合に正しくエラーを返す

ファイルシステムのマウントに何らかの理由で失敗した場合に正しくエラーを返せるようにする

systemed
[unit]
ConditionPathExists=/opt/RasPiSample/bin

3.3 systemdにコンソールにも出力を表示させるように指示する方法

下記を記載することでコンソール出力とSystemD内のログの双方にprint文の内容が出力されます.

systemed
[Service]
StandardOutput=syslog+console

systemdを用いたプログラムの自動起動

4.serviceに実行権限を与える

必要に応じてPython のコードに対して実行権限を与えます。

sudo chmod 755 /opt/***.py

その後、systemd で Python のコードを実行してみます。

sudo systemctl daemon-reload
sudo systemctl start ***.service

systemd 実行後に

jornalctl -u ***.service

を実行してログの中にHello Worldと表示されれば成功です。

5.サービスの確認

サービス有効化
sudo systemctl enable start_app.service
サービス無効化
sudo systemctl disable start_app.service
サービスの状態やログ確認
sudo systemctl status start_app.service

起動している場合
image.png

停止している
image.png

参考情報

既にある.serviceファイルを編集する場合

vimで編集
cd /etc/systemd/system
sudo vim start_app.service

定刻でのプログラム自動起動

1.timerが始動していることを確認

ステータスの確認
sudo systemctl status myscript.timer

2.タイマーの一覧と設定状況

タイマーの一覧と設定状況
sudo systemctl list-timers

上で起動した****.timerの次回実行時刻、前回実行からの経過時間などが確認できます。

  1. 実行ログの確認
journalctl -e

systemdのログ全般は journalctl コマンドを使用します。

4.無効化

sudo systemctl disable ****.timer

Raspberry Pi で systemd を使ってプログラムを自動実行する
Systemdのtimer設定チートシート(コマンドまとめ)

以上

参考サイト

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?