欢迎光临
我们一直在努力

【Linux实战】从零手搓“进程看门狗“:边缘设备保命指南(附双架构实现)

一、项目背景与目标

在边缘计算场景中(如 RK3588、树莓派),AI 推理程序经常因为内存泄漏、硬件过热等原因半夜闪退。程序一旦挂掉,整个设备就变成了"砖头"——没有告警、没有日志、没人知道它什么时候死的。

本文记录了我使用 Shell 脚本从零打造一个带日志分类、二次验证、自动拉起功能的"看门狗"监控程序,并对比了"常驻循环"与"系统定时器"两种核心架构的实现。

为了测试看门狗,我先写了一个模拟 AI 推理的 Python 脚本(ai_infer.py),让它一直死循环运行:

#!/usr/bin/env python3
import time

# 模拟 AI 推理主循环
while True:
print('AI 推理中…')
time.sleep(2) # 模拟每2秒进行一次推理

二、看门狗架构一:常驻内存(while true + sleep)

这是最经典的守护进程写法,脚本作为一个独立进程常驻后台,每 60 秒巡逻一次。

1. 核心代码(watchdog.sh)

#!/bin/bash

while true
do
ai_infer="/home/panxuqian/watchdog/ai_infer.py"

# 1. 统计进程存活数(核心:必须使用 grep -v grep 排除自身)
process=$(ps -ef | grep ai_infer.py | grep -v grep | wc -l)

# 2. 按日期定义日志文件路径
date_day=$(date "+%Y-%m-%d")
true_logs="/home/panxuqian/watchdog/watchdog_logs/${date_day}_true.log"
false_logs="/home/panxuqian/watchdog/watchdog_logs/${date_day}_false.log"

# 3. 核心状态判断
if [ $process -eq 0 ]; then
echo "$(date '+%H:%M:%S') 进程死亡" >> "$false_logs"
echo "$(date '+%H:%M:%S') 正在拉起进程" >> "$false_logs"

# 后台静默拉起
nohup python3 "$ai_infer" > /dev/null 2>&1 &
sleep 3 # 关键:给新进程留出启动时间

# 二次复查
process_1=$(ps -ef | grep ai_infer.py | grep -v grep | wc -l)
if [ $process_1 -eq 0 ]; then
echo "$(date '+%H:%M:%S') 拉起失败" >> "$false_logs"
else
echo "$(date '+%H:%M:%S') 拉起成功" >> "$false_logs"
fi
else
echo "$(date '+%H:%M:%S') –进程存活" >> "$true_logs"
fi

sleep 60
done

2. 日志分类设计

这里有个小细节值得单独说:日志按 true / false 分成两个文件,而不是一股脑写进同一个文件。

  • *_true.log:只记录"进程存活"的心跳,用于确认看门狗本身在正常工作;
  • *_false.log:只记录"进程死亡 / 拉起成功 / 拉起失败"等异常事件。

这样排查问题时,直接 tail -f *_false.log 就能看到所有异常,不用在成千上万条心跳里翻找。文件名带上日期,也方便按天归档和清理。

三、看门狗架构二:系统定时器(crontab 强力兜底)

如果你担心 while true 脚本本身哪天也崩溃了(或者被误杀),此时常驻进程就彻底失效了——看门狗自己也需要被看护。更稳妥的方案是:让脚本只执行一次检查,然后交给 Linux 系统的 crontab 定时器去每分钟唤醒它。

1. 单次执行脚本(watchdog_cron.sh)

去掉 while 和 sleep,脚本跑完立刻退出,完全零内存占用:

#!/bin/bash

ai_infer="/home/panxuqian/watchdog/ai_infer.py"

process=$(ps -ef | grep ai_infer.py | grep -v grep | wc -l)

date_day=$(date "+%Y-%m-%d")
true_logs="/home/panxuqian/watchdog/watchdog_logs/${date_day}_true.log"
false_logs="/home/panxuqian/watchdog/watchdog_logs/${date_day}_false.log"

if [ $process -eq 0 ]; then
echo "$(date '+%H:%M:%S') 进程死亡" >> "$false_logs"
nohup python3 "$ai_infer" > /dev/null 2>&1 &
sleep 3
process_1=$(ps -ef | grep ai_infer.py | grep -v grep | wc -l)
if [ $process_1 -eq 0 ]; then
echo "$(date '+%H:%M:%S') 拉起失败" >> "$false_logs"
else
echo "$(date '+%H:%M:%S') 拉起成功" >> "$false_logs"
fi
else
echo "$(date '+%H:%M:%S') –进程存活" >> "$true_logs"
fi

2. 配置系统级计时器(Crontab)

敲 crontab -e,输入以下魔法规则(注意必须写绝对路径):

# 每隔 1 分钟执行一次看门狗脚本
*/1 * * * * /home/panxuqian/watchdog/watchdog_cron.sh

