编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • Android提升进阶

  • iOS开发和进阶

  • Web开发和进阶

  • Linux应用开发

    • README
    • QML基础入门

    • QT核心库实践

    • Linux实践开发

      • README
      • Linux开发指引
      • 崩溃监听实践
      • Linux应用指令
      • 应用重启策略
      • 守护进程保活
      • 脚本进程保活
        • 01.核心设计理念
          • 1.1 守护进程模式
          • 1.2 进程生命周期
          • 1.3 整体设计思路
        • 02.脚本守护进程
          • 2.1 理解脚本进程
          • 2.2 脚本如何变进程
          • 2.3 进程直观验证
          • 2.4 守护脚本进程模型
          • 2.5 与应用进程区别
        • 03.脚本进程优缺点
          • 3.1 脚本进程优势
          • 3.2 脚本进程劣势
        • 04.脚本守护实践
          • 4.1 简单守护循环
          • 4.2 添加日志系统
          • 4.3 防止多实例运行
          • 4.4 环境变量支持
          • 4.5 优雅终止应用
          • 4.6 异常重启检测
          • 4.7 日志轮换与清理
          • 4.8 完整整合脚本
          • 4.9 设计模式应用
        • 05.稳定性的设计
          • 5.1 异常重启检测
          • 5.2 退出码语义化
          • 5.3 日志和监控体系
      • 应用启动的原理
      • LVGL设计原理
      • Qt应用保活方案设计
  • Apps
  • Linux应用开发
  • Linux实践开发
杨充
2025-09-18
目录

脚本进程保活

# 05.脚本进程保活

# 目录介绍

  • 01.核心设计理念
    • 1.1 守护进程模式
    • 1.2 进程生命周期
    • 1.3 整体设计思路
  • 02.脚本守护进程
    • 2.1 理解脚本进程
    • 2.2 脚本如何变进程
    • 2.3 进程直观验证
    • 2.4 守护脚本进程模型
    • 2.5 与应用进程区别
  • 03.脚本进程优缺点
    • 3.1 脚本进程优势
    • 3.2 脚本进程劣势
  • 04.脚本守护实践
    • 4.1 简单守护循环
    • 4.2 添加日志系统
    • 4.3 防止多实例运行
    • 4.4 环境变量支持
    • 4.5 优雅终止应用
    • 4.6 异常重启检测
    • 4.7 日志轮换与清理
    • 4.8 完整整合脚本
    • 4.9 设计模式应用
  • 05.稳定性的设计
    • 5.1 异常重启检测
    • 5.2 退出码语义化
    • 5.3 日志和监控体系

# 01.核心设计理念

# 1.1 守护进程模式

设计思想: 持续监控目标应用,确保服务的高可用性。

while true; do
    # 启动应用
    # 监控退出状态
    # 决定是否重启
done
1
2
3
4
5

# 1.2 进程生命周期

启动阶段:

  1. 单例模式:通过PID文件确保只有一个watchdog实例运行
  2. 环境准备:设置库路径、创建日志目录
  3. 历史清理:清理旧进程、旧日志

运行阶段:

  1. 异常检测:监控频繁重启(60秒内3次)
  2. 优雅重启:先TERM信号,再KILL信号
  3. 状态记录:详细的日志记录

退出阶段:

  1. 资源清理:删除PID文件和临时文件

# 1.3 整体设计思路

  1. 守护脚本需要定期检查目标Qt应用是否在运行。
  2. 如果应用不在运行,则启动它。
  3. 为了避免频繁检查导致系统负担,可以设置一个合理的检查间隔。
  4. 同时,可能还需要考虑脚本本身的可靠性,使用循环加休眠的方式。

# 02.脚本守护进程

# 2.1 理解脚本进程

Shell 脚本启动时,操作系统通过解释器(如bash)创建一个新的进程,该进程拥有所有进程特性(PID、资源隔离、信号响应等)。脚本中的命令在此进程内顺序执行,其启动的子进程会形成进程树。守护脚本的本质是 创建一个永不退出的父进程,由其负责监控和重启子进程。

# 2.2 脚本如何变进程

执行权限:当运行 ./script.sh 或 bash script.sh时,操作系统首先检查文件是否有可执行权限(x)。

