MECHATRAXのブログ

弊社製品やサービスに関するブログです。企画や開発の際の参考になれば幸いです。
slee-Pi 3のWatchdog timerでラズパイのプロセス監視【異常検知ー実装】

slee-Pi 3のWatchdog timerでラズパイのプロセス監視【異常検知ー実装】

2021.04.13@kiyonaga

【2025年10月追記】
本ブログは、2025年10月に下記環境にて動作確認済みです。
 sleepi3-bookworm-lite-arm64-20250520.md

今回は slee-Pi 3 のウォッチドッグタイマを利用したプロセス監視を行います。
前回【異常動作検知による監視】は動作確認でしたが、今回はより実用性を求めてサービスファイルでNice 値などは設定しません
※作業環境と事前準備は こちら をご覧ください。

[関連ブログ]

slee-Pi 3 のウォッチドッグタイマを利用したプロセス監視
OOM 状態からの即時復帰
正常動作検知による監視
異常動作検知による監視
【異常動作検知-実装編】← 本ブログ

[目的と方法]

目的:停止したくないプログラムが停止した場合に、クリーンな状態で再開させたい。
方法:プログラム内にハートビートを組込んでおき、ウォッチドッグタイマが異常検知した際に再起動させる。

[手順1]サービスファイル作成

test-heartbeat.service を作成します。

[Unit]
Description=sleepi3 test heartbeat
DefaultDependencies=no 
After=sysinit.target

[Service]
Type=simple
ExecStart=/opt/mtx/test-heartbeat.py

[Install]
WantedBy=sysinit.target

[手順2]ハートビートを内包するスクリプトを作成

今回は、素数計算中にハートビートを発生させます。

#!/usr/bin/env python3

import os
import time
import math
from datetime import datetime
from datetime import timedelta

LED_PATH = '/sys/class/leds/sleepi:led0/brightness'
PRIME_PATH = '/opt/mt/prime-num'

class LedBlink():
    def __init__(self,num,dt):
        self.num = num
        self.dt = dt

    def blink(self):
        now = datetime.now()
        if now - self.dt > timedelta(seconds=1):
            with open(LED_PATH,mode="w") as f:
                f.write(self.num)
            self.dt = now
            if self.num == "0":
                self.num = "1"
            else:
                self.num = "0"

def check_files():
     init = 3

     if not os.path.isfile(PRIME_PATH):
         return init

     try:
         with open(PRIME_PATH) as f:
             saved_num = f.readline().strip()
     except Exception:
        return init

     if saved_num.isdigit():
        return int(saved_num)
     else:
        return init

def save_num(PATH,i):
    with open(PATH,mode="w") as f:
         f.write(str(i))
         f.flush()
         os.fsync(f.fileno())

def prime_calc(num,Led):
    i = num
    try:
        while True:
            Led.blink()
            j = 3
            flag = 0
            r = math.sqrt(i)
            while j<=r:
                if i%j==0:
                    flag = 1
                    Led.blink()
                    break
                j+=2
            if flag==0:
                save_num(PRIME_PATH,i)
            i+=2
    finally:
        print(f"\n[!] Saved current number: {i}")
        save_num(PRIME_PATH, i)
        exit(1)

def main():
    Led = LedBlink(num="0",dt=datetime.now())
    num = check_files()
    prime_calc(num,Led)

if __name__ == "__main__":
    main()
else:
    print("FILE NAME ERROR")

[手順3]sleepi3-heartbeat.service を停止して自作ハートビートを起動する

sudo systemctl stop sleepi3-heartbeat.service
sudo systemctl disable sleepi3-heartbeat.service
sudo systemctl enable test-heartbeat.service
sudo systemctl restart test-heartbeat.service

※ sleepi3-heartbeat.service は sleepi3-utils に含まれているサービスです。

[手順4]OOM Killer 発生用プログラム作成

こちら のブログを参考に oom.c を作成します。

※ コード中に (&amp;t) のような HTML エンティティが含まれています。コードを使用する際は該当部分を取り除いてご利用ください。

[手順5]復帰確認

先ほど作成した oom.c をコンパイル後、実行します。

ハートビート信号はしばらく続きますが、最終的には OOM によりハートビート停止 → ハードウェアリセットする様子が slee-Pi 3 のLEDで確認できます。

再起動後、下記コマンドで再起動要因を確認してください。watchdog であれば正しくハードウェアリセットできています。

$ sleepi3ctl -g wakeup-flag
watchdog

[まとめ]

今回は slee-Pi 3 のウォッチドッグタイマによるプロセス監視機能を利用しました。
監視しているプロセスが OOM Killer で強制終了された際に、ハードウェアリセットをかける仕組みです。

[補足]ハードウェアリセットがうまく機能しない場合

カーネルの OOMK やスワップメモリにより OOM Killer 発生用プログラムが意図しない動作をしている可能性があります。以下のようにスワップメモリを無効化し、OOMK 発生用プログラムが生き残るようにしてください。

スワップメモリ無効化

$ sudo bash -c "echo 1 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
1
$ sudo swapoff -a

OOMK プログラム生存設定

$ sudo nice -n -20 oom
$ Ctrl + Z で一時的に停止
$ sudo bash -c 'echo -1000 > /proc/$(pidof oom)/oom_score_adj'
$ fg
sudo nice -n -20 oom

< ブログ一覧へ戻る

関連製品はこちら

関連記事はこちら