注:crontab 最小单位是分钟。如果真需要秒级响应,通常就用方案一的 while 循环。

  • 基础运行展示
    在这里插入图片描述
  • 四、架构对比与核心避坑点

    架构优点缺点适用场景
    常驻循环(while) 响应极快,可精确到秒级 占用微小内存;自身死掉无人救 核心主业务、实时性要求高
    定时器(crontab) 系统级兜底,脚本跑完即退 响应较慢,仅支持分钟级 定时巡检、系统级脚本

    🚨 核心避坑点(面试高频考点)

    • grep 的自匹配问题:必须用 grep -v grep 排除自己,否则永远判定为存活。
    • 变量静态快照:拉起后用 process_1 重新查询,不能用旧的 process。
    • 启动时序竞争:拉起和二次复查之间,必须加 sleep 3,给进程启动留出时间。
    • 中文路径与空格:在 nohup 执行时,尽量避开带空格的绝对路径,否则极易报"找不到文件"错误。

    五、终极进阶:如何防范"假死"?

    无论是 ps 还是 crontab,都只能检测进程"是否存在"。如果进程发生死锁(假死),检测会失效——进程还在,但已经不再干活了。

    终极方案:让 AI 程序每次推理完,往一个文件里更新一次时间戳。看门狗检测时,如果发现时间戳超过 3 分钟没变,直接 kill -9 杀掉并重启。这就是商业级监控软件的底层逻辑。

    先改造 ai_infer.py,让它在每次推理后刷新心跳文件:

    #!/usr/bin/env python3
    import time
    import os

    heartbeat = "/home/panxuqian/watchdog/heartbeat.txt"

    # 模拟 AI 推理主循环
    while True:
    print('AI 推理中…')
    time.sleep(2) # 模拟每2秒进行一次推理

    # 每次推理完成后刷新心跳时间戳
    with open(heartbeat, "w") as f:
    f.write(str(int(time.time())))

    看门狗侧则把心跳判断嵌进常驻循环里,和进程存活判断形成"双保险"——进程没了就拉起,进程还在但心跳停了就强杀重启:

    #!/bin/bash

    while true
    do
    ai_infer="/home/panxuqian/watchdog/ai_infer.py"

    process=$(ps -ef | grep ai_infer.py | grep -v grep | wc -l)

    date_day=$(date "+%Y-%m-%d")
    true_logs="/home/panxuqian/watchdog/watchdog_logs/${date_day}_true.log"
    false_logs="/home/panxuqian/watchdog/watchdog_logs/${date_day}_false.log"

    heartbeat="/home/panxuqian/watchdog/heartbeat.txt"

    if [ $process -eq 0 ]; then
    echo "$(date '+%H:%M:%S') 进程死亡" >> "$false_logs"
    echo "$(date '+%H:%M:%S') 正在拉起进程" >> "$false_logs"

    nohup python3 "$ai_infer" > /dev/null 2>&1 &
    sleep 3 # 给新进程留出启动时间

    process_1=$(ps -ef | grep ai_infer.py | grep -v grep | wc -l)
    if [ $process_1 -eq 0 ]; then
    echo "$(date '+%H:%M:%S') 拉起失败" >> "$false_logs"
    else
    echo "$(date '+%H:%M:%S') 拉起成功" >> "$false_logs"
    fi
    else
    # 进程还在,进一步判断是否假死
    if [ -f "$heartbeat" ]; then
    now=$(date "+%s")
    last=$(stat -c %Y "$heartbeat")
    diff_time=$((now – last))

    if [ $diff_time -gt 180 ]; then # 心跳停止超过 3 分钟
    echo "$(date '+%H:%M:%S') –检测到假死,强制重启" >> "$false_logs"
    pkill -9 -f ai_infer.py
    sleep 1 # 给系统清理进程的时间
    nohup python3 "$ai_infer" > /dev/null 2>&1 &
    sleep 3 # 给进程重启的时间
    echo "$(date '+%H:%M:%S') 假死重启完成" >> "$false_logs"
    else
    echo "$(date '+%H:%M:%S') 心跳正常" >> "$true_logs"
    fi
    else
    # 心跳文件不存在(刚启动或尚未生成)
    echo "$(date '+%H:%M:%S') –等待心跳文件" >> "$true_logs"
    fi
    fi

    sleep 10
    done

    这里有几个容易踩的细节:

    • stat -c %Y 取的是文件修改时间,比读文件内容更省事,也不用担心写入格式问题;
    • pkill -9 之后要 sleep 1,否则旧进程可能还没被回收,新进程起来后又被误判;
    • 心跳文件不存在时不能直接判死,刚启动的进程还没来得及写第一次心跳,直接杀会陷入"启动即被杀"的死循环;
    • 巡检间隔从 60 秒缩短到 10 秒,因为假死检测的响应精度取决于巡检频率,60 秒一轮意味着最坏要 3 分 60 秒才能发现异常。

    从"进程是否存在"到"进程是否还活着",这一步跨越,正是玩具脚本和可用监控之间的分水岭。

    六、进阶版完整运行展示

    在这里插入图片描述
    在这里插入图片描述

    赞(0)
    未经允许不得转载:171主机测评 » 【Linux实战】从零手搓“进程看门狗“:边缘设备保命指南(附双架构实现)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址