解释器介入:

  1. 若直接执行 ./script.sh,系统会读取脚本首行的 Shebang(#!/bin/bash),启动对应的解释器(如 /bin/bash)。
  2. 若通过 bash script.sh执行,则直接由指定的解释器处理。

进程创建:

# 操作系统实际执行的动作:
/bin/bash ./script.sh   # 创建一个新的bash进程,并将脚本作为参数传递
1
2

此时系统会分配独立的 PID、内存空间和文件描述符,形成一个完整的进程。

# 2.3 进程直观验证

首先启动进程脚本

[root@RV1126_RV1109:/]# chmod +x /userdata/palmApp/watchdog_palmapp.sh
[root@RV1126_RV1109:/]# echo "My PID: $$"
My PID: 3871
[root@RV1126_RV1109:/]# ./userdata/palmApp/watchdog_palmapp.sh &
1
2
3
4

然后查询进程脚本的信息,如下所示:

[root@RV1126_RV1109:/]# ps -aux | grep palm
root      590  0.0  0.1   2392  1580 ?        S    11:58   0:00 /bin/sh /userdata/palmApp/watchdog_palmapp.sh
root      641  0.0  0.0   2392   424 pts/0    S+   14:03   0:00 grep palm
root      644  3.1  7.2 425536 66932 ?        SLl  11:58   3:55 /userdata/yt-palm/yt-palm -platform linuxfb
[root@RV1126_RV1109:/]# ps l
F   UID  PID PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
0     0  827    1  20   0   2392   436 poll_s Ss+  ttyFIQ0    0:00 -/bin/sh
0     0  905 3871  20   0   2256   296 -      R+   pts/0      0:00 ps l
0     0 3871  751  20   0   2392  1680 wait   Ss   pts/0      0:00 /bin/sh -l
[root@RV1126_RV1109:/]# cd /tmp/palmapp_watchdog.pid
/bin/sh: cd: can't cd to /tmp/palmapp_watchdog.pid
[root@RV1126_RV1109:/]# cat /tmp/palmapp_watchdog.pid
590
[root@RV1126_RV1109:/]#
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2.4 守护脚本进程模型

在守护脚本场景中:

# qt_app_daemon.sh 运行时:
守护脚本进程 (PID: 2000)
  ├─ 循环监控逻辑
  └─ 启动的Qt应用 (PID: 2001)   # 通过 nohup 或 & 启动的子进程
1
2
3
4

守护进程自身:通过 while true保持常驻

目标应用进程:由脚本进程创建的子进程(通过 pgrep可监控)

# 2.5 与应用进程区别

特性 Shell 脚本进程 编译型程序(如C++)
执行方式 由解释器(bash/sh)动态解释执行 直接执行机器码
内存占用 更高(需加载解释器) 更低
启动速度 较慢(需解析脚本) 较快
进程表现 进程名为解释器(如 bash) 进程名为程序本身
调试支持 set -x 跟踪执行 需要GDB等工具

# 03.脚本进程优缺点

# 3.1 脚本进程优势

  1. 简单即稳定:脚本逻辑通常很简单——“检查进程是否存在,不存在则启动”。没有复杂的内部状态,这意味着它自身崩溃的概率极低。
  2. 资源占用极低:脚本本身通常只占用了极少的CPU和内存资源,它的大部分时间都在 sleep,这意味着它极不容易因资源耗尽而被系统杀死。
  3. 依赖少:核心命令如 pgrep、nohup、& 都是Unix/Linux系统中最基础、最稳定的工具。

# 3.2 脚本进程劣势

  1. 缺乏专业进程管理功能:应对粗暴杀死:如果Qt应用被 kill -9 杀死,脚本可以重启它。但如果脚本本身被 kill -9,那么整个守护机制就彻底失效了。
  2. 依赖外部环境(非常关键):文件路径和权限:脚本中写的绝对路径可能因系统部署不同而失效;没有写入日志目录或PID文件目录的权限会导致脚本报错退出。
  3. 错误处理薄弱:脚本默认会忽略它启动的进程的错误。如果Qt应用因为缺少库、配置文件错误等无法启动,脚本只会默默地不断尝试,并在日志中留下大量错误记录,但无法自主通知运维人员。

目前实际脚本,对厂商设备,脚本进程,应用进程分析

  1. 针对1,守护进程每次设备重启,会首先执行脚本进程,然后脚本进程会开启刷掌应用进程。且脚本进程不会被无端杀死。
  2. 针对2,对于部署环境,日志路径,定在在刷掌应用的路径下,已经有读写权限。
  3. 针对3,刷掌应用暂时多方位测试,不会报出崩溃,重启,然后在崩溃操作。且在脚本中,连续3次则会判断为异常,防止无限重启循环,识别系统性问题。

# 04.脚本守护实践

目标:创建一个守护脚本,用于监控并重启一个Qt应用(yt-palm),同时具备日志管理、防止重复启动、异常重启检测等功能。

  1. 基础框架:无限循环重启应用,无额外功能。
  2. 添加日志记录:记录启动、退出和重启事件。
  3. 防止多个守护脚本实例:通过PID文件实现单例。
  4. 日志清理:定期清理旧日志(保留7天)。
  5. 异常重启检测:60秒内重启3次则判定异常。
  6. 优雅终止应用:先尝试普通kill,再强制kill。
  7. 特殊退出码处理:当应用返回255时,守护脚本退出。

# 4.1 简单守护循环

基础框架 - 最简单的守护循环。最基本的守护循环结构,要满足应用启动和退出码检查,重启延迟控制。

#!/bin/sh

# 基础配置
APP_NAME="yt-palm"
BASE_DIR="/userdata/yt-palm"
RESTART_DELAY=5

# 核心守护循环
while true; do
    # 启动应用
    if [ -f "${BASE_DIR}/${APP_NAME}" ]; then
        # 赋予执行权限
        chmod +x "${BASE_DIR}/${APP_NAME}"
        
        # 执行应用
        "${BASE_DIR}/${APP_NAME}" -platform linuxfb
        EXIT_CODE=$?
    else
        EXIT_CODE=127  # 文件不存在
    fi
    
    # 检查退出码
    if [ "$EXIT_CODE" -eq 255 ]; then
        break  # 正常退出,停止守护
    fi
    
    # 等待后重启
    sleep $RESTART_DELAY
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 4.2 添加日志系统

创建日志目录和文件:实现日志函数统一记录,然后在一些关键的节点,添加日志记录。

#!/bin/sh

# ...保留基础配置...

# 新增日志配置
LOGFILE="/userdata/yt-palm/log/palmapp_watchdog.log"
mkdir -p $(dirname "$LOGFILE")

# 日志函数
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') [watchdog] $1" >> "$LOGFILE"
}

# 核心守护循环
while true; do
    log "Launching $APP_NAME..."
    
    if [ -f "${BASE_DIR}/${APP_NAME}" ]; then
        chmod +x "${BASE_DIR}/${APP_NAME}"
        "${BASE_DIR}/${APP_NAME}" -platform linuxfb
        EXIT_CODE=$?
        log "$APP_NAME exited with code $EXIT_CODE"
    else
        log "Application not found"
        EXIT_CODE=127
    fi
    
    if [ "$EXIT_CODE" -eq 255 ]; then
        log "Normal exit detected, stopping watchdog."
        break
    fi
    
    log "Restarting in $RESTART_DELAY seconds..."
    sleep $RESTART_DELAY
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 4.3 防止多实例运行

PID文件机制原理:检查并终止旧实例,当前进程ID记录,退出时资源清理。

#!/bin/sh

# ...保留已有配置...

# 新增PID文件配置
WATCHDOG_PID="/tmp/palmapp_watchdog.pid"

# 检查并清理旧进程
if [ -f "$WATCHDOG_PID" ]; then
    OLD_PID=$(cat "$WATCHDOG_PID" 2>/dev/null)
    if [ -n "$OLD_PID" ]; then
        kill -9 "$OLD_PID" 2>/dev/null
        log "Killed previous watchdog process: $OLD_PID"
    fi
    rm -f "$WATCHDOG_PID"
fi

# 记录当前PID
echo $$ > "$WATCHDOG_PID"
log "Started watchdog with PID: $$"

# ...保留守护循环...

# 脚本退出时清理PID文件
rm -f "$WATCHDOG_PID"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 4.4 环境变量支持

export命令的正确使用:库路径(LD_LIBRARY_PATH)设置,图形环境变量配置。

#!/bin/sh

# ...保留已有配置...

# 新增环境变量设置
export LD_LIBRARY_PATH="${BASE_DIR}/libs:$LD_LIBRARY_PATH"

# ...保留守护循环...
1
2
3
4
5
6
7
8

# 4.5 优雅终止应用

pidof命令获取进程ID:kill -0检查进程是否存在,优雅终止的重要性。

#!/bin/sh

# ...保留已有配置...

# 在守护循环内修改应用启动部分
if [ -f "${BASE_DIR}/${APP_NAME}" ]; then
    # 先尝试终止可能存在的旧实例
    PID=$(pidof ${APP_NAME})
    if [ -n "$PID" ]; then
        # 先发送普通终止信号
        kill $PID        
        # 等待2秒
        sleep 2
        # 检查是否仍然运行
        if kill -0 $PID 2>/dev/null; then
            # 强制终止
            kill -9 $PID
        fi
    fi
    
    chmod +x "${BASE_DIR}/${APP_NAME}"
    "${BASE_DIR}/${APP_NAME}" -platform linuxfb
    EXIT_CODE=$?
else
    # ...保持不变...
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 4.6 异常重启检测

异常模式检测算法:文件操作技巧(tail/mv),临时文件的使用。

#!/bin/sh

# ...保留已有配置...

# 新增重启记录配置
RESTART_LOG="/tmp/palmapp_restart_times.log"
TMP_RESTART_LOG="/tmp/tmp_restart.log"

# 在守护循环开始处添加
while true; do
    # 检查是否在60秒内重启3次
    if [ $(wc -l < "$RESTART_LOG") -ge 3 ] && [ $DIFF -lt 60 ]; then
        log "Rebooted 3 times within 60 seconds. Abnormal restart detected."
        # 这里可以添加警报或特殊处理
    fi
    
    # ...原有应用启动逻辑...
done

# 脚本退出时清理
rm -f "$RESTART_LOG"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 4.7 日志轮换与清理

日志轮换的重要性:awk高级文本处理,主要是为了防止日志文件太多了,只保留最近7天的日志信息。

#!/bin/sh

# ...保留已有配置...

# 新增日志清理配置
TMPLOG="/tmp/tmp_watchdog_log"

# 在脚本开始处添加日志清理
if [ -f "$LOGFILE" ]; then
    # 保留最近7天的日志记录
    TODAY=$(date +%s)
    SEVEN_DAYS_AGO=$((TODAY - 7 * 24 * 60 * 60))
    
    # 使用awk解析日志时间戳
    awk -v limit=$SEVEN_DAYS_AGO '
    {
        if (NF == 0) next;   # 跳过空行
        # 提取日志时间部分
        timestamp = $1 " " $2 " " $3 " " $4 " " $6;
        # 转换为时间戳
        cmd = "date -d \"" timestamp "\" +%s";
        cmd | getline t;
        close(cmd);
        # 保留7天内日志
        if (t >= limit) print $0;
    }' "$LOGFILE" > "$TMPLOG" && mv "$TMPLOG" "$LOGFILE"
    
    log "Cleaned logs older than 7 days"
fi

# ...后续原有代码...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 4.8 完整整合脚本

#!/bin/sh
LOGFILE=/userdata/yt-palm/log/palmapp_watchdog.log
RESTART_DELAY=5
APP_NAME=yt-palm
SHELL_NAME=watchdog_palmapp.sh
BASE_DIR=/userdata/yt-palm
TMPLOG="/tmp/tmp_watchdog_log"
RESTART_LOG="/tmp/palmapp_restart_times.log"
TMP_RESTART_LOG="/tmp/tmp_restart.log"
WATCHDOG_PID="/tmp/palmapp_watchdog.pid"

# 在脚本开始时设置一次环境变量
export LD_LIBRARY_PATH=${BASE_DIR}/libs:$LD_LIBRARY_PATH

# 检查并清理已存在的watchdog进程(通过PID文件)
if [ -f "$WATCHDOG_PID" ]; then
    OLD_PID=$(cat "$WATCHDOG_PID" 2>/dev/null)
    if [ -n "$OLD_PID" ]; then
        kill -9 "$OLD_PID" 2>/dev/null
        echo "$(date) [watchdog] Kill shell app old process: $OLD_PID" >> "$LOGFILE"
    fi
    rm -f "$WATCHDOG_PID"
fi

# 记录当前watchdog进程ID
echo $$ > "$WATCHDOG_PID"
# 在记录PID到文件后,会同时记录到日志中。这样可以方便追踪watchdog进程的启动情况。
echo "$(date) [watchdog] Started shell app with PID: $$" >> "$LOGFILE"
mkdir -p ${BASE_DIR}/log
# 清理日志,保留最近7天的记录
if [ -f "$LOGFILE" ]; then
    # 保留最近7天的日志记录
    TODAY=$(date +%s)
    SEVEN_DAYS_AGO=$((TODAY - 7 * 24 * 60 * 60))
    echo "$(date) [watchdog] Filter log older than: $(date -d @$SEVEN_DAYS_AGO)"
    # 使用awk脚本解析时间戳进行过滤
    awk -v limit=$SEVEN_DAYS_AGO '
    {
        if (NF == 0) next;   # 空行直接丢弃
        timestamp = $1 " " $2 " " $3 " " $4 " " $6;
        cmd = "date -d \"" timestamp "\" +%s";
        cmd | getline t;
        close(cmd);
        if (t >= limit) print $0;
    }' "$LOGFILE" > "$TMPLOG" && mv "$TMPLOG" "$LOGFILE";
fi



# 核心逻辑
while true; do
    # 60S连续启动3次,判定为异常
    NOW=$(date +%s)
    echo $NOW >> "$RESTART_LOG"
    tail -n 3 "$RESTART_LOG" > "$TMP_RESTART_LOG"
    mv "$TMP_RESTART_LOG" "$RESTART_LOG"
    FIRST=$(head -n 1 "$RESTART_LOG")
    [ -z "$FIRST" ] && FIRST=$NOW
    DIFF=$((NOW - FIRST))

    # 60秒内连续启动3次判定为异常
    if [ $(wc -l < "$RESTART_LOG") -ge 3 ] && [ $DIFF -lt 60 ]; then
        echo "$(date) [watchdog] Rebooted 3 times within 60 seconds. Abnormal restart detected." >> "$LOGFILE"
    fi
    echo "$(date) [watchdog] Launching palmapp..." >> "$LOGFILE"

    # 应用启动逻辑
    if [ -f ${BASE_DIR}/${APP_NAME} ]; then
        # kill -9过于暴力,可能导致数据丢失。如果pidof返回空值,会导致kill -9报错
        # kill -9 $(pidof ${APP_NAME})
        PID=$(pidof ${APP_NAME})
        if [ -n "$PID" ]; then
            kill $PID
            echo "$(date) [watchdog] Kill Palm app process $PID" >> "$LOGFILE"
            sleep 2
            if kill -0 $PID 2>/dev/null; then
                kill -9 $PID
                echo "$(date) [watchdog] Kill Palm app process $PID  -9" >> "$LOGFILE"
            fi
        fi
        # 给权限
        chmod +x ${BASE_DIR}/${APP_NAME}
        echo "$(date) [watchdog] palmapp start, is launched" >> "$LOGFILE"
        # 执行应用
        ${BASE_DIR}/${APP_NAME} -platform linuxfb
        EXIT_CODE=$?
    else
        echo "$(date) [watchdog] Palm app not found" >> "$LOGFILE"
        # 标准的"命令未找到"错误码
        EXIT_CODE=127
    fi
    echo "$(date) [watchdog] Palm app exited with code $EXIT_CODE" >> "$LOGFILE"
    # 正常退出(退出码255)时停止watchdog。那么在退出应用,则需要使用255这个code码
    # 异常退出(退出码非255)时重启watchdog
    if [ "$EXIT_CODE" -eq 255 ]; then
        echo "$(date) [watchdog] Normal exit detected, stopping watchdog." >> "$LOGFILE"
        break
    fi
    echo "$(date) [watchdog] Abnormal exit detected, restarting in $RESTART_DELAY seconds..." >> "$LOGFILE"
    sleep $RESTART_DELAY
done
rm -f "$WATCHDOG_PID"
rm -f "$RESTART_LOG"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

# 4.9 设计模式应用

  1. 单例模式 - PID文件确保唯一实例
  2. 观察者模式 - 监控应用状态变化
  3. 策略模式 - 根据退出码选择不同处理策略
  4. 模板方法模式 - 标准化的启动-监控-重启流程

# 05.稳定性的设计

# 5.1 异常重启检测

设计思想: 防止无限重启循环,识别系统性问题。

# 60秒内连续启动3次判定为异常
if [ $(wc -l < "$RESTART_LOG") -ge 3 ] && [ $DIFF -lt 60 ]; then
    echo "$(date) [watchdog] Rebooted 3 times within 60 seconds. Abnormal restart detected."
fi
1
2
3
4

# 5.2 退出码语义化

设计思想: 通过退出码区分正常关闭和异常崩溃。

if [ "$EXIT_CODE" -eq 255 ]; then
    # 正常退出,停止watchdog
    break
else
    # 异常退出,继续重启
    sleep $RESTART_DELAY
fi
1
2
3
4
5
6
7

# 5.3 日志和监控体系

记录启动、重启、异常事件。一定要注意:日志轮转防止磁盘占满。

结构化日志:格式: 时间戳 [组件] 操作描述

echo "$(date) [watchdog] Started shell app with PID: $$" >> "$LOGFILE"
1

日志轮转,设计思想: 防止日志文件无限增长,保持系统性能。

# 保留最近7天的日志记录
awk -v limit=$SEVEN_DAYS_AGO '...' "$LOGFILE" > "$TMPLOG"
1
2
上次更新: 2026/06/10, 11:13:41
守护进程保活
应用启动的原理

← 守护进程保活 应用启动的原理→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式