第一部分 系统守护进程完整架构深度分析

一、整体架构树形分析

核心架构层级

sysdemo 系统守护进程 (主控大脑)
├── 配置管理层 (骨架与神经系统)
│   ├── 静态配置 (JSON解析) - demo_config.c/h
│   ├── 运行时配置 - sysdemo.h 宏定义
│   └── 阈值策略配置 - 进程监控策略
│
├── 进程生命周期管理 (心脏与循环系统)
│   ├── 进程启动/停止 - run_main_process()
│   ├── 状态监控循环 - start_main_processes()
│   ├── 健康检查 - get_process_stat()/heap/cpu
│   └── 异常处理 - kill_process()
│
├── 文件监控与备份 (免疫系统)
│   ├── 实时监控 - inotify/epoll
│   ├── 双级备份策略 - 1级(实时)/2级(可靠)
│   ├── 完整性校验 - CRC32/MD5
│   └── 分区健康检查 - 读写测试
│
├── 通信与协调系统 (神经网络)
│   ├── 消息系统接口 - sys_demo_msg_*
│   ├── 死锁检测机制 - 心跳监控
│   └── 调试接口 - 信号处理
│
└── 工具与服务层 (消化系统)
    ├── 校验算法 - checkapi.c/h
    ├── 时间/延迟函数 - 系统调用封装
    └── 日志系统 - 进程退出记录

二、主要函数关联树形分析

1. 主控制流函数树

main()
├── 参数解析 (argc/argv)
├── 配置加载 get_demo_config()
│   └── JSON解析 get_json_config_file()
│       ├── 文件读取/解析
│       ├── boot节点解析 get_boot_cfg()
│       ├── 进程节点解析 get_process_cfg()
│       └── 备份节点解析 get_backup_cfg()
├── 系统初始化 run_boot_init()
│   ├── 首次上电判断 is_first_poweron()
│   ├── 数据库初始化 db_env_init()
│   ├── 消息模块初始化 module_msg_init()
│   └── boot脚本执行 start_boot_config()
└── 主任务执行 run_demo_task()
    ├── 消息模块初始化 sys_demo_msg_init()
    └── 根据参数分支
        ├── start: start_main_processes()
        ├── factory: start_factory_processes()
        └── clean: 清理流程

2. 进程监控循环函数树

start_main_processes()
├── 时间基准初始化 (死锁检查/心跳发送)
└── 主监控循环 (while(1))
    ├── 时间检查 (死锁/心跳周期)
    ├── 遍历所有进程 for (index < cfg.proc_len)
    │   ├── 类型过滤 (TYPE_NORMAL/CIE/HLOG)
    │   ├── 进程管理 run_main_process()
    │   │   ├── 文件检查 access(path, X_OK)
    │   │   ├── 依赖检查 is_main_run_ok()
    │   │   ├── PID检查 get_process_pid()
    │   │   ├── CIE特殊处理 (生命周期/OOM调整)
    │   │   ├── 命令构造 get_process_run_cmd()
    │   │   ├── 进程启动 system(cmd)
    │   │   └── 状态更新/日志
    │   └── 检查周期内处理
    │       ├── 死锁检查 check_deadlock()
    │       │   └── 消息队列分析 is_msg_deadlock()
    │       ├── 核心转储压缩 (条件编译)
    │       └── 阈值处理 process_threshold_handler()
    │           ├── CPU/堆内存获取 get_process_*()
    │           ├── 阈值比较与处理
    │           └── 重启策略 kill_process() + run_main_process()
    └── 频率控制 delay_s(剩余时间)

3. 备份监控函数树

run_database_backup_thread()
├── epoll/inotify初始化
└── 主监控循环
    ├── epoll_wait等待文件事件
    │   └── inotify事件处理
    │       ├── 文件修改 IN_MODIFY → 设置1级备份标志
    │       ├── 严重事件 (删除/卸载/移动) → 分区错误退出
    │       └── 延迟处理避免过频
    ├── 定时检查 (2级备份周期)
    └── 遍历所有监控文件
        ├── 错误计数检查与分区健康检查
        ├── 源文件存在性检查
        ├── 1级备份处理
        │   ├── 条件: 修改标志或备份文件不存在
        │   ├── 删除旧备份
        │   ├── 直接拷贝 db_backup()
        │   └── 结果处理 (错误计数/标志更新)
        └── 2级备份处理
            ├── 条件: 需要2级备份且达到周期
            ├── 完整性校验 db_integrity_check()
            ├── 文件压缩与校验 gzip_db()
            │   ├── 校验值比较 compare_file()
            │   │   ├── 读取旧校验值
            │   │   ├── 计算新校验值 (CRC32/MD5)
            │   │   └── 比较与返回
            │   ├── 文件压缩 system("gzip")
            │   └── 校验值保存
            └── 结果处理

三、Linux子系统调用逻辑树形分析

1. 进程管理子系统

进程控制
├── fork/exec 相关
│   ├── system() - 通过shell执行命令
│   ├── (备选) run_exec() - 直接fork+exec
│   └── 进程状态获取
│       ├── /proc/[pid]/stat 读取 - get_process_stat()
│       ├── /proc/[pid]/maps 解析 - get_process_heap()
│       └── /proc/[pid]/comm 读取 - get_process_pid()
├── 信号处理
│   ├── kill() - 发送终止信号
│   ├── signal() - 信号处理器注册
│   └── SIGUSR1 - 调试信号
└── 进程间通信
    └── 消息系统 (module_msg_*) - 具体实现隐藏

2. 文件系统子系统

文件操作
├── 基础I/O
│   ├── open/read/close - 配置文件读取
│   ├── access() - 文件存在/权限检查
│   ├── stat() - 文件信息获取
│   └── remove() - 文件删除
├── 目录操作
│   ├── opendir/readdir/closedir - /proc遍历
│   └── dirent结构分析
├── 文件监控
│   ├── inotify_init() - 监控初始化
│   ├── inotify_add_watch() - 添加监控
│   └── inotify_rm_watch() - 移除监控
└── 高级I/O
    ├── epoll_create1() - 事件多路复用
    ├── epoll_ctl() - 事件注册
    └── epoll_wait() - 事件等待

3. 时间与调度子系统

时间管理
├── 时间获取
│   ├── gettimeofday() - 高精度时间
│   ├── time() - 日历时间
│   └── sysinfo() - 系统运行时间
└── 延时控制
    ├── select() 实现延时 - delay_s()/delay_ms()
    └── 固定周期巡检 - 主循环频率控制

4. 内存与资源子系统

资源管理
├── 内存监控
│   └── /proc/[pid]/maps 解析 - 堆内存计算
├── CPU监控
│   └── (预留) get_process_cpu()
└── OOM控制
    └── /proc/[pid]/oom_score_adj 设置

四、源码文件设计树形分析

1. sysdemo_main.c - 主控中心

设计思想: 指挥协调中心
├── 为什么独立文件?
│   ├── 主程序逻辑集中,便于理解和维护
│   ├── 与配置文件解析分离,关注业务逻辑
│   └── 函数按调用层次组织,逻辑清晰
├── 内部结构
│   ├── 顶部: 全局变量和函数声明
│   ├── 中部: main() 入口和主要功能函数
│   └── 底部: 辅助工具函数
└── 设计合理性
    ├── 优点: 逻辑层次清晰,职责单一
    ├── 缺点: 文件较大(300+行),可考虑进一步拆分
    └── 改进建议: 将工具函数拆分到单独文件
/**
 * @file sysdemo_main.c
 * @brief 系统守护进程主程序文件,负责进程管理和系统监控
 */
​
#include <stdio.h>                      /**< 标准输入输出头文件,提供printf等函数 */
#include <stdlib.h>                     /**< 标准库头文件,提供system、malloc等函数 */
#include <string.h>                     /**< 字符串处理头文件,提供strcmp、memset等函数 */
​
#include <sys/statfs.h>                 /**< 文件系统统计头文件,提供statfs结构体和函数 */
#include <sys/stat.h>                   /**< 文件状态头文件,提供stat结构体和函数 */
#include <sys/wait.h>                   /**< 进程等待头文件,提供WIFEXITED等宏 */
#include <fcntl.h>                      /**< 文件控制头文件,提供文件操作相关常量 */
#include <dirent.h>                     /**< 目录操作头文件,提供DIR、dirent结构体 */
​
#include "sysconf_struct_types.h"       /**< 系统配置结构体类型定义头文件 */
#include "system_functions.h"           /**< 系统函数头文件,提供sys_get_final_iob等函数 */
#include "message.h"                    /**< 消息模块头文件,提供message_t结构体等 */
#include "db_api.h"                     /**< 数据库API头文件,提供数据库操作接口 */
​
#include "checkapi.h"                   /**< 检查API头文件,提供check_deadlock等函数 */
#include "backup_db.h"                  /**< 数据库备份头文件,提供run_backup_task函数 */
#include "sysdemo.h"                    /**< 系统守护进程主头文件,包含宏定义和函数声明 */
​
static demofcg_t cfg;                   /**< 全局配置结构体,存储从配置文件读取的配置信息 */
​
/**
 * @brief 声明静态函数原型
 * @note 使用静态函数限制作用域在当前文件,提高封装性
 */
static int run_boot_init(void);         /**< 系统启动初始化函数 */
static int run_demo_task(int argc, char *argv[]); /**< 执行守护进程任务主函数 */
static void debug_sysdemo_handler(int sig); /**< 调试信号处理函数 */
​
/**
 * @brief 主函数 - 程序入口点
 * @param argc 命令行参数个数
 * @param argv 命令行参数数组
 * @return int 程序退出码,0表示成功,-1表示失败
 * @note 设计模式:模板方法模式,根据参数选择不同执行路径
 *       性能分析:启动时解析配置文件,有一定I/O开销
 */
int main(int argc, char *argv[])
{
    /** 参数检查 */
    if (argc < 2)                       /**< 检查命令行参数数量 */
    {
        printf("Usage: %s [start|clean|factory]\n", argv[0]); /**< 打印使用说明 */
        return -1;                      /**< 参数不足,返回错误 */
    }
​
    /** 初始化配置 */
    memset(&cfg, 0, sizeof(demofcg_t)); /**< 清零配置结构体,防止未初始化内存 */
    if (get_demo_config(&cfg) < 0)      /**< 读取配置文件到cfg结构体 */
    {
        printf("get demo config error !! \n"); /**< 配置文件读取失败 */
        release_demo_config(&cfg);      /**< 释放配置相关资源 */
        return -1;                      /**< 返回错误 */
    }
​
    run_boot_init();                    /**< 执行启动初始化(数据库、消息模块等) */
    signal(SIGUSR1, debug_sysdemo_handler); /**< 注册SIGUSR1信号处理函数用于调试 */
​
    /** 根据参数执行相应任务 */
    if (strcmp(argv[1], "start") == 0)  /**< 如果是start命令 */
        run_backup_task(cfg);           /**< 执行数据库备份任务 */
​
    run_demo_task(argc, argv);          /**< 执行主守护进程任务 */
​
    return 0;                           /**< 正常退出 */
}
​
/** ================ 全局变量定义 ================ */
static js_process_t *main_cie = NULL;   /**< 指向主CIE进程的指针,设计模式:单例思想 */
static int main_oom_flag = 0;           /**< OOM管制设置标志位,0=未设置,1=已设置 */
static long exe_time = 0;               /**< 进程存活时间(秒),用于生命周期检测 */
static int exe_count = 0;               /**< 进程异常次数计数器,设计模式:状态计数器 */
static int syswd_mod_id = -1;           /**< 看门狗消息模块ID,-1表示未注册 */
​
/** ================ 静态函数声明 ================ */
static int start_boot_config(void);     /**< 启动boot配置项(shell脚本) */
static int start_main_processes(void);  /**< 启动主进程管理循环 */
static int start_factory_processes(void); /**< 启动工厂测试进程 */
​
static int stop_boot_config(void);      /**< 停止boot配置项 */
static int stop_main_processes(void);   /**< 停止主进程 */
​
#if 0                                   /**< 条件编译注释掉的函数原型 */
static int run_exec(char *path, char *name, char *arg, int block_flag); /**< 执行外部程序 */
#endif
​
extern int module_msg_init();           /**< 外部声明消息模块初始化函数 */
static int sys_demo_msg_init(void);     /**< 系统守护进程消息模块初始化 */
static void sys_demo_msg_handle(message_t *msg); /**< 消息处理回调函数 */
​
static int kill_process(char *process, int sig, unsigned int core); /**< 杀死指定进程 */
#ifdef SYSDEMO_COREDUMP_ZIP_ENABLE      /**< 条件编译:核心转储压缩使能 */
static int gzip_process_core(char *process); /**< 压缩进程核心转储文件 */
#endif
​
static int process_threshold_handler(js_process_t *process, int where); /**< 进程阈值处理 */
​
static int get_process_run_cmd(char cmd[MAX_BUFFER_LEN], js_process_t *process, int mode, int mode_flag); /**< 获取进程运行命令 */
static int get_process_pid(char *process); /**< 获取进程PID */
static char get_process_stat(int pid);   /**< 获取进程状态字符 */
static int get_process_cpu(int pid);     /**< 获取进程CPU使用率 */
static int get_process_heap(int pid);    /**< 获取进程堆内存使用量 */
​
static bool is_rootfs_mtd(char *mtd);    /**< 判断是否为rootfs MTD分区 */
static bool is_main_run_ok(js_process_t *process); /**< 判断主进程是否运行正常 */
static bool is_first_poweron(void);      /**< 判断是否为首次上电 */
​
static int run_main_process(js_process_t *process); /**< 运行主进程管理逻辑 */
static int run_factory_process(js_process_t *process); /**< 运行工厂测试进程 */
​
static void debug_process_handler(void); /**< 调试进程处理函数 */
​
/**
 * @brief 启动初始化函数
 * @return int 总是返回0
 * @note 设计模式:初始化钩子,在首次上电时执行一次性初始化
 *       性能分析:仅首次上电执行,包含数据库和消息模块初始化
 */
static int run_boot_init(void)
{
    if (is_first_poweron() == true)     /**< 检查是否为首次上电 */
    {
        int iob_num;                    /**< I/O板编号变量 */
        sys_get_final_iob(&iob_num);    /**< 获取最终I/O板编号 */
​
        db_env_init(iob_num);           /**< 初始化数据库环境,性能:可能有磁盘I/O */
        module_msg_init();              /**< 初始化消息模块,性能:进程间通信初始化 */
        start_boot_config();            /**< 执行boot配置脚本 */
    }
    return 0;                           /**< 总是成功返回 */
}
​
/**
 * @brief 执行守护进程的主任务
 * @param argc 命令行参数个数
 * @param argv 命令行参数数组
 * @return int 成功返回0,失败返回-1
 * @note 设计模式:命令模式,根据参数执行不同操作
 *       性能分析:长时间运行,包含消息循环和进程监控
 */
static int run_demo_task(int argc, char *argv[])
{
    sys_demo_msg_init();                /**< 初始化系统守护进程消息模块 */
​
    if (strcmp(argv[1], "start") == 0)  /**< start命令:启动主进程 */
    {
        start_main_processes();         /**< 进入主进程管理循环(不会返回) */
    }
    else if (strcmp(argv[1], "factory") == 0) /**< factory命令:启动工厂测试 */
    {
        start_factory_processes();      /**< 进入工厂测试进程循环 */
    }
    else if (strcmp(argv[1], "clean") == 0) /**< clean命令:清理停止 */
    {
        stop_main_processes();          /**< 停止主进程 */
        stop_boot_config();             /**< 停止boot配置 */
        module_msg_init();              /**< 重新初始化消息模块 */
        release_demo_config(&cfg);      /**< 释放配置资源 */
    }
    else                                /**< 无效参数处理 */
    {
        printf("invalid param !!\n");   /**< 打印错误信息 */
        release_demo_config(&cfg);      /**< 释放配置资源 */
        return -1;                      /**< 返回错误 */
    }
​
    return 0;                           /**< 成功返回 */
}
​
/**
 * @brief 执行boot节点的start配置脚本
 * @return int 总是返回0
 * @note 设计模式:命令执行器,依次执行配置的shell脚本
 *       性能分析:顺序执行,阻塞等待每个脚本完成
 */
static int start_boot_config(void)
{
    int index;                          /**< 循环索引 */
    for (index = 0; index < cfg.boot.shell_len; index++) /**< 遍历所有boot脚本 */
    {
        if (cfg.boot.shell[index].valid != true || cfg.boot.shell[index].type != TYPE_SCRIPT_S) /**< 检查脚本有效性和类型 */
            continue;                   /**< 跳过无效或非启动脚本 */
​
        int status = system(cfg.boot.shell[index].cmd); /**< 执行shell命令,性能:fork+exec开销 */
        if (-1 == status || WIFEXITED(status) != true || 0 != WEXITSTATUS(status)) /**< 检查执行结果 */
        {
            printf("run shell error, cmd = \n%s\n", cfg.boot.shell[index].cmd); /**< 打印错误信息 */
        }
    }
    return 0;                           /**< 总是成功返回 */
}
​
/**
 * @brief 启动主进程管理循环
 * @return int 不会返回,除非发生致命错误
 * @note 设计模式:轮询模式,定期检查所有进程状态
 *       性能分析:循环频率由SYSDEMO_PROCESS_INSPECTION控制,需平衡响应性和CPU使用
 */
static int start_main_processes(void)
{
    long check_freq = get_sysuptime();  /**< 死锁检查时间基准点 */
    long baet_freq = get_sysuptime();   /**< 心跳消息发送时间基准点 */
    
    while(1)                            /**< 主监控循环,永不退出 */
    {
        int index, check_flag = 0;      /**< 进程索引和检查标志 */
        long start = get_sysuptime();   /**< 记录循环开始时间 */
        
        /** 检查是否到达死锁检查时间 */
        if (start-check_freq >= SYSDEMO_DEADLOCK_CHECK_FREQ) /**< 死锁检查频率判断 */
        {
            check_freq = start;         /**< 更新检查时间基准点 */
            check_flag = 1;             /**< 设置检查标志 */
        }
        
        /** 检查是否到达心跳发送时间 */
        if (start-baet_freq >= SYSDEMO_SEND_BAET_MSG_FREQ) /**< 心跳发送频率判断 */
        {
            baet_freq = start;          /**< 更新心跳时间基准点 */
            sys_demo_msg_send(NULL, MNTLOC_BAET_MSG, NULL, 0); /**< 发送心跳消息 */
        }
        
        /** 遍历所有进程 */
        for (index = 0; index < cfg.proc_len; index++) /**< 遍历进程配置数组 */
        {
            /** 过滤非监控类型的进程 */
            if (cfg.process[index].type != TYPE_NORMAL && \
                cfg.process[index].type != TYPE_CIE    && \
                cfg.process[index].type != TYPE_HLOG) /**< 只监控普通、CIE和HLOG进程 */
                continue;               /**< 跳过非监控类型进程 */
            
            /** 运行主进程管理逻辑 */
            if (run_main_process(&cfg.process[index]) < 0) /**< 启动/重启进程 */
                continue;               /**< 运行失败则跳过后续检查 */
            
            if (check_flag == 0)        /**< 非检查周期 */
                continue;               /**< 跳过死锁和阈值检查 */
            
            /** 1. 死锁监测 */
            char stat = get_process_stat(cfg.process[index].pid); /**< 获取进程状态字符 */
            if ((stat != 'T' && stat != 't') && check_deadlock(cfg.process[index].pid) == true) /**< 非停止状态且检测到死锁 */
            {
                kill_process(cfg.process[index].name, SIGABRT, SYSDEMO_KILLCORE_DEADLOCK_FROM_MSG); /**< 发送SIGABRT终止进程 */
                printf("[pid = %d] %s/%s run deadlock !!\n", cfg.process[index].pid, cfg.process[index].path, cfg.process[index].name); /**< 打印死锁信息 */
                printf("[restart] # %s/%s\n", cfg.process[index].path, cfg.process[index].name); /**< 打印重启信息 */
                --index;                /**< 回退索引以便立即重启该进程 */
                continue;               /**< 继续循环 */
            }
            
            /** 2. core文件压缩(条件编译) */
            #ifdef SYSDEMO_COREDUMP_ZIP_ENABLE /**< 如果启用核心转储压缩 */
            gzip_process_core(cfg.process[index].name); /**< 压缩core文件 */
            #endif
            
            /** 3. 阈值处理 */
            process_threshold_handler(&cfg.process[index], THRESHOLD_WHERE_INSPECTION); /**< 检查CPU/内存阈值 */
        }
        
        /** 控制循环频率 */
        long end = get_sysuptime();     /**< 记录循环结束时间 */
        int use_time = end - start;     /**< 计算循环耗时 */
        int inspection = SYSDEMO_PROCESS_INSPECTION - use_time; /**< 计算需要睡眠的时间 */
        if (inspection >= 0)            /**< 如果还有剩余时间 */
            delay_s(inspection);        /**< 精确睡眠剩余时间,性能:保持固定巡检周期 */
    }
    return 0;                           /**< 理论上不会执行到这里 */
}
​
/**
 * @brief 启动工厂测试进程
 * @return int 不会返回,除非发生致命错误
 * @note 设计模式:简单轮询,仅管理工厂测试进程
 *       性能分析:相比主进程管理更简单,仅负责重启
 */
static int start_factory_processes(void)
{   
    while(1)                            /**< 工厂测试监控循环 */
    {
        int index;                      /**< 循环索引 */
        for (index = 0; index < cfg.proc_len; index++) /**< 遍历所有进程配置 */
        {
            if (cfg.process[index].type != TYPE_FAC_NORMAL && cfg.process[index].type != TYPE_FAC) /**< 只处理工厂测试进程 */
                continue;               /**< 跳过非工厂测试进程 */
            
            run_factory_process(&cfg.process[index]); /**< 运行工厂测试进程管理逻辑 */
        }
        delay_s(SYSDEMO_PROCESS_INSPECTION); /**< 固定周期睡眠,性能:简单定时 */
    }
    return 0;                           /**< 理论上不会执行到这里 */
}
​
/**
 * @brief 执行boot节点的stop配置脚本
 * @return int 总是返回0
 * @note 设计模式:与start_boot_config对称,执行停止脚本
 *       性能分析:通常在系统关闭时执行
 */
static int stop_boot_config(void)
{
    int index;                          /**< 循环索引 */
    for (index = 0; index < cfg.boot.shell_len; index++) /**< 遍历所有boot脚本 */
    {
        if (cfg.boot.shell[index].valid != true || cfg.boot.shell[index].type != TYPE_SCRIPT_K) /**< 检查脚本有效性和类型 */
            continue;                   /**< 跳过无效或非停止脚本 */
        
        int status = system(cfg.boot.shell[index].cmd); /**< 执行shell命令 */
        if (-1 == status || WIFEXITED(status) != true || 0 != WEXITSTATUS(status)) /**< 检查执行结果 */
        {
            printf("run shell error, cmd = \n%s\n", cfg.boot.shell[index].cmd); /**< 打印错误信息 */
        }
    }
    return 0;                           /**< 总是成功返回 */
}
​
/**
 * @brief 杀死指定进程
 * @param process 进程名
 * @param sig 终止信号(如SIGKILL、SIGABRT)
 * @param core 终止原因代码(见sysdemo.h中的宏)
 * @return int 成功返回0,失败返回-1
 * @note 设计模式:进程生命周期管理
 *       性能分析:发送信号+等待回收,有超时机制
 *       数据流结构:使用固定超时计数target_cnt
 */
static int kill_process(char *process, int sig, unsigned int core)
{
    if (process == NULL)                /**< 参数检查 */
        return -1;                      /**< 进程名为空 */
​
    int pid = get_process_pid(process); /**< 通过进程名获取PID */
    if (pid <= 0)                       /**< PID无效检查 */
        return -1;                      /**< 进程不存在 */
    
    if (kill(pid, sig) < 0)             /**< 发送终止信号 */
    {
        printf("kill %s error\n", process); /**< 打印错误信息 */
        return -1;                      /**< 发送信号失败 */
    }
​
    int loop_cnt = 0;                   /**< 等待循环计数器 */
    int each_wait = 200;                /**< 每次等待200ms,数据流:控制等待粒度 */
    int target_cnt = SYSDEMO_PROCESS_FINISH_RECYCLE_WAIT*5; /**< 最大等待次数 = 2分钟*5=600次 */
    delay_ms(each_wait);                /**< 首次等待 */
    
    /** 等待进程完全退出 */
    while(get_process_pid(process) > 0) /**< 检查进程是否还存在 */
    {
        if ((++loop_cnt) >= target_cnt) /**< 超时检查 */
        {
            printf("kill %s timeout, loop = %d\n", process, loop_cnt); /**< 打印超时信息 */
            return -1;                  /**< 进程回收超时 */
        }
        delay_ms(each_wait);            /**< 继续等待 */
    }
​
    process_exit_info(process, pid, sig, core); /**< 记录进程退出信息到日志 */
    printf("\033[35m[%d] %s kill sucess !! \n\033[0m", pid, process); /**< 彩色打印成功信息 */
​
    return 0;                           /**< 成功返回 */
}
​
#ifdef SYSDEMO_COREDUMP_ZIP_ENABLE      /**< 条件编译:核心转储压缩功能 */
/**
 * @brief 压缩进程的核心转储文件
 * @param process 进程名
 * @return int 成功返回0,失败返回-1
 * @note 设计模式:异步压缩(使用&后台执行)
 *       性能分析:gzip压缩可能消耗CPU,后台执行减少阻塞
 *       数据流结构:使用MAX_BUFFER_LEN(256)字节的缓冲区
 */
static int gzip_process_core(char *process)
{
    if (process == NULL)                /**< 参数检查 */
        return -1;                      /**< 进程名为空 */
​
    char path[MAX_BUFFER_LEN] = {0};    /**< 文件路径缓冲区 */
    snprintf(path, MAX_BUFFER_LEN, "%s/%s.core", SYSDEMO_CORE_FILE_PATH, process); /**< 构造core文件路径 */
    if (access(path, F_OK) < 0)         /**< 检查文件是否存在 */
        return -1;                      /**< core文件不存在 */
​
    char cmd[MAX_BUFFER_LEN] = {0};     /**< 命令缓冲区 */
    snprintf(cmd, MAX_BUFFER_LEN, "gzip %s&", path); /**< 构造gzip命令,&表示后台执行 */
​
    snprintf(path, MAX_BUFFER_LEN, "%s/%s.core.gz", SYSDEMO_CORE_FILE_PATH, process); /**< 构造压缩后文件路径 */
    if (access(path, F_OK) == 0)        /**< 检查是否已存在压缩文件 */
        remove(path);                   /**< 删除已存在的压缩文件,避免冲突 */
    
    system(cmd);                        /**< 执行压缩命令,性能:异步执行不阻塞 */
​
    return 0;                           /**< 成功返回 */
}
#endif                                  /**< 结束条件编译块 */
​
/**
 * @brief 存储进程杀死的原因到文件中
 * @param process 进程名称字符串
 * @param pid 进程ID
 * @param sig 导致进程终止的信号值
 * @param core 终止原因代码(位掩码,见sysdemo.h中的宏定义)
 * @return int 成功返回0,失败返回-1,正常退出返回0(不记录)
 * @note 设计模式:日志记录器模式,提供统一的进程终止记录
 *       性能分析:每次调用都涉及文件I/O,建议异步或批量处理
 *       数据流结构:info缓冲区大小MAX_BUFFER_LEN(256字节)
 */
int process_exit_info(char *process, int pid, int sig, unsigned int core)
{
    /** 参数有效性检查 */
    if (process == NULL || pid < 0 || (core & SYSDEMO_KILLCORE_NORMAL)) /**< 检查空指针、无效PID和正常退出标志 */
        return 0;                       /**< 正常退出不记录日志 */
​
    char info[MAX_BUFFER_LEN] = {0};    /**< 日志信息缓冲区,清零初始化 */
    time_t timep;                       /**< 时间戳变量,存储秒数 */
    time(&timep);                       /**< 获取当前系统时间,性能:系统调用开销 */
    struct tm *ptime = gmtime(&timep);  /**< 转换为UTC时间结构体,线程不安全但此处为局部变量 */
​
    /** 格式化日志字符串 */
    snprintf(info, MAX_BUFFER_LEN, "[%d-%02d-%02d %02d:%02d:%02d]\t%s\t%d-%d\t0x%08x\n", 
        (1900+ptime->tm_year), (1+ptime->tm_mon), ptime->tm_mday, /**< 年、月、日(tm_year从1900开始,tm_mon从0开始) */
        ptime->tm_hour, ptime->tm_min, ptime->tm_sec,             /**< 时、分、秒 */
        process, pid, sig, core                                   /**< 进程名、PID、信号、原因代码 */
    ); /**< 数据流:格式化为固定格式的字符串,便于解析 */
​
    /** 打开日志文件(追加模式) */
    FILE *fp = fopen(SYSDEMO_KILL_COREINFO_PATH, "a+"); /**< 以追加读写模式打开文件 */
    if (fp == NULL)                                     /**< 文件打开失败处理 */
    {
        printf("request_info_path %s error !!\n", SYSDEMO_KILL_COREINFO_PATH); /**< 打印错误信息 */
        return -1;                              /**< 返回失败 */
    }
    
    /** 写入日志信息 */
    int len = strlen(info);                     /**< 计算字符串长度(不含结尾空字符) */
    int ret = fwrite(info, 1, len, fp);         /**< 写入文件,fwrite返回实际写入字节数 */
    if (ret < len)                              /**< 写入字节数不足处理 */
    {
        printf("write_to_info_path %s error: %d !!\n", SYSDEMO_KILL_COREINFO_PATH, ret); /**< 打印错误信息 */
        fclose(fp);                             /**< 关闭文件 */
        return -1;                              /**< 返回失败 */
    }
    fclose(fp);                                 /**< 成功写入后关闭文件 */
​
    /** 日志文件轮询管理(限制文件大小) */
    struct stat fstat;                          /**< 文件状态结构体 */
    int linsize = 80;                           /**< 估算每行日志大小(字节),基于格式化字符串长度 */
    stat(SYSDEMO_KILL_COREINFO_PATH, &fstat);   /**< 获取文件状态,性能:系统调用开销 */
    if (fstat.st_size/linsize > SYSDEMO_KILL_COREINFO_MAX_LINES) /**< 计算行数并检查是否超过最大限制 */
    {
        char cmd[MAX_BUFFER_LEN] = {0};         /**< 命令缓冲区 */
        /** 构造sed命令删除前N行 */
        snprintf(cmd, MAX_BUFFER_LEN, "busybox sed -i \"1,%dd\" %s", SYSDEMO_KILL_COREINFO_DELETE_LINES, SYSDEMO_KILL_COREINFO_PATH);
        system(cmd);                            /**< 执行sed命令删除旧日志,性能:fork+exec开销较大 */
        /** 设计模式:使用外部工具实现日志轮转,简单但效率较低 */
    }
​
    printf("%s", info);                         /**< 同时在控制台输出日志信息 */
    return 0;                                   /**< 成功返回 */
}
​
/**
 * @brief 终止所有主程序节点进程
 * @return int 总是返回0
 * @note 设计模式:反向遍历终止,避免进程依赖问题
 *       性能分析:顺序终止,可能耗时较长
 */
static int stop_main_processes(void)
{
    int index;                                  /**< 循环索引,从后往前遍历 */
    for (index = cfg.proc_len-1; index >= 0; index--) /**< 反向遍历进程配置数组 */
    {
        if (cfg.process[index].type == TYPE_NORMAL || cfg.process[index].type == TYPE_CIE) /**< 只处理普通和CIE类型进程 */
        {
            kill_process(cfg.process[index].name, SIGKILL, SYSDEMO_KILLCORE_NORMAL); /**< 发送SIGKILL终止进程 */
            /** 设计模式:使用SYSDEMO_KILLCORE_NORMAL表示正常终止,不记录日志 */
        }
    }
    return 0;                                   /**< 总是成功返回 */
}
​
/**
 * @brief 根据进程名获取进程ID
 * @param process 进程名称字符串
 * @return int >0表示进程ID,<0表示获取失败
 * @note 设计模式:遍历/proc文件系统,查找comm文件匹配的进程
 *       性能分析:每次调用都遍历/proc,O(n)复杂度,可优化为缓存机制
 *       数据流结构:使用MAX_BUFFER_LEN(256)字节的缓冲区
 */
static int get_process_pid(char *process)
{
    DIR *dir;                                   /**< 目录流指针 */
    struct dirent *ptr;                         /**< 目录项结构体指针 */
    int fd, pid = -1;                           /**< 文件描述符和进程ID(初始化为-1) */
    char filepath[MAX_BUFFER_LEN] = {0};        /**< 文件路径缓冲区 */
    char buf[MAX_BUFFER_LEN] = {0};             /**< 读取缓冲区 */
    int len = strlen(process);                  /**< 进程名长度 */
​
    dir = opendir("/proc");                     /**< 打开/proc目录,性能:目录遍历开始 */
    if (dir == NULL)                            /**< 目录打开失败处理 */
    {
        printf("directory /proc not exist!\n"); /**< 打印错误信息(实际上/proc总是存在) */
        return -1;                              /**< 返回失败 */
    }
​
    /** 遍历/proc目录下所有子目录 */
    while ((ptr = readdir(dir)) != NULL)        /**< 读取下一个目录项 */
    {
        /** 跳过"."和".."目录 */
        if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0))
            continue;                           /**< 跳过当前目录和父目录 */
        
        /** 只处理目录类型 */
        if (DT_DIR != ptr->d_type)              /**< 检查是否为目录(d_type可能不可靠) */
            continue;                           /**< 跳过非目录项 */
        
        /** 只处理数字命名的目录(进程ID目录) */
        if (ptr->d_name[0] < '0' || ptr->d_name[0] > '9') /**< 检查目录名首字符是否为数字 */
            continue;                           /**< 跳过非进程ID目录 */
​
        /** 构造comm文件路径:/proc/[pid]/comm */
        snprintf(filepath, MAX_BUFFER_LEN, "/proc/%s/comm", ptr->d_name); /**< 格式化文件路径 */
        fd = open(filepath, O_RDONLY);          /**< 以只读方式打开comm文件 */
        if (fd < 0)                             /**< 文件打开失败(进程可能已退出) */
            continue;                           /**< 跳过该进程 */
​
        memset(buf, 0, len+1);                  /**< 清空缓冲区(只清空需要比较的部分+1) */
        int ret = read(fd, buf, len);           /**< 读取comm文件内容,最大读取进程名长度 */
        if (ret < 0)                            /**< 读取失败 */
        {
            close(fd);                          /**< 关闭文件描述符 */
            continue;                           /**< 跳过该进程 */
        }
​
        close(fd);                              /**< 读取成功后关闭文件描述符 */
​
        /** 比较进程名(注意:comm文件包含换行符,但strcmp比较到第一个不同字符) */
        if (strcmp(process, buf) == 0)          /**< 进程名匹配成功 */
        {
            pid = atoi(ptr->d_name);            /**< 将目录名转换为整数PID */
            break;                              /**< 找到后跳出循环 */
        }
    }
    closedir(dir);                              /**< 关闭目录流 */
    // printf("%s's pid = %d\n", process, pid); /**< 调试信息(已注释) */
​
    return pid;                                 /**< 返回PID(未找到返回-1) */
}
​
/**
 * @brief 获取进程的运行状态字符
 * @param pid 进程ID
 * @return char 进程状态字符,错误返回-1
 * @note 设计模式:解析/proc/[pid]/stat文件第三字段
 *       性能分析:每次调用打开文件,可缓存提高性能
 *       数据流结构:linebuff缓冲区32字节足够存放状态字符
 */
static char get_process_stat(int pid)
{
    char file_name[MAX_BUFFER_LEN]={0};         /**< 文件路径缓冲区 */
    int index = 0;                              /**< 字符索引 */
    int target = 3 - 1;                         /**< 目标字段索引(stat文件中第3个字段为状态) */
    FILE *fp = NULL;                            /**< 文件指针 */
    char linebuff[32]={0};                      /**< 行缓冲区,大小32字节 */
​
    snprintf(file_name, MAX_BUFFER_LEN, "/proc/%d/stat", pid); /**< 构造stat文件路径 */
    
    fp = fopen(file_name, "r");                 /**< 以只读方式打开stat文件 */
    if (NULL == fp)                             /**< 文件打开失败处理 */
    {
        printf("open %s error !!\n", file_name); /**< 打印错误信息(进程可能已退出) */
        return -1;                              /**< 返回错误 */
    }
    
    fgets(linebuff, 32-1, fp);                  /**< 读取一行(stat文件只有一行),保留一个字节给结尾空字符 */
    fclose(fp);                                 /**< 关闭文件 */
​
    /** 查找第三个空格分隔的字段 */
    for (index = 0; index < 32; index++)        /**< 遍历缓冲区 */
    {
        if (' ' != linebuff[index])             /**< 不是空格则继续 */
            continue;                           /**< 跳过非空格字符 */
​
        if((--target) == 0)                     /**< 找到目标字段前的空格 */
        {
            index++;                             /**< 移动到状态字符位置 */
            break;                              /**< 跳出循环 */
        }
    }
    if (index >= 32)                            /**< 索引越界处理 */
        return 0;                               /**< 返回0表示未找到 */
​
    // printf("pid[%d] stat = %c\n", pid, linebuff[index]); /**< 调试信息(已注释) */
    return linebuff[index];                     /**< 返回状态字符 */
}
​
/**
 * @brief 获取进程的CPU占用率(暂未实现)
 * @param pid 进程ID
 * @return int 失败返回-1
 * @note 预留接口,待需求明确后实现
 */
static int get_process_cpu(int pid)
{
    return -1;                                  /**< 暂未实现,返回-1 */
}
​
/**
 * @brief 获取进程的堆内存大小(单位:KB)
 * @param pid 进程ID
 * @return int 堆大小(KB),失败返回-1,无堆返回0
 * @note 设计模式:解析/proc/[pid]/maps文件查找[heap]段
 *       性能分析:线性搜索maps文件,最坏情况遍历所有内存段
 *       数据流结构:buf缓冲区128字节,maps行通常较短
 */
static int get_process_heap(int pid)
{
    char buf[MAX_BUFFER_LEN] = {0};             /**< 行读取缓冲区 */
    char path[MAX_BUFFER_LEN] = {0};            /**< 文件路径缓冲区 */
    char *delim = "-: \t\n";                    /**< 分隔符字符串:减号、冒号、空格、制表符、换行符 */
    char *p0 = NULL;                            /**< 字符串分割结果指针1 */
    char *p1 = NULL;                            /**< 字符串分割结果指针2 */
    int find_flag = 0;                          /**< 查找标志:0=未找到,1=找到 */
    int i, heap_start, heap_end, heap_size = 0; /**< 循环变量、堆起始地址、结束地址、堆大小 */
    snprintf(path, MAX_BUFFER_LEN, "/proc/%d/maps", pid); /**< 构造maps文件路径 */
​
    FILE *fp = fopen(path,"r");                 /**< 以只读方式打开maps文件 */
    if (fp == NULL)                             /**< 文件打开失败处理 */
    {
        printf("open %s fail !!\n", path);      /**< 打印错误信息 */
        return -1;                              /**< 返回失败 */
    }
​
    /** 逐行读取maps文件 */
    while (fgets(buf,128,fp) != NULL)           /**< 读取一行,最多127字符 */
    {
        int len = strlen(buf);                  /**< 获取行长度 */
        /** 查找包含"[heap]"的行 */
        for (i = 0; i < len; i++)               /**< 遍历行中每个字符 */
        {
            if (buf[i] == '[')                  /**< 找到左方括号 */
            {
                if (strncmp(&buf[i+1], "heap", strlen("heap")) == 0) /**< 检查是否为"[heap]" */
                {
                    find_flag = 1;              /**< 设置找到标志 */
                    break;                      /**< 跳出字符循环 */
                }
            }
        }
        if (find_flag == 0)                     /**< 未找到[heap]标签 */
            continue;                           /**< 继续下一行 */
​
        /** 解析地址范围(格式:start-end) */
        p0 = strtok(buf, delim);                /**< 第一次分割,获取起始地址十六进制字符串 */
        if (p0 == NULL)                         /**< 分割失败处理 */
        {
            fclose(fp);                         /**< 关闭文件 */
            return -1;                          /**< 返回失败 */
        }
        p1 = strtok(NULL, delim);               /**< 第二次分割,获取结束地址十六进制字符串 */
        if (p1 == NULL)                         /**< 分割失败处理 */
        {
            fclose(fp);                         /**< 关闭文件 */
            return -1;                          /**< 返回失败 */
        }
        /** 将十六进制字符串转换为整数并计算堆大小 */
        heap_start = strtol(p0, NULL, 16);      /**< 转换起始地址 */
        heap_end = strtol(p1, NULL, 16);        /**< 转换结束地址 */
        heap_size = (heap_end - heap_start)/1024; /**< 计算堆大小(KB) */
​
        // printf("Heap: %d KB\n", heap_size);   /**< 调试信息(已注释) */
        fclose(fp);                             /**< 关闭文件 */
        return heap_size;                       /**< 返回堆大小 */
    }
    fclose(fp);                                 /**< 关闭文件(未找到[heap]段) */
    return 0;                                   /**< 返回0表示无堆段 */
}
​
/**
 * @brief 进程阈值处理接口
 * @param process 进程配置结构体指针
 * @param where 阈值检查位置标识(如巡检时、启动时等)
 * @return int 总是返回0
 * @note 设计模式:策略模式,根据阈值配置采取不同处理方式
 *       性能分析:每次调用可能涉及CPU和堆内存检测,I/O开销
 */
static int process_threshold_handler(js_process_t *process, int where)
{
    /** 参数有效性检查 */
    if (process == NULL || process->threshold.where == 0 || (process->threshold.where & where) == 0) /**< 检查空指针和阈值使能位 */
        return 0;                       /**< 无需处理则直接返回 */
​
    int handler_flag = 0;               /**< 处理标志:0=未触发,1=已触发 */
    int cpu = get_process_cpu(process->pid); /**< 获取CPU使用率(当前返回-1未实现) */
    int heap = get_process_heap(process->pid); /**< 获取堆内存使用量(KB) */
    unsigned int mode = 0;               /**< 终止原因位掩码,初始为0 */
​
    /** CPU阈值检查 */
    if (process->threshold.cpu > 0 && process->threshold.cpu <= cpu) /**< 配置了CPU阈值且当前值超过阈值 */
    {
        handler_flag = 1;               /**< 设置处理标志 */
        mode |= SYSDEMO_KILLCORE_THRESHOLD_FROM_CPU; /**< 设置CPU超限位 */
        printf("\033[35m%s's cpu too high: %d%% \n\033[0m", process->name, cpu); /**< 彩色打印警告信息 */
    }
    
    /** 堆内存阈值检查 */
    if (process->threshold.heap > 0 && process->threshold.heap <= heap) /**< 配置了堆内存阈值且当前值超过阈值 */
    {
        handler_flag = 1;               /**< 设置处理标志 */
        mode |= SYSDEMO_KILLCORE_THRESHOLD_FROM_HEAP; /**< 设置堆内存超限位 */
        printf("\033[35m%s's heap too high: %d KB \n\033[0m", process->name, heap); /**< 彩色打印警告信息 */
    }
​
    if (handler_flag != 1)              /**< 未触发任何阈值 */
        return 0;                       /**< 直接返回 */
​
    /** 根据配置的处理方式执行相应操作 */
    switch (process->threshold.handler) /**< 检查阈值处理策略 */
    {
        case THRESHOLD_HANDLER_RESTART: /**< 重启策略 */
            kill_process(process->name, SIGKILL, mode); /**< 杀死进程 */
            run_main_process(process);   /**< 重新启动进程 */
            break;
        default:                        /**< 其他策略(暂未实现) */
            break;
    }
​
    return 0;                           /**< 总是成功返回 */
}
​
/**
 * @brief 判断当前rootfs是否挂载在指定的MTD分区
 * @param mtd MTD分区名称字符串
 * @return bool true=是,false=否
 * @note 设计模式:文件解析器,解析/proc/mounts文件
 *       性能分析:线性搜索,最坏情况遍历所有挂载点
 *       数据流结构:buf缓冲区128字节足够
 */
static bool is_rootfs_mtd(char *mtd)
{
    char buf[128] = {0};                 /**< 行读取缓冲区 */
    char *delim = "/ \t\n";              /**< 分隔符:斜杠、空格、制表符、换行符 */
​
    FILE *fp = fopen("/proc/mounts","r"); /**< 打开挂载信息文件 */
    if (fp == NULL)                      /**< 文件打开失败处理 */
    {
        printf("open /proc/mounts fail !!\n"); /**< 打印错误信息 */
        return false;                       /**< 返回false */
    }
​
    /** 逐行读取/proc/mounts文件 */
    while (fgets(buf,128,fp) != NULL)     /**< 读取一行 */
    {
        char *ptk = strtok(buf, delim);  /**< 第一次分割,获取设备名 */
        while (ptk != NULL)              /**< 遍历该行的所有字段 */
        {
            if (strncmp(ptk, mtd, strlen(mtd)) == 0) /**< 比较字段是否匹配目标MTD */
            {
                fclose(fp);              /**< 关闭文件 */
                return true;             /**< 找到匹配,返回true */
            }
            ptk = strtok(NULL, delim);   /**< 继续分割下一个字段 */
        }
    }
    fclose(fp);                          /**< 关闭文件 */
    return false;                        /**< 未找到匹配,返回false */
}
​
/**
 * @brief 判断CIE主进程是否已经正常运行
 * @param process 当前待检查的进程配置
 * @return bool true=CIE运行正常可启动其他进程,false=CIE未运行或运行异常
 * @note 设计模式:依赖检查器,确保进程启动顺序
 *       性能分析:最多等待SYSDEMO_PROCESS_WAIT_MAIN_RUN秒,每秒检查一次
 */
static bool is_main_run_ok(js_process_t *process)
{
    /** CIE和HLOG进程自身不需要等待 */
    if (process->type == TYPE_CIE || process->type == TYPE_HLOG)
        return true;                     /**< 直接返回true */
​
    /** 防止空指针(main_cie可能未初始化) */
    if (main_cie == NULL)                /**< 主CIE指针为空 */
        return false;                    /**< 返回false */
​
    /** 等待CIE主进程启动 */
    int cnt = SYSDEMO_PROCESS_WAIT_MAIN_RUN; /**< 最大等待次数(秒) */
    while(cnt--)                          /**< 循环等待 */
    {
        if (get_process_pid(main_cie->name) > 0) /**< 检查CIE进程是否存在 */
            return true;                   /**< CIE已启动,返回true */
        delay_s(1);                        /**< 等待1秒,性能:每秒检查一次 */
    }
    
    /** 最终检查 */
    if (get_process_pid(main_cie->name) < 0) /**< 等待超时后再次检查 */
        return false;                    /**< CIE仍未启动,返回false */
    return true;                         /**< CIE已启动,返回true */
}
​
/**
 * @brief 判断当前是否为系统刚上电的状态
 * @return bool true=首次上电,false=非首次上电
 * @note 设计模式:状态检测器,通过检查所有配置进程是否存在判断
 *       性能分析:遍历所有进程配置,每个进程调用get_process_pid(O(n)复杂度)
 *       注释说明:经测试,该接口耗时约为进程数*5ms
 */
static bool is_first_poweron(void)
{
    int index;                            /**< 循环索引 */
    for (index = 0; index < cfg.proc_len; index++) /**< 遍历所有进程配置 */
    {
        int pid = get_process_pid(cfg.process[index].name); /**< 获取进程PID */
        if (pid > 0)                      /**< 进程已存在 */
            return false;                 /**< 非首次上电 */
    }
    return true;                          /**< 所有进程都不存在,首次上电 */
}
​
/**
 * @brief 获取程序执行的命令行字符串
 * @param cmd 存放命令的缓冲区
 * @param process 程序配置信息
 * @param mode 运行模式参数
 * @param mode_flag 是否启用mode参数(1=启用,0=不启用)
 * @return int 总是返回0
 * @note 设计模式:命令构建器,根据配置动态构建执行命令
 *       性能分析:字符串拼接操作,开销较小
 *       数据流结构:cmd缓冲区大小MAX_BUFFER_LEN(256字节)
 */
static int get_process_run_cmd(char cmd[MAX_BUFFER_LEN], js_process_t *process, int mode, int mode_flag)
{
    char *path = process->path;           /**< 进程路径 */
    char *name = process->name;           /**< 进程名称 */
    char *arg = process->arg[0];          /**< 默认参数(第一个参数) */
​
    /** 处理多参数配置(arg_len为偶数时有效) */
    if (process->arg_len > 1 && process->arg_len%2 == 0) /**< 参数成对出现:条件-参数 */
    {
        int index;                        /**< 循环索引 */
        for (index = 0; index < process->arg_len; index+=2) /**< 每次跳两个元素 */
        {
            if (strcmp("default", process->arg[index]) == 0) /**< 找到"default"条件 */
                break;                    /**< 使用默认参数 */
            if (is_rootfs_mtd(process->arg[index]) == true) /**< 检查MTD条件是否满足 */
                break;                    /**< 使用对应参数 */
        }
        arg = process->arg[index+1];      /**< 获取对应参数值 */
    }
​
    /** 根据mode_flag构造完整命令 */
    if (mode_flag == 1)                   /**< 启用mode参数 */
        snprintf(cmd, MAX_BUFFER_LEN, "%s/%s %d %s", path, name, mode, arg); /**< 包含mode参数 */
    else                                  /**< 不启用mode参数 */
        snprintf(cmd, MAX_BUFFER_LEN, "%s/%s %s", path, name, arg); /**< 不包含mode参数 */
    
    return 0;                             /**< 总是成功返回 */
}
​
/**
 * @brief 执行主进程管理逻辑
 * @param process 进程配置结构体指针
 * @return int 成功返回进程PID,进程不存在返回-2,执行失败返回-1
 * @note 设计模式:进程生命周期管理器,包含启动、监控、重启逻辑
 *       性能分析:涉及文件检查、进程状态获取、系统命令执行
 *       数据流结构:使用MAX_BUFFER_LEN(256字节)的缓冲区
 */
static int run_main_process(js_process_t *process)
{
    /** 参数有效性检查 */
    if (process == NULL)                /**< 空指针检查 */
        return -2;                      /**< 返回进程不存在错误码 */
​
    /** 检查可执行文件是否存在且可执行 */
    char path[MAX_BUFFER_LEN] = {0};    /**< 可执行文件完整路径缓冲区 */
    snprintf(path, MAX_BUFFER_LEN, "%s/%s", process->path, process->name); /**< 拼接路径和文件名 */
    if (access(path, X_OK) < 0)         /**< 检查文件是否存在且有执行权限 */
        return -2;                      /**< 文件不可执行,返回进程不存在 */
​
    /** 检查依赖的主进程是否已正常运行 */
    if (is_main_run_ok(process) == false) /**< 对于非CIE进程,需要等待CIE启动 */
        return -1;                      /**< 依赖进程未就绪,返回失败 */
​
    /** 检查进程是否已经在运行 */
    int pid = get_process_pid(process->name); /**< 通过进程名获取PID */
    if (pid > 0)                        /**< 进程已存在 */
    {
        process->pid = pid;              /**< 更新配置结构体中的PID */
        if (process->type == TYPE_CIE)   /**< 如果是CIE主进程 */
            main_cie = process;          /**< 更新全局主进程指针 */
        return pid;                     /**< 返回已有PID */
    }
    
    /** 进程不存在,需要启动 */
    char cmd[MAX_BUFFER_LEN] = {0};     /**< 命令缓冲区 */
​
    /** CIE主进程的特殊处理:生命周期检测 */
    if (process->type == TYPE_CIE)      /**< 仅对CIE主进程进行生命周期管理 */
    {
        long date = get_sysuptime();    /**< 获取当前系统运行时间 */
        /** 检查进程存活时间是否过短 */
        if (date < exe_time + SYSDEMO_PROCESS_MIN_LIFETIME) /**< 如果距离上次启动时间小于最小生命周期 */
        {
            ++exe_count;                /**< 增加异常次数计数器 */
            if (exe_count > 1)          /**< 第一次异常不打印kill计数 */
                printf("kill num = %d\n", exe_count-1); /**< 打印异常重启次数 */
            /** 检查是否超过最大异常次数 */
            if (exe_count > SYSDEMO_PROCESS_LIFETIME_MAX) /**< 超过最大允许异常次数 */
            {
                /** 打印致命错误信息并退出 */
                printf("%s/%s cannot normal runing, exit 0x%X\n", process->path, process->name, SYSDEMO_EXIT_CORE_FROM_LIFETIME);
                process_exit_info("sysdemo", getpid(), 0, SYSDEMO_EXIT_CORE_FROM_LIFETIME); /**< 记录守护进程自身退出信息 */
                exit(SYSDEMO_EXIT_CORE_FROM_LIFETIME); /**< 守护进程自身退出 */
            }
        }
        else                            /**< 进程存活时间正常 */
        {
            exe_count = 0;              /**< 重置异常计数器 */
        }
        
        /** CIE启动前停止所有其他进程 */
        stop_main_processes();          /**< 停止所有主进程 */
        main_oom_flag = 0;              /**< 重置OOM调整标志 */
    }
    
    /** 构造执行命令 */
    get_process_run_cmd(cmd, process, 0, 0); /**< 获取进程执行命令,不传递mode参数 */
    // printf("cmd = %s\n", cmd);       /**< 调试信息(已注释) */
​
    /** 执行进程(使用system()函数) */
    #if 0                               /**< 条件编译:备用的run_exec函数(已注释) */
    int status = run_exec(process->path, process->name, process->arg, 0);
    #endif
    int status = system(cmd);           /**< 执行shell命令启动进程,性能:fork+exec开销 */
    if (-1 == status || WIFEXITED(status) != true || 0 != WEXITSTATUS(status)) /**< 检查执行结果 */
    {
        printf("run process error: %s\n", cmd); /**< 打印错误信息 */
        return -1;                      /**< 返回执行失败 */
    }
​
    /** 等待进程稳定(如果配置了等待时间) */
    if (process->wait)                  /**< 配置了等待时间 */
        delay_s(process->wait);         /**< 等待指定秒数 */
​
    /** 获取新启动进程的PID */
    process->pid = get_process_pid(process->name); /**< 重新获取PID */
    if (process->type == TYPE_CIE)      /**< CIE主进程的特殊处理 */
    {
        main_cie = process;              /**< 更新全局主进程指针 */
        /** 设置OOM调整分数(防止CIE被OOM killer杀死) */
        if (main_oom_flag == 0 && main_cie->pid > 0) /**< 未设置过且PID有效 */
        {
            //#echo -17 > /proc/`busybox pgrep cie_app`/oom_adj 已弃用 /**< 旧方法注释 */
            snprintf(cmd, MAX_BUFFER_LEN, "echo -1000 > /proc/%d/oom_score_adj", main_cie->pid); /**< 新方法:设置OOM分数调整 */
            system(cmd);                /**< 执行echo命令设置OOM调整 */
            main_oom_flag = 1;          /**< 设置标志防止重复执行 */
        }
        exe_time = get_sysuptime();     /**< 记录CIE启动时间 */
    }
​
    /** 打印成功启动信息 */
    if (process->pid > 0)               /**< PID有效 */
        printf("\033[35m[%d] %s init sucess !! \n\033[0m", process->pid, process->name); /**< 彩色打印成功信息 */
​
    return process->pid;                /**< 返回新进程PID */
}
​
/**
 * @brief 执行工厂测试节点进程
 * @param process 进程配置结构体指针
 * @return int 成功返回进程PID,进程不存在返回-2,执行失败返回-1,进程已运行返回1
 * @note 设计模式:简单进程管理器,只负责启动和状态维护
 *       性能分析:相比主进程逻辑更简单,无生命周期检测
 */
static int run_factory_process(js_process_t *process)
{
    /** 参数有效性检查 */
    if (process == NULL)                /**< 空指针检查 */
        return -2;                      /**< 返回进程不存在错误码 */
​
    /** 检查可执行文件是否存在且可执行 */
    char path[MAX_BUFFER_LEN] = {0};    /**< 可执行文件完整路径缓冲区 */
    snprintf(path, MAX_BUFFER_LEN, "%s/%s", process->path, process->name); /**< 拼接路径和文件名 */
    if (access(path, X_OK) < 0)         /**< 检查文件是否存在且有执行权限 */
        return -2;                      /**< 文件不可执行,返回进程不存在 */
​
    /** 检查进程是否已经在运行 */
    int pid = get_process_pid(process->name); /**< 通过进程名获取PID */
    if (pid > 0)                        /**< 进程已存在 */
    {
        process->pid = pid;              /**< 更新配置结构体中的PID */
        return 1;                       /**< 返回1表示进程已运行(区别于PID) */
    }
​
    /** 进程不存在,需要启动 */
    char cmd[MAX_BUFFER_LEN] = {0};     /**< 命令缓冲区 */
    get_process_run_cmd(cmd, process, 0, 0); /**< 获取进程执行命令 */
    // printf("cmd = %s\n", cmd);       /**< 调试信息(已注释) */
    
    /** 执行进程 */
    int status = system(cmd);           /**< 执行shell命令启动进程 */
    if (-1 == status || WIFEXITED(status) != true || 0 != WEXITSTATUS(status)) /**< 检查执行结果 */
    {
        printf("run process error: %s\n", cmd); /**< 打印错误信息 */
        return -1;                      /**< 返回执行失败 */
    }
​
    /** 等待进程稳定 */
    delay_s(process->wait);             /**< 等待配置的时间 */
​
    /** 获取新启动进程的PID */
    process->pid = get_process_pid(process->name); /**< 重新获取PID */
​
    /** 打印成功启动信息 */
    if (process->pid > 0)               /**< PID有效 */
        printf("[%d] %s init sucess !! \n", process->pid, process->name); /**< 打印成功信息 */
​
    return process->pid;                /**< 返回新进程PID */
}
​
/**
 * @brief 备用的进程执行函数(使用fork+exec,当前未使用)
 * @param path 可执行文件路径
 * @param name 进程名称
 * @param arg 命令行参数字符串
 * @param block_flag 是否阻塞等待
 * @return int 成功返回0,失败返回-1
 * @note 设计模式:直接进程创建,相比system()更高效但更复杂
 *       性能分析:减少shell解释器开销,但需手动处理参数解析
 */
#if 0                                   /**< 条件编译:当前未使用此函数 */
static int run_exec(char *path, char *name, char *arg, int block_flag)
{
    pid_t pid = fork();                 /**< 创建子进程 */
    if (pid < 0)                        /**< fork失败 */
        return -1;                      /**< 返回失败 */
​
    if (pid == 0)                       /**< 子进程代码 */
    {
        if (arg == NULL)                /**< 无参数情况 */
        {
            execlp(path, name, NULL);   /**< 执行程序,自动搜索PATH */
        }
        else                            /**< 有参数情况 */
        {
            char *argv[128];            /**< 参数指针数组,最大128个参数 */
            char tmp[128];              /**< 参数字符串临时缓冲区 */
            int cnt = 0;                /**< 参数计数器 */
            char *delim = " ";          /**< 参数分隔符(空格) */
​
            strcpy(tmp, arg);           /**< 复制参数字符串到临时缓冲区 */
            argv[cnt++] = name;         /**< 第一个参数为程序名 */
            argv[cnt] = strtok(tmp, delim); /**< 分割第一个参数 */
            while (argv[cnt] != NULL)   /**< 继续分割所有参数 */
            {
                argv[++cnt] = strtok(NULL, delim); /**< 分割下一个参数 */
            }
            execvp(path, argv);         /**< 执行程序,自动搜索PATH */
        }
    }
​
    int status;                         /**< 子进程退出状态 */
    wait(&status);                      /**< 等待子进程退出 */
​
    return 0;                           /**< 成功返回 */
}
#endif                                  /**< 结束条件编译块 */
​
/**
 * @brief 初始化系统守护进程消息模块
 * @return int 总是返回0
 * @note 设计模式:模块初始化器,负责消息系统的初始化和注册
 *       性能分析:涉及消息队列/共享内存等IPC机制初始化
 */
static int sys_demo_msg_init(void)
{   
    module_logout(SYS_WATCHDOG_MODULE); /**< 先登出(确保不会重复登录) */
    init_beat_msg_list();               /**< 初始化心跳消息列表 */
    syswd_mod_id = module_login(SYS_WATCHDOG_MODULE, 32, sys_demo_msg_handle); /**< 登录消息模块,注册处理函数 */
    return 0;                           /**< 总是成功返回 */
}
​
/**
 * @brief 进程管理模块的消息发送接口
 * @param dest 目标模块名称,NULL表示广播
 * @param msgtype 消息类型
 * @param message 消息数据指针
 * @param len 消息数据长度
 * @return int 成功返回消息发送结果,失败返回-1
 * @note 设计模式:门面模式,封装底层消息发送细节
 *       性能分析:消息发送性能取决于底层IPC机制
 */
int sys_demo_msg_send(char *dest, int msgtype, void *message, unsigned int len)
{
    if(syswd_mod_id <= 0)               /**< 检查消息模块是否已初始化 */
        return -1;                      /**< 模块未初始化,返回失败 */
    
    if(dest != NULL)                    /**< 指定目标模块 */
        return module_msg_send(syswd_mod_id, dest, MSG_SEND_P2P, msgtype, message, len); /**< 点对点发送 */
    else                                /**< 目标为空 */
        return module_msg_send(syswd_mod_id, NULL, MSG_SEND_BROADCAST, msgtype, message, len); /**< 广播发送 */
}
​
/**
 * @brief 进程管理模块的消息处理回调函数
 * @param msg 消息结构体指针
 * @note 设计模式:观察者模式,响应系统事件消息
 *       性能分析:消息处理应快速完成,避免阻塞消息队列
 */
static void sys_demo_msg_handle(message_t *msg)
{
    if (msg == NULL)                    /**< 空消息检查 */
        return ;                        /**< 直接返回 */
​
    int index;                          /**< 循环索引 */
    switch (msg->msg_type)              /**< 根据消息类型处理 */
    {
        case MNTALL_RESET_BEGIN_MSG:    /**< 系统重置开始消息 */
            for (index = 0; index < cfg.proc_len; index++) /**< 遍历所有进程 */
            {
                process_threshold_handler(&cfg.process[index], THRESHOLD_WHERE_RESET_BEGIN); /**< 在重置开始处检查阈值 */
            }
            break;                      /**< 结束case */
​
        case MNTALL_RESET_END_MSG:      /**< 系统重置结束消息 */
            for (index = 0; index < cfg.proc_len; index++) /**< 遍历所有进程 */
            {
                process_threshold_handler(&cfg.process[index], THRESHOLD_WHERE_RESET_END); /**< 在重置结束处检查阈值 */
            }
            break;                      /**< 结束case */
        
        default:                        /**< 其他未处理的消息类型 */
            break;                      /**< 忽略 */
    }
}
​
/**
 * @brief 打印进程管理模块的调试信息
 * @note 设计模式:调试信息聚合器,集中展示系统状态
 *       性能分析:遍历所有进程配置,可能有较多输出
 */
static void debug_process_handler(void)
{
    int index;                          /**< 循环索引 */
    
    /** 打印模块头部信息 */
    printf("--------------- debug_process_handler --------------\n"); /**< 分隔线 */
    printf("| syswd module id = %08x\n", syswd_mod_id); /**< 消息模块ID */
    printf("| linux runtime = %ld s\n", get_sysuptime()); /**< 系统运行时间 */
    printf("| cie runtime = %ld s\n", exe_time);       /**< CIE进程运行时间 */
    printf("| cie run error cnt = %d\n", exe_count);   /**< CIE异常次数 */
    printf("----------------------------------------------------\n"); /**< 分隔线 */
    
    /** 遍历并打印每个进程的详细信息 */
    for (index = 0; index < cfg.proc_len; index++) /**< 遍历所有进程配置 */
    {
        printf("| path: %s/%s\n", cfg.process[index].path, cfg.process[index].name); /**< 进程路径和名称 */
        printf("| arg: \n");                        /**< 参数信息标题 */
        
        /** 打印参数配置 */
        if (cfg.process[index].arg_len == 1)        /**< 单个参数(默认配置) */
        {
            printf("|      default partition: %s\n", cfg.process[index].arg[0]); /**< 默认分区参数 */
        }
        else                                        /**< 多个参数对 */
        {
            int i;                                  /**< 内部循环索引 */
            for (i = 0; i < cfg.process[index].arg_len; i += 2) /**< 每次跳两个元素(条件-参数对) */
                printf("|      %s partition: %s\n", cfg.process[index].arg[i], cfg.process[index].arg[i+1]); /**< 打印条件参数对 */
        }
        
        printf("| wait: %d s\n", cfg.process[index].wait); /**< 启动等待时间 */
        printf("| pid: %d\n", cfg.process[index].pid); /**< 当前进程PID */
        printf("| threshold: \n");                 /**< 阈值配置标题 */
        
        /** 打印阈值配置详情 */
        printf("|      max cpu: %d%s\n", cfg.process[index].threshold.cpu, 
               cfg.process[index].threshold.cpu?"":"(not set)"); /**< CPU阈值,未设置时标注 */
        printf("|      max heap: %d%s\n", cfg.process[index].threshold.heap, 
               cfg.process[index].threshold.heap?"":"(not set)"); /**< 堆内存阈值,未设置时标注 */
        printf("|      handler: %d%s\n", cfg.process[index].threshold.handler, 
               cfg.process[index].threshold.handler?"":"(nothing to do)"); /**< 处理方式,未设置时标注 */
        printf("|      where: %08x%s\n", cfg.process[index].threshold.where, 
               cfg.process[index].threshold.where?"":"(not set)"); /**< 检查位置位掩码,未设置时标注 */
        
        printf("----------------------------------------------------\n"); /**< 每个进程信息后的分隔线 */
    }
}
​
/**
 * @brief SIGUSR1信号处理函数,用于调试
 * @param sig 信号值(应为SIGUSR1)
 * @note 设计模式:信号处理器,响应调试请求
 *       性能分析:信号处理函数中应避免复杂操作,此处仅打印信息
 */
static void debug_sysdemo_handler(int sig)
{
    debug_process_handler();             /**< 打印进程管理调试信息 */
    debug_backup_handler();              /**< 打印备份模块调试信息(外部函数) */
}
​

2. demo_config.c/h - 配置管理

设计思想: 数据与逻辑分离
├── 为什么使用JSON配置?
│   ├── 人类可读/可编辑
│   ├── 结构化数据,支持复杂配置
│   ├── 动态加载,无需重新编译
│   └── 易于扩展,添加新字段
├── cJSON库集成
│   ├── 轻量级,适合嵌入式
│   ├── 内存管理可控
│   └── API简洁
└── 结构体设计层次
    ├── demofcg_t (顶层容器)
    ├── js_bootcfg_t (启动配置)
    ├── js_process_t (进程配置,最复杂)
    ├── js_backup_t (备份配置)
    └── js_threshold_t (阈值配置,内嵌)
/**
 * @file demo_config.c
 * @brief 配置文件解析实现文件,负责JSON配置文件的读取和解析
 */
​
#include <stdio.h>                      /**< 标准输入输出头文件,提供printf等函数 */
#include <stdlib.h>                     /**< 标准库头文件,提供malloc、free等函数 */
#include <string.h>                     /**< 字符串处理头文件,提供memset、strlen等函数 */
​
#include <sys/stat.h>                   /**< 文件状态头文件,提供stat结构体和函数 */
#include <fcntl.h>                      /**< 文件控制头文件,提供open、O_RDONLY等常量 */
#include <unistd.h>                     /**< UNIX标准函数头文件,提供access、read、close等函数 */
​
#include "cJSON.h"                      /**< cJSON库头文件,提供JSON解析功能 */
#include "demo_config.h"                /**< 配置文件头文件,包含结构体定义和常量 */
​
/**
 * @defgroup JSON_NODE_NAMES JSON节点名称常量
 * @brief JSON配置文件中各节点的名称定义
 * @{
 */
#define JSNODE_BOOT_NAME      "boot"             /**< 启动配置的json节点名称 */
#define JSNODE_MAIN_NAME      "main process"     /**< 主应用的json节点名称 */
#define JSNODE_FACT_NAME      "factory process"  /**< 产测应用的json节点名称 */
#define JSNODE_BACKUP_NAME    "backup files"     /**< 文件备份的json节点名称 */
/** @} */ /* JSON_NODE_NAMES */
​
/** 全局cJSON对象指针,存储解析后的JSON树 */
static cJSON *root = NULL;              /**< JSON根节点指针,设计模式:单例模式思想 */
static cJSON *boot_root = NULL;         /**< boot节点指针 */
static cJSON *main_root = NULL;         /**< main process节点指针 */
static cJSON *fact_root = NULL;         /**< factory process节点指针 */
static cJSON *back_root = NULL;         /**< backup files节点指针 */
​
/** 静态函数声明 */
static int get_json_config_file(void);  /**< 读取并解析JSON配置文件 */
static int get_boot_cfg(js_bootcfg_t *boot); /**< 解析boot节点配置 */
static int get_process_cfg(js_process_t **process, int *proc_len); /**< 解析进程节点配置 */
static int get_backup_cfg(js_backup_t **backup, int *bak_len); /**< 解析备份节点配置 */
​
static int release_json_config_file(void); /**< 释放JSON解析树内存 */
static int release_boot_cfg(js_bootcfg_t *boot); /**< 释放boot配置内存 */
static int release_process_cfg(js_process_t **process); /**< 释放进程配置内存 */
static int release_backup_cfg(js_backup_t **bakup); /**< 释放备份配置内存 */
​
static int get_process_nodes(cJSON *array, js_process_t *process, int len); /**< 解析进程数组节点 */
static int get_shell_nodes(cJSON *array,  js_shell_t *shell, int len); /**< 解析shell命令数组节点 */
static int get_backup_nodes(cJSON *array,  js_backup_t *bakup, int len); /**< 解析备份数组节点 */
​
/**
 * @brief 获取演示配置(主入口函数)
 * @param cfg 配置结构体指针,用于存储解析后的配置
 * @return int 成功返回0,失败返回-1
 * @note 设计模式:外观模式,提供简化的配置获取接口
 *       性能分析:总耗时约3.76ms(如测试代码所示),包含文件I/O和JSON解析
 */
int get_demo_config(demofcg_t *cfg)
{
    /** 读取并解析JSON配置文件 */
    if (get_json_config_file() < 0)     /**< 读取配置文件失败 */
    {
        printf("get json config file error !!\n"); /**< 打印错误信息 */
        release_json_config_file();      /**< 清理可能已分配的资源 */
        return -1;                      /**< 返回错误 */
    }
​
    /** 解析boot节点配置(允许失败,非关键) */
    if (get_boot_cfg(&(cfg->boot)) < 0) /**< 解析boot配置 */
    {
        printf("get boot config error !!\n"); /**< 打印警告信息 */
    }
​
    /** 解析备份节点配置(允许失败,非关键) */
    if (get_backup_cfg(&(cfg->backup), &(cfg->bak_len)) < 0) /**< 解析备份配置 */
    {
        printf("get backup config error !!\n"); /**< 打印警告信息 */
    }
​
    /** 解析进程节点配置(关键,失败需清理) */
    if (get_process_cfg(&(cfg->process), &(cfg->proc_len)) < 0) /**< 解析进程配置 */
    {
        printf("get process config error !!\n"); /**< 打印错误信息 */
        release_boot_cfg(&(cfg->boot)); /**< 释放已分配的boot配置内存 */
        release_backup_cfg(&(cfg->backup)); /**< 释放已分配的备份配置内存 */
        release_json_config_file();      /**< 释放JSON解析树 */
        return -1;                      /**< 返回错误 */
    }
    return 0;                           /**< 成功返回 */
}
​
/**
 * @brief 释放演示配置占用的内存
 * @param cfg 配置结构体指针
 * @return int 总是返回0
 * @note 设计模式:资源清理器,对称于get_demo_config
 *       性能分析:释放所有动态分配的内存,防止内存泄漏
 */
int release_demo_config(demofcg_t *cfg)
{
    release_boot_cfg(&(cfg->boot));     /**< 释放boot配置内存 */
    release_process_cfg(&(cfg->process)); /**< 释放进程配置内存 */
    release_backup_cfg(&(cfg->backup)); /**< 释放备份配置内存 */
    release_json_config_file();         /**< 释放JSON解析树 */
    memset(cfg, 0, sizeof(demofcg_t));  /**< 清零配置结构体,数据流:安全清理 */
    return 0;                           /**< 总是成功返回 */
}
​
/**
 * @brief 获取配置文件内容并解析为cJSON树
 * @return int 成功返回0,失败返回-1
 * @note 设计模式:单例模式,全局只加载一次配置文件
 *       性能分析:涉及文件I/O和JSON解析,是主要耗时操作
 */
static int get_json_config_file(void)
{
    if (root != NULL)                   /**< 已经加载过,直接返回(单例模式) */
        return 0;                       /**< 已初始化,直接成功返回 */
    
    char *filepath = DEMO_CONFIG_PATH_BAK; /**< 默认使用备用路径 */
    #ifdef ENABLE_PATCH_PATH           /**< 如果启用了补丁路径功能 */
    if (access(DEMO_CONFIG_PATH, R_OK) >= 0) /**< 检查首选路径是否存在且可读 */
    {
        filepath = DEMO_CONFIG_PATH;    /**< 使用首选路径 */
    }
    #endif
​
    /** 打开配置文件 */
    int fd = open(filepath, O_RDONLY);  /**< 以只读方式打开文件 */
    if (fd < 0)                         /**< 打开文件失败 */
    {
        printf("open %s error !!\n", filepath); /**< 打印错误信息 */
        return -1;                      /**< 返回错误 */
    }
    printf("get jsconfig from %s !!\n", filepath); /**< 打印加载的配置文件路径 */
​
    /** 获取文件大小 */
    struct stat statbuff;               /**< 文件状态结构体 */
    if(stat(filepath, &statbuff) < 0)   /**< 获取文件状态失败 */
    {
        printf("get %s file stat error !!\n", filepath); /**< 打印错误信息 */
        return -1;                      /**< 返回错误 */
    }
    
    /** 分配文件内容缓冲区 */
    char *filebuff = (char *)malloc(statbuff.st_size); /**< 根据文件大小分配内存 */
    if (filebuff == NULL)               /**< 内存分配失败 */
    {
        printf("file buffer malloc error !!\n"); /**< 打印错误信息 */
        return -1;                      /**< 返回错误 */
    }
    
    /** 读取文件内容 */
    lseek(fd, 0, SEEK_SET);             /**< 确保文件指针在开头 */
    if (read(fd, filebuff, statbuff.st_size) < 0) /**< 读取整个文件 */
    {
        printf("read %s error!! \n", filepath); /**< 读取失败 */
        free(filebuff);                 /**< 释放缓冲区 */
        filebuff = NULL;                /**< 指针置空 */
        return -1;                      /**< 返回错误 */
    }
    close(fd);                          /**< 关闭文件描述符 */
​
    /** 解析JSON内容 */
    root = cJSON_Parse(filebuff);       /**< 解析JSON字符串为cJSON树 */
    if (root == NULL)                   /**< 解析失败 */
    {
        printf("parse config file error!\n"); /**< 打印错误信息 */
        free(filebuff);                 /**< 释放缓冲区 */
        filebuff = NULL;                /**< 指针置空 */
        return -1;                      /**< 返回错误 */
    }
​
    /** 删除注释性节点(减少内存占用) */
    cJSON_DeleteItemFromObject(root, "copyright"); /**< 删除版权信息节点 */
    cJSON_DeleteItemFromObject(root, "version");   /**< 删除版本信息节点 */
    cJSON_DeleteItemFromObject(root, "boot description"); /**< 删除boot描述节点 */
    cJSON_DeleteItemFromObject(root, "process description"); /**< 删除进程描述节点 */
​
    /** 获取各功能节点 */
    boot_root = cJSON_GetObjectItem(root, JSNODE_BOOT_NAME); /**< 获取boot节点 */
    if (boot_root == NULL)              /**< boot节点不存在 */
    {
        printf("get boot config json node error !!\n"); /**< 打印警告信息 */
    }
    main_root = cJSON_GetObjectItem(root, JSNODE_MAIN_NAME); /**< 获取main process节点 */
    if (main_root == NULL)              /**< main节点不存在 */
    {
        printf("get main config json node error !!\n"); /**< 打印警告信息 */
    }
    fact_root = cJSON_GetObjectItem(root, JSNODE_FACT_NAME); /**< 获取factory process节点 */
    if (fact_root == NULL)              /**< factory节点不存在 */
    {
        printf("get factory config json node error !!\n"); /**< 打印警告信息 */
    }
    back_root = cJSON_GetObjectItem(root, JSNODE_BACKUP_NAME); /**< 获取backup files节点 */
    if (back_root == NULL)              /**< backup节点不存在 */
    {
        printf("get backup config json node error !!\n"); /**< 打印警告信息 */
    }
    
    /** 调试信息(已注释) */
    // printf("root = %s\n", cJSON_Print(root));
    // printf("boot_root = %s\n", cJSON_Print(boot_root));
    // printf("main_root = %s\n", cJSON_Print(main_root));
    // printf("fact_root = %s\n", cJSON_Print(fact_root));
    // printf("root_len = %lu\n", strlen(cJSON_Print(root)));
​
    free(filebuff);                     /**< 释放原始文件内容缓冲区 */
​
    return 0;                           /**< 成功返回 */
}
​
/**
 * @brief 释放JSON解析树内存
 * @return int 总是返回0
 * @note cJSON_Delete会自动递归释放所有子节点
 */
static int release_json_config_file()
{
    cJSON_Delete(root);                 /**< 递归删除整个JSON树 */
    root = NULL;                        /**< 根指针置空 */
    boot_root = NULL;                   /**< boot节点指针置空 */
    main_root = NULL;                   /**< main节点指针置空 */
    fact_root = NULL;                   /**< factory节点指针置空 */
    back_root = NULL;                   /**< backup节点指针置空 */
    return 0;                           /**< 总是成功返回 */
}
​
/**
 * @brief 解析boot节点配置
 * @param boot boot配置结构体指针
 * @return int 成功返回0,失败返回-1
 * @note 设计模式:建造者模式,逐步构建配置对象
 */
static int get_boot_cfg(js_bootcfg_t *boot)
{
    if (boot_root == NULL)              /**< boot节点不存在 */
        return -1;                      /**< 返回错误 */
​
    /** 获取shell命令数组 */
    cJSON *shell  = cJSON_GetObjectItem(boot_root, "shell"); /**< 获取shell数组节点 */
    cJSON *check_freq = cJSON_GetObjectItem(boot_root, "check freq"); /**< 获取检查频率节点(已弃用) */
​
    if (shell != NULL)                  /**< shell节点存在 */
    {
        if (boot->shell != NULL)        /**< 如果之前已分配内存(安全处理) */
        {
            free(boot->shell);          /**< 先释放旧内存 */
        }
        boot->shell_len = cJSON_GetArraySize(shell); /**< 获取数组大小 */
        if (boot->shell_len > 0)        /**< 数组非空 */
        {
            /** 分配shell配置结构体数组内存 */
            boot->shell = (js_shell_t *)malloc(boot->shell_len * sizeof(js_shell_t)); /**< 动态分配内存 */
            if (boot->shell != NULL)    /**< 分配成功 */
            {
                /** 解析shell节点数组 */
                boot->shell_len = get_shell_nodes(shell, boot->shell, boot->shell_len); /**< 解析实际有效项数 */
            }
        }
    }
​
    if (check_freq != NULL)             /**< check freq节点存在(向后兼容) */
    {
        boot->check_freq = check_freq->valueint; /**< 设置检查频率值 */
    }
​
    return 0;                           /**< 成功返回 */
}
​
/**
 * @brief 释放boot节点配置内存
 * @param boot boot配置结构体指针
 * @return int 总是返回0
 */
static int release_boot_cfg(js_bootcfg_t *boot)
{
    if (boot->shell == NULL)            /**< 内存已释放或未分配 */
        return 0;                       /**< 直接返回 */
    free(boot->shell);                  /**< 释放shell数组内存 */
    boot->shell = NULL;                 /**< 指针置空 */
    boot->shell_len = 0;                /**< 长度清零 */
    return 0;                           /**< 成功返回 */
}
​
/**
 * @brief 解析进程节点配置(包括main和factory)
 * @param process 进程配置数组指针的指针(二级指针,用于修改指针值)
 * @param proc_len 进程数量的指针
 * @return int 成功返回0,失败返回-1
 * @note 设计模式:工厂方法,创建进程配置对象数组
 *       数据流结构:分配连续内存存储所有进程配置
 */
static int get_process_cfg(js_process_t **process, int *proc_len)
{
    /** 计算总进程数 */
    int main_len = cJSON_GetArraySize(main_root); /**< main进程数组大小 */
    int fact_len = cJSON_GetArraySize(fact_root); /**< factory进程数组大小 */
    *proc_len = main_len + fact_len;    /**< 总进程数 */
    if (*proc_len == 0)                 /**< 没有配置任何进程 */
        return 0;                       /**< 直接成功返回(空配置) */
​
    /** 分配进程配置结构体数组内存 */
    js_process_t *proc = (js_process_t *)malloc(*proc_len * sizeof(js_process_t)); /**< 动态分配内存 */
    if (proc == NULL)                   /**< 内存分配失败 */
    {
        *proc_len = 0;                  /**< 长度清零 */
        *process = NULL;                /**< 指针置空 */
        printf("malloc process buffer error !!\n"); /**< 打印错误信息 */
        return -1;                      /**< 返回错误 */
    }
    
    /** 分别解析main和factory进程 */
    if (main_len > 0)                   /**< 存在main进程配置 */
    {
        main_len = get_process_nodes(main_root, proc, main_len); /**< 解析main进程,返回实际有效数 */
    }
    if (fact_len > 0)                   /**< 存在factory进程配置 */
    {
        fact_len = get_process_nodes(fact_root, &proc[main_len], fact_len); /**< 解析factory进程,从数组中间开始 */
    }
​
    *process = proc;                    /**< 设置输出指针 */
    *proc_len = main_len + fact_len;    /**< 更新实际总进程数 */
​
    /** 调试信息(已注释) */
    // int index = 0;
    // for (index = 0; index < *proc_len; index++)
    // {
    //     printf("path = %s \n",   (*process)[index].path );
    //     printf("name = %s \n",   (*process)[index].name );
    //     printf("arg  = %s \n",   (*process)[index].arg  );
    //     printf("wait = %d \n",   (*process)[index].wait );
    //     printf("type = 0x%X \n", (*process)[index].type );
    // }
    return 0;                           /**< 成功返回 */
}
​
/**
 * @brief 释放进程节点配置内存
 * @param process 进程配置数组指针的指针
 * @return int 总是返回0
 */
static int release_process_cfg(js_process_t **process)
{
    free(*process);                     /**< 释放进程数组内存 */
    *process = NULL;                    /**< 指针置空 */
    return 0;                           /**< 成功返回 */
}
​
/**
 * @brief 解析备份节点配置
 * @param backup 备份配置数组指针的指针
 * @param bak_len 备份数量的指针
 * @return int 成功返回0,失败返回-1
 */
static int get_backup_cfg(js_backup_t **backup, int *bak_len)
{
    if (back_root == NULL)              /**< backup节点不存在 */
        return -1;                      /**< 返回错误 */
​
    *bak_len = cJSON_GetArraySize(back_root); /**< 获取备份数组大小 */
    if (*bak_len == 0)                 /**< 没有备份配置 */
        return 0;                       /**< 直接成功返回(空配置) */
​
    /** 分配备份配置结构体数组内存 */
    *backup = (js_backup_t *)malloc(*bak_len * sizeof(js_backup_t)); /**< 动态分配内存 */
    memset(*backup, 0, *bak_len * sizeof(js_backup_t)); /**< 清零初始化,数据流:安全初始化 */
    if (*backup == NULL)                /**< 内存分配失败 */
    {
        printf("malloc backup buffer error !!\n"); /**< 打印错误信息 */
        return -1;                      /**< 返回错误 */
    }
​
    /** 解析备份节点数组 */
    *bak_len = get_backup_nodes(back_root, *backup, *bak_len); /**< 解析实际有效项数 */
​
    return 0;                           /**< 成功返回 */
}
​
/**
 * @brief 释放备份节点配置内存
 * @param bakup 备份配置数组指针的指针
 * @return int 总是返回0
 */
static int release_backup_cfg(js_backup_t **bakup)
{
    if (*bakup == NULL)                 /**< 内存已释放或未分配 */
        return 0;                       /**< 直接返回 */
    free(*bakup);                       /**< 释放备份数组内存 */
    *bakup = NULL;                      /**< 指针置空 */
    return 0;                           /**< 成功返回 */
}
​
/**
 * @brief 解析阈值配置节点
 * @param node 阈值JSON节点指针
 * @param threshold 阈值配置结构体指针
 * @return int 总是返回0
 * @note 设计模式:数据转换器,将JSON数据转换为C结构体
 */
static int get_threshold_node(cJSON *node, js_threshold_t *threshold)
{
    if (node == NULL)                   /**< 阈值节点不存在 */
    {
        memset(threshold, 0, sizeof(js_threshold_t)); /**< 清零初始化阈值配置 */
        return 0;                       /**< 返回成功(使用默认值) */
    }
    /** 从JSON节点读取各字段值 */
    threshold->cpu = cJSON_GetObjectItem(node, "cpu")->valueint; /**< CPU阈值(百分比) */
    threshold->heap = cJSON_GetObjectItem(node, "heap")->valueint; /**< 堆内存阈值(KB) */
    threshold->handler = cJSON_GetObjectItem(node, "handler")->valueint; /**< 处理方式 */
    threshold->where = (int)strtol(cJSON_GetObjectItem(node, "where")->valuestring, NULL, 16); /**< 监控位置(十六进制字符串转整数) */
    
    /** 调试信息(已注释) */
    // printf("threshold->cpu     = %d \n",   threshold->cpu    );
    // printf("threshold->heap    = %d \n",   threshold->heap   );
    // printf("threshold->handler = %d \n",   threshold->handler);
    // printf("threshold->where   = %d \n",   threshold->where  );
    return 0;                           /**< 成功返回 */
}
​
/**
 * @brief 解析进程数组节点
 * @param array 进程数组JSON节点指针
 * @param process 进程配置结构体数组指针
 * @param len 预期解析的进程数量
 * @return int 实际解析的有效进程数量
 * @note 设计模式:迭代器模式,遍历JSON数组
 *       性能分析:对每个进程进行文件可执行性检查,可能涉及磁盘I/O
 */
static int get_process_nodes(cJSON *array, js_process_t *process, int len)
{
    int index = 0;                      /**< 实际有效进程计数器 */
    int i;                              /**< 循环索引 */
    for (i = 0; i < len; i++)           /**< 遍历JSON数组 */
    {
        cJSON *node = cJSON_GetArrayItem(array, i); /**< 获取第i个数组元素 */
        /** 读取基本字段(注意:valuestring指向JSON字符串,不复制) */
        process[index].path = cJSON_GetObjectItem(node, "path")->valuestring; /**< 路径字符串 */
        process[index].name = cJSON_GetObjectItem(node, "name")->valuestring; /**< 程序名字符串 */
        
        /** 检查可执行文件是否存在且可执行 */
        char path[128] = {0};           /**< 完整路径缓冲区,数据流:固定大小128字节 */
        sprintf(path, "%s/%s", process[index].path, process[index].name); /**< 拼接完整路径 */
        if (access(path, X_OK) < 0)     /**< 检查文件可执行权限 */
            continue;                   /**< 跳过不可执行的文件 */
        
        /** 读取其他字段 */
        process[index].wait = cJSON_GetObjectItem(node, "wait")->valueint; /**< 等待时间 */
        process[index].type = (int)strtol(cJSON_GetObjectItem(node, "type")->valuestring, NULL, 16); /**< 进程类型(十六进制字符串转整数) */
​
        /** 解析参数数组 */
        cJSON *arg = cJSON_GetObjectItem(node, "arg"); /**< 获取arg节点 */
        if (arg->type == cJSON_Array)   /**< arg是数组类型 */
        {
            process[index].arg_len = cJSON_GetArraySize(arg); /**< 获取数组大小 */
            int arg_index;              /**< 参数数组索引 */
            for (arg_index = 0; arg_index < process[index].arg_len; arg_index++) /**< 遍历参数数组 */
                process[index].arg[arg_index] = cJSON_GetArrayItem(arg, arg_index)->valuestring; /**< 获取参数值 */
        }
        else                            /**< arg是单个字符串 */
        {
            process[index].arg_len = 1; /**< 参数长度为1 */
            process[index].arg[0] = cJSON_GetObjectItem(node, "arg")->valuestring; /**< 获取参数字符串 */
        }
        
        /** 解析阈值配置 */
        get_threshold_node(cJSON_GetObjectItem(node, "threshold"), &process[index].threshold); /**< 解析阈值节点 */
​
        /** 调试信息(已注释) */
        // printf("path = %s \n",   process[index].path );
        // printf("name = %s \n",   process[index].name );
        // int arg_index;
        // for (arg_index = 0; arg_index < process[index].arg_len; arg_index++)
        //     printf("arg[%d]  = %s \n", arg_index, process[index].arg[arg_index]);
        // printf("wait = %d \n",   process[index].wait );
        // printf("type = 0x%X \n", process[index].type );
        index++;                        /**< 增加有效进程计数 */
    }
    return index;                       /**< 返回实际解析的有效进程数 */
}
​
/**
 * @brief 解析shell命令数组节点
 * @param array shell数组JSON节点指针
 * @param shell shell配置结构体数组指针
 * @param len 预期解析的shell命令数量
 * @return int 实际解析的有效shell命令数量
 */
static int get_shell_nodes(cJSON *array,  js_shell_t *shell, int len)
{
    int index = 0;                      /**< 实际有效shell命令计数器 */
    int i;                              /**< 循环索引 */
    for (i = 0; i < len; i++)           /**< 遍历JSON数组 */
    {
        cJSON *node = cJSON_GetArrayItem(array, i); /**< 获取第i个数组元素 */
        shell[index].valid = cJSON_GetObjectItem(node, "valid")->valueint; /**< 有效性标志 */
        if (shell[index].valid == false) /**< 无效的shell命令 */
            continue;                   /**< 跳过 */
        /** 根据命令类型字符串'S'或'K'设置类型常量 */
        shell[index].type = cJSON_GetObjectItem(node, "type")->valuestring[0] == 'S'? TYPE_SCRIPT_S:TYPE_SCRIPT_K;
        shell[index].cmd = cJSON_GetObjectItem(node, "command")->valuestring; /**< 命令字符串 */
        index++;                        /**< 增加有效命令计数 */
        // printf("[%c-%X] %s \n", shell[index].valid?'V':'X', shell[index].type, shell[index].cmd);
    }
    return index;                       /**< 返回实际解析的有效shell命令数 */
}
​
/**
 * @brief 解析备份数组节点
 * @param array 备份数组JSON节点指针
 * @param bakup 备份配置结构体数组指针
 * @param len 预期解析的备份项数量
 * @return int 实际解析的有效备份项数量
 */
static int get_backup_nodes(cJSON *array,  js_backup_t *bakup, int len)
{
    int index = 0;                      /**< 实际有效备份项计数器 */
    int i;                              /**< 循环索引 */
    for (i = 0; i < len; i++)           /**< 遍历JSON数组 */
    {
        cJSON *node = cJSON_GetArrayItem(array, i); /**< 获取第i个数组元素 */
        bakup[index].path = cJSON_GetObjectItem(node, "path")->valuestring; /**< 源文件路径 */
        bakup[index].file = cJSON_GetObjectItem(node, "file")->valuestring; /**< 源文件名 */
        bakup[index].backup_path = cJSON_GetObjectItem(node, "backup-path")->valuestring; /**< 备份路径 */
        index++;                        /**< 增加有效备份项计数 */
    }
    return index;                       /**< 返回实际解析的有效备份项数 */
}
​
/**
 * @brief 性能测试主函数(条件编译,实际未使用)
 * @note 设计模式:性能测试桩,用于评估配置文件解析性能
 *       性能分析:测试结果显示解析耗时约3.76ms(平均值)
 */
#if 0                                   /**< 条件编译:性能测试代码,实际未使用 */
#include <sys/time.h>                   /**< 时间头文件,用于性能测试 */
#define TEST_CNT 10000                  /**< 测试循环次数 */
int main()
{
    int cnt0 = TEST_CNT;                /**< 测试计数器1 */
    int cnt1 = TEST_CNT;                /**< 测试计数器2(用于计算平均值) */
​
    struct timeval start, end;          /**< 时间戳结构体 */
    gettimeofday(&start, NULL);         /**< 记录开始时间 */
​
    /** 性能测试循环 */
    while(cnt0--)                       /**< 重复测试TEST_CNT次 */
    {
        demofcg_t cfg;                  /**< 配置结构体 */
        memset(&cfg, 0, sizeof(demofcg_t)); /**< 清零初始化 */
        get_demo_config(&cfg);          /**< 解析配置文件 */
        release_demo_config(&cfg);      /**< 释放配置内存 */
    }
​
    gettimeofday(&end, NULL);           /**< 记录结束时间 */
​
    /** 计算并打印平均耗时 */
    printf("useTime = %.2lf ms\n", 
           ((end.tv_sec - start.tv_sec)*1000.0+(end.tv_usec - start.tv_usec)/1000.0)/cnt1); /**< 计算平均毫秒数 */
​
    return 0;                           /**< 测试完成 */
}
#endif                                  /**< 结束条件编译块 */
​

3. checkapi.c/h - 校验算法

设计思想: 算法插件化
├── 为什么条件编译?
│   ├── MD5 vs CRC32 选择
│   ├── 性能vs安全性权衡
│   └── 编译时决策,运行时无开销
├── 内联算法实现
│   ├── 避免外部依赖
│   ├── 控制代码大小
│   └── 性能优化(查表法)
└── 文件流处理模式
    ├── 固定缓冲区(4KB)
    ├── 流式读取/计算
    └── 内存效率高
/**
 * @file checkapi.c
 * @brief 文件校验API实现文件,提供CRC32和MD5文件校验功能
 */
​
#include <stdio.h>                      /**< 标准输入输出头文件,提供fprintf、fread等函数 */
#include <stdlib.h>                     /**< 标准库头文件,提供exit等函数 */
#include <string.h>                     /**< 字符串处理头文件,提供memcpy、memset等函数 */
​
#include "checkapi.h"                   /**< 校验API头文件,包含函数声明 */
​
/** 类型定义别名,提高代码可读性 */
typedef unsigned int u32;               /**< 32位无符号整数类型别名 */
typedef unsigned char       uint8_t;   /**< 8位无符号整数类型别名 */
typedef unsigned short int  uint16_t;  /**< 16位无符号整数类型别名 */
typedef unsigned int        uint32_t;  /**< 32位无符号整数类型别名 */
​
/**
 * @defgroup MD5_IMPLEMENTATION MD5实现
 * @brief MD5哈希算法完整实现(条件编译)
 * @note 设计模式:模板方法模式,将算法分解为初始化、更新、结束步骤
 * @{
 */
#ifdef USE_MD5                            /**< 条件编译:如果定义了USE_MD5宏 */
/**
 * @def 循环左移宏
 * @param x 要移位的32位值
 * @param n 移位位数
 * @return 循环左移结果
 */
#define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) ) /**< 32位循环左移宏定义 */
​
/**
 * @brief MD5上下文结构体
 * @note 设计模式:状态保持器,维护MD5计算中间状态
 *       数据流结构:92字节(4*4 + 4 + 64 + 4 = 92)
 */
typedef struct {
    u32 A,B,C,D;      /**< 链式变量(chaining variables),MD5的四个状态寄存器 */
    u32  nblocks;      /**< 已处理的完整块数(每个块64字节) */
    unsigned char buf[64]; /**< 缓冲区,用于存储不足64字节的数据 */
    int  count;        /**< 缓冲区中当前字节数 */
} MD5_CONTEXT;
​
/**
 * @brief 初始化MD5上下文
 * @param ctx MD5上下文结构体指针
 * @note MD5初始向量(IV)定义在RFC 1321中
 */
static void md5_init( MD5_CONTEXT *ctx )
{
    ctx->A = 0x67452301; /**< 初始化A寄存器 */
    ctx->B = 0xefcdab89; /**< 初始化B寄存器 */
    ctx->C = 0x98badcfe; /**< 初始化C寄存器 */
    ctx->D = 0x10325476; /**< 初始化D寄存器 */
    ctx->nblocks = 0;    /**< 已处理块数清零 */
    ctx->count = 0;      /**< 缓冲区计数器清零 */
}
​
/** MD5逻辑函数定义(来自RFC 1321) */
#define FF(b, c, d) (d ^ (b & (c ^ d))) /**< 第一轮逻辑函数F */
#define FG(b, c, d) FF (d, b, c)       /**< 第二轮逻辑函数G(实际上是F的重排) */
#define FH(b, c, d) (b ^ c ^ d)        /**< 第三轮逻辑函数H */
#define FI(b, c, d) (c ^ (b | ~d))     /**< 第四轮逻辑函数I */
​
/**
 * @brief MD5核心变换函数
 * @param ctx MD5上下文结构体指针
 * @param data 64字节数据块指针
 * @note 实现MD5算法的64轮变换,每轮16次操作
 */
static void transform( MD5_CONTEXT *ctx, unsigned char *data )
{
    u32 correct_words[16];             /**< 16个32位字的工作数组 */
    u32 A = ctx->A;                    /**< 加载A寄存器到局部变量 */
    u32 B = ctx->B;                    /**< 加载B寄存器到局部变量 */
    u32 C = ctx->C;                    /**< 加载C寄存器到局部变量 */
    u32 D = ctx->D;                    /**< 加载D寄存器到局部变量 */
    u32 *cwp = correct_words;          /**< 工作数组指针 */
​
    memcpy( correct_words, data, 64 ); /**< 将64字节数据复制到32位字数组 */
​
/** 
 * @def 第一轮操作宏(简化版)
 * @note 用于MD5第一轮(共16次操作)的宏定义
 */
#define OP(a, b, c, d, s, T)                        \
  do                                    \
    {                                   \
      a += FF (b, c, d) + (*cwp++) + T;         /**< a += F(b,c,d) + M[i] + T[i] */ \
      a = rol(a, s);                            /**< 循环左移s位 */ \
      a += b;                               /**< a += b */ \
    }                                   \
  while (0)
 
 
    /* Before we start, one word about the strange constants.
       They are defined in RFC 1321 as
       T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
       这些常量在RFC 1321中定义为 T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
     */
​
    /* Round 1. 第一轮(16次操作) */
    OP (A, B, C, D,  7, 0xd76aa478); /**< 操作1 */
    OP (D, A, B, C, 12, 0xe8c7b756); /**< 操作2 */
    OP (C, D, A, B, 17, 0x242070db); /**< 操作3 */
    OP (B, C, D, A, 22, 0xc1bdceee); /**< 操作4 */
    OP (A, B, C, D,  7, 0xf57c0faf); /**< 操作5 */
    OP (D, A, B, C, 12, 0x4787c62a); /**< 操作6 */
    OP (C, D, A, B, 17, 0xa8304613); /**< 操作7 */
    OP (B, C, D, A, 22, 0xfd469501); /**< 操作8 */
    OP (A, B, C, D,  7, 0x698098d8); /**< 操作9 */
    OP (D, A, B, C, 12, 0x8b44f7af); /**< 操作10 */
    OP (C, D, A, B, 17, 0xffff5bb1); /**< 操作11 */
    OP (B, C, D, A, 22, 0x895cd7be); /**< 操作12 */
    OP (A, B, C, D,  7, 0x6b901122); /**< 操作13 */
    OP (D, A, B, C, 12, 0xfd987193); /**< 操作14 */
    OP (C, D, A, B, 17, 0xa679438e); /**< 操作15 */
    OP (B, C, D, A, 22, 0x49b40821); /**< 操作16 */
 
 
#undef OP /**< 取消第一轮操作宏定义 */
​
/** 
 * @def 通用操作宏(用于第2-4轮)
 * @note 支持不同逻辑函数的通用操作宏
 */
#define OP(f, a, b, c, d, k, s, T)  \
    do                                    \
      {                                   \
    a += f (b, c, d) + correct_words[k] + T;              /**< a += f(b,c,d) + M[k] + T[i] */ \
    a = rol(a, s);                            /**< 循环左移s位 */ \
    a += b;                               /**< a += b */ \
      }                                   \
    while (0)
 
 
    /* Round 2. 第二轮(16次操作) */
    OP (FG, A, B, C, D,  1,  5, 0xf61e2562); /**< 操作17 */
    OP (FG, D, A, B, C,  6,  9, 0xc040b340); /**< 操作18 */
    OP (FG, C, D, A, B, 11, 14, 0x265e5a51); /**< 操作19 */
    OP (FG, B, C, D, A,  0, 20, 0xe9b6c7aa); /**< 操作20 */
    OP (FG, A, B, C, D,  5,  5, 0xd62f105d); /**< 操作21 */
    OP (FG, D, A, B, C, 10,  9, 0x02441453); /**< 操作22 */
    OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); /**< 操作23 */
    OP (FG, B, C, D, A,  4, 20, 0xe7d3fbc8); /**< 操作24 */
    OP (FG, A, B, C, D,  9,  5, 0x21e1cde6); /**< 操作25 */
    OP (FG, D, A, B, C, 14,  9, 0xc33707d6); /**< 操作26 */
    OP (FG, C, D, A, B,  3, 14, 0xf4d50d87); /**< 操作27 */
    OP (FG, B, C, D, A,  8, 20, 0x455a14ed); /**< 操作28 */
    OP (FG, A, B, C, D, 13,  5, 0xa9e3e905); /**< 操作29 */
    OP (FG, D, A, B, C,  2,  9, 0xfcefa3f8); /**< 操作30 */
    OP (FG, C, D, A, B,  7, 14, 0x676f02d9); /**< 操作31 */
    OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); /**< 操作32 */
 
 
    /* Round 3. 第三轮(16次操作) */
    OP (FH, A, B, C, D,  5,  4, 0xfffa3942); /**< 操作33 */
    OP (FH, D, A, B, C,  8, 11, 0x8771f681); /**< 操作34 */
    OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); /**< 操作35 */
    OP (FH, B, C, D, A, 14, 23, 0xfde5380c); /**< 操作36 */
    OP (FH, A, B, C, D,  1,  4, 0xa4beea44); /**< 操作37 */
    OP (FH, D, A, B, C,  4, 11, 0x4bdecfa9); /**< 操作38 */
    OP (FH, C, D, A, B,  7, 16, 0xf6bb4b60); /**< 操作39 */
    OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); /**< 操作40 */
    OP (FH, A, B, C, D, 13,  4, 0x289b7ec6); /**< 操作41 */
    OP (FH, D, A, B, C,  0, 11, 0xeaa127fa); /**< 操作42 */
    OP (FH, C, D, A, B,  3, 16, 0xd4ef3085); /**< 操作43 */
    OP (FH, B, C, D, A,  6, 23, 0x04881d05); /**< 操作44 */
    OP (FH, A, B, C, D,  9,  4, 0xd9d4d039); /**< 操作45 */
    OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); /**< 操作46 */
    OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); /**< 操作47 */
    OP (FH, B, C, D, A,  2, 23, 0xc4ac5665); /**< 操作48 */
 
 
    /* Round 4. 第四轮(16次操作) */
    OP (FI, A, B, C, D,  0,  6, 0xf4292244); /**< 操作49 */
    OP (FI, D, A, B, C,  7, 10, 0x432aff97); /**< 操作50 */
    OP (FI, C, D, A, B, 14, 15, 0xab9423a7); /**< 操作51 */
    OP (FI, B, C, D, A,  5, 21, 0xfc93a039); /**< 操作52 */
    OP (FI, A, B, C, D, 12,  6, 0x655b59c3); /**< 操作53 */
    OP (FI, D, A, B, C,  3, 10, 0x8f0ccc92); /**< 操作54 */
    OP (FI, C, D, A, B, 10, 15, 0xffeff47d); /**< 操作55 */
    OP (FI, B, C, D, A,  1, 21, 0x85845dd1); /**< 操作56 */
    OP (FI, A, B, C, D,  8,  6, 0x6fa87e4f); /**< 操作57 */
    OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); /**< 操作58 */
    OP (FI, C, D, A, B,  6, 15, 0xa3014314); /**< 操作59 */
    OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); /**< 操作60 */
    OP (FI, A, B, C, D,  4,  6, 0xf7537e82); /**< 操作61 */
    OP (FI, D, A, B, C, 11, 10, 0xbd3af235); /**< 操作62 */
    OP (FI, C, D, A, B,  2, 15, 0x2ad7d2bb); /**< 操作63 */
    OP (FI, B, C, D, A,  9, 21, 0xeb86d391); /**< 操作64 */
 
 
    /* Put checksum in context given as argument. 将校验和存入上下文 */
    ctx->A += A; /**< 更新上下文A寄存器 */
    ctx->B += B; /**< 更新上下文B寄存器 */
    ctx->C += C; /**< 更新上下文C寄存器 */
    ctx->D += D; /**< 更新上下文D寄存器 */
}
​
/**
 * @brief MD5数据更新函数
 * @param hd MD5上下文指针
 * @param inbuf 输入数据缓冲区
 * @param inlen 输入数据长度
 * @note 处理任意长度的数据,以64字节为单位进行变换
 */
static void md5_write( MD5_CONTEXT *hd, unsigned char *inbuf, size_t inlen)
{
    /** 如果缓冲区已满(64字节),则执行变换 */
    if( hd->count == 64 ) { /* flush the buffer 刷新缓冲区 */
    transform( hd, hd->buf ); /**< 对缓冲区数据执行MD5变换 */
    hd->count = 0;            /**< 重置缓冲区计数器 */
    hd->nblocks++;            /**< 增加已处理块数 */
    }
    if( !inbuf )             /**< 输入缓冲区为空(用于刷新) */
    return;
    
    /** 如果缓冲区有部分数据,先填满缓冲区 */
    if( hd->count ) {
    for( ; inlen && hd->count < 64; inlen-- )
        hd->buf[hd->count++] = *inbuf++; /**< 复制数据到缓冲区 */
    md5_write( hd, NULL, 0 );            /**< 递归调用以处理填满的缓冲区 */
    if( !inlen )                        /**< 所有数据已处理 */
        return;
    }
​
    /** 处理完整的64字节块 */
    while( inlen >= 64 ) {
    transform( hd, inbuf );              /**< 直接对输入数据执行变换 */
    hd->count = 0;                      /**< 重置缓冲区计数器 */
    hd->nblocks++;                      /**< 增加已处理块数 */
    inlen -= 64;                        /**< 减少剩余数据长度 */
    inbuf += 64;                        /**< 移动输入缓冲区指针 */
    }
    
    /** 处理剩余的不完整块 */
    for( ; inlen && hd->count < 64; inlen-- )
    hd->buf[hd->count++] = *inbuf++;     /**< 复制剩余数据到缓冲区 */
}
​
/**
 * @brief MD5最终处理函数
 * @param hd MD5上下文指针
 * @note 添加填充位和长度信息,生成最终的128位MD5值
 */
static void md5_final( MD5_CONTEXT *hd )
{
    u32 t, msb, lsb;         /**< 临时变量用于位操作 */
    unsigned char *p;        /**< 缓冲区指针 */
​
    md5_write(hd, NULL, 0); /* flush 刷新缓冲区 */;
​
    /** 计算原始消息的总位数(乘以8) */
    t = hd->nblocks;        /**< 获取已处理的完整块数 */
    lsb = t << 6;           /**< 乘以64转换为字节数(左移6位) */
    msb = t >> 26;          /**< 高位部分(用于处理溢出) */
    t = lsb;                /**< 保存低位 */
    if( (lsb += hd->count) < t ) /**< 加上缓冲区字节数,检查是否溢出 */
    msb++;                  /**< 溢出则高位加1 */
    t = lsb;                /**< 保存新的低位 */
    lsb <<= 3;              /**< 乘以8转换为位数(左移3位) */
    msb <<= 3;              /**< 高位也乘以8 */
    msb |= t >> 29;         /**< 将低位的溢出位合并到高位 */
 
    /** 添加填充位(根据RFC 1321规范) */
    if( hd->count < 56 ) { /* enough room 有足够空间 */
    hd->buf[hd->count++] = 0x80; /* pad 填充0x80 */
    while( hd->count < 56 )
        hd->buf[hd->count++] = 0;  /* pad 填充0 */
    }
    else { /* need one extra block 需要额外一个块 */
    hd->buf[hd->count++] = 0x80; /* pad character 填充字符0x80 */
    while( hd->count < 64 )
        hd->buf[hd->count++] = 0; /**< 填充0直到块满 */
    md5_write(hd, NULL, 0);  /* flush 刷新 */;
    memset(hd->buf, 0, 56 ); /* fill next block with zeroes 用零填充下一个块 */
    }
    
    /** 附加64位消息长度(小端序) */
    hd->buf[56] = lsb      ;        /**< 长度低位字节0 */
    hd->buf[57] = lsb >>  8;        /**< 长度低位字节1 */
    hd->buf[58] = lsb >> 16;        /**< 长度低位字节2 */
    hd->buf[59] = lsb >> 24;        /**< 长度低位字节3 */
    hd->buf[60] = msb      ;        /**< 长度高位字节0 */
    hd->buf[61] = msb >>  8;        /**< 长度高位字节1 */
    hd->buf[62] = msb >> 16;        /**< 长度高位字节2 */
    hd->buf[63] = msb >> 24;        /**< 长度高位字节3 */
    transform( hd, hd->buf );       /**< 对填充后的块执行最终变换 */
 
    /** 将结果从寄存器复制到缓冲区(小端序) */
    p = hd->buf;
#ifdef BIG_ENDIAN_HOST /**< 大端系统处理 */
#define X(a) do { *p++ = hd-> a      ; *p++ = hd-> a >> 8;      \
                  *p++ = hd-> a >> 16; *p++ = hd-> a >> 24; } while(0)
#else /* little endian 小端系统 */
#define X(a) do { *(u32*)p = hd-> a ; p += 4; } while(0) /**< 直接按32位复制 */
#endif
    X(A); /**< 复制A寄存器 */
    X(B); /**< 复制B寄存器 */
    X(C); /**< 复制C寄存器 */
    X(D); /**< 复制D寄存器 */
#undef X /**< 取消宏定义 */
}
#endif /**< 结束条件编译块 */
/** @} */ /* MD5_IMPLEMENTATION */
​
/**
 * @brief CRC32查找表(预计算好的256个值)
 * @note 数据流结构:256*4=1024字节,使用标准CRC32多项式0xEDB88320
 */
static uint32_t crc32Table[256] ={
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 
    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 
    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 
    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 
    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 
    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 
    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 
    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 
    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 
    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 
    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 
    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 
    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 
    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 
    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 
    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 
    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 
    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 
    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 
    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 
    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 
    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
​
/**
 * @defgroup PERFORMANCE_TEST 性能测试相关
 * @brief 用于性能测试的代码(条件编译)
 * @{
 */
#ifdef ENABLE_CHECK_TEST /**< 条件编译:性能测试使能 */
#include <sys/time.h>   /**< 时间头文件,用于性能测试 */
/**
 * @brief 计算函数接口所耗费的时间(单位:ms)
 * @param tag 时间戳标签
 * @note 与sysdemo.h中的函数类似,但此处用于性能测试
 */
static inline void cal_timestamp_ms(char *tag)
{
    static struct timeval start = {0}; /**< 静态变量保存开始时间 */
    static struct timeval end = {0};   /**< 静态变量保存结束时间 */
    gettimeofday(&end, NULL);          /**< 获取当前时间 */
    if (start.tv_sec != 0 && start.tv_usec != 0) /**< 不是第一次调用 */
    {
        unsigned long useTime = (end.tv_sec-start.tv_sec)*1000+(end.tv_usec-start.tv_usec)/1000; /**< 计算时间差(毫秒) */
        printf("%s use time: %lu ms\n", tag, useTime); /**< 打印耗时 */
    }
    start.tv_sec = end.tv_sec; start.tv_usec = end.tv_usec; /**< 更新开始时间 */
}
#endif
/** @} */ /* PERFORMANCE_TEST */
​
/**
 * @brief 对文件进行CRC32校验
 * @param filename 需要校验的文件名
 * @param crcval 返回CRC32校验值
 * @return int 返回0表示成功,非0表示失败
 * @note 设计模式:流处理器,逐块读取文件并计算CRC32
 *       性能分析:使用4KB缓冲区,平衡内存使用和I/O效率
 *       参考:http://www.ip33.com/crc.html CRC-32
 */
int crc32(char *filename, unsigned int *crcval)
{
    FILE *fp;                         /**< 文件指针 */
    char buffer[4096];                /**< 4KB读取缓冲区,数据流:固定大小,平衡性能和内存 */
    size_t n;                         /**< 实际读取的字节数 */
​
    /** 打开文件 */
    fp = fopen (filename, "rb");      /**< 以二进制只读模式打开文件 */
    if (!fp)                          /**< 文件打开失败 */
    {
        fprintf (stderr, "can't open %s\n", filename); /**< 打印错误信息到标准错误 */
        return -1;                    /**< 返回错误 */
    }
    
    /** 性能测试(条件编译) */
    #ifdef ENABLE_CHECK_TEST
    int j;                            /**< 循环计数器 */
    cal_timestamp_ms(NULL);           /**< 记录开始时间 */
    for (j = 0; j < 1000; j++)        /**< 重复测试1000次 */
    {
    #endif
    
    /** CRC32计算(初始值为0xffffffff) */
    uint32_t crc = 0xffffffff;        /**< CRC32初始值 */
    while ((n = fread (buffer, 1, sizeof buffer, fp))) /**< 循环读取文件,直到文件结束 */
    {
        int i;                        /**< 字节索引 */
        for (i = 0; i < n; i++)       /**< 处理每个字节 */
        {
            /** CRC32查表算法:crc = (crc >> 8) ^ table[(crc ^ byte) & 0xff] */
            crc = (crc >> 8)^(crc32Table[(crc ^ buffer[i])&0xff]); /**< 查表计算CRC32 */
        }
    }
    crc = crc^0xffffffff;             /**< 最终异或值(取反) */
    *crcval = crc;                    /**< 将结果存入输出参数 */
    
    #ifdef ENABLE_CHECK_TEST
    fseek(fp, 0, SEEK_SET);           /**< 重置文件指针到开头,用于下一次循环 */
    }
    printf ("crc32: %08x  %s", *crcval, filename); /**< 打印CRC32结果和文件名 */
    cal_timestamp_ms(" ");            /**< 记录结束时间并打印耗时 */
    #endif
​
    fclose (fp);                      /**< 关闭文件 */
    return 0;                         /**< 成功返回 */
}
​
#ifdef USE_MD5 /**< 条件编译:MD5功能 */
/**
 * @brief 对文件进行MD5SUM校验
 * @param filename 需要校验的文件名
 * @param md5val 返回MD5SUM校验值(16字节数组)
 * @return int 返回0表示成功,非0表示失败
 * @note 设计模式:流处理器,逐块读取文件并计算MD5
 *       性能分析:使用4KB缓冲区,与CRC32相同的I/O模式
 */
int md5sum(char *filename, unsigned char md5val[16])
{
    FILE *fp;                         /**< 文件指针 */
    unsigned char buffer[4096];       /**< 4KB读取缓冲区 */
    size_t n;                         /**< 实际读取的字节数 */
    MD5_CONTEXT ctx;                  /**< MD5上下文结构体 */
​
    /** 打开文件 */
    fp = fopen (filename, "rb");      /**< 以二进制只读模式打开文件 */
    if (!fp)                          /**< 文件打开失败 */
    {
        fprintf (stderr, "can't open %s\n", filename); /**< 打印错误信息 */
        return -1;                    /**< 返回错误 */
    }
    
    /** 性能测试(条件编译) */
    #ifdef ENABLE_CHECK_TEST
    int i;                            /**< 循环计数器 */
    cal_timestamp_ms(NULL);           /**< 记录开始时间 */
    for (i = 0; i < 1000; i++)        /**< 重复测试1000次 */
    {
    #endif
    
    /** MD5计算 */
    md5_init (&ctx);                  /**< 初始化MD5上下文 */
    while ((n = fread(buffer, 1, sizeof(buffer), fp))) /**< 循环读取文件 */
    {
        md5_write (&ctx, buffer, n);  /**< 更新MD5计算 */
    }
    md5_final (&ctx);                 /**< 完成MD5计算 */
​
    #ifdef ENABLE_CHECK_TEST
    fseek(fp, 0, SEEK_SET);           /**< 重置文件指针到开头 */
    }
    printf ("md5sum: ");              /**< 打印MD5结果 */
    for (i=0; i < 16; i++)
        printf ("%02x", ctx.buf[i]);  /**< 以十六进制打印每个字节 */
    printf ("  ");                    /**< 打印分隔符 */
    cal_timestamp_ms(filename);       /**< 记录结束时间并打印耗时 */
    #endif
​
    memcpy(md5val, ctx.buf, 16);      /**< 将MD5结果复制到输出参数 */
    fclose (fp);                      /**< 关闭文件 */
    return 0;                         /**< 成功返回 */
}
#endif /**< 结束条件编译块 */
​
#ifdef ENABLE_CHECK_TEST /**< 条件编译:性能测试主函数 */
/**
 * @brief 性能测试主函数
 * @param argc 命令行参数个数
 * @param argv 命令行参数数组
 * @return int 总是返回0
 * @note 设计模式:测试桩,用于评估CRC32和MD5的性能差异
 *       性能分析结论:128KB文件,相同CPU占用率79%情况下,
 *       MD5耗时28.8秒,CRC32耗时29秒,两者性能相当
 */
int main (int argc, char **argv)
{
    if (argc < 2)                      /**< 参数检查 */
    {
        fprintf (stderr, "usage: ./checkapi filenames\n"); /**< 打印使用说明 */
        exit (1);                      /**< 参数错误退出 */
    }
    int tmpc = argc;                   /**< 参数计数器副本 */
    char **tmpv = argv;                /**< 参数数组副本 */
    
    /** 测试MD5(如果启用) */
    #ifdef USE_MD5
    for (tmpc--, tmpv++; tmpc; tmpv++, tmpc--) /**< 遍历所有文件名参数 */
    {
        char md5val[16];               /**< MD5结果缓冲区 */
        md5sum(*tmpv, md5val);         /**< 计算MD5 */
    }
    tmpc = argc;                       /**< 重置参数计数器 */
    tmpv = argv;                       /**< 重置参数数组指针 */
    #endif
    
    /** 测试CRC32 */
    for (tmpc--, tmpv++; tmpc; tmpv++, tmpc--) /**< 遍历所有文件名参数 */
    {
        int crc;                       /**< CRC32结果变量 */
        crc32(*tmpv, &crc);            /**< 计算CRC32 */
    }
    return 0;                          /**< 测试完成 */
}
#endif /**< 结束条件编译块 */
​

4. file_inotify.c + backup_db.h - 文件监控

设计思想: 事件驱动架构
├── 线程分离设计
│   ├── 主线程专注进程管理
│   ├── 备份线程专注文件监控
│   └── 通过全局变量通信
├── inotify + epoll 组合
│   ├── inotify: 文件变化通知
│   ├── epoll: 高效事件等待
│   └── 水平触发(LT)模式,简单可靠
└── 双级备份策略
    ├── 1级: 快速响应,保持最新
    ├── 2级: 可靠存储,完整性验证
    └── 压缩存储,节省空间
/**
 * @file file_inotify.c
 * @brief 文件监控与备份实现文件,使用inotify实时监控文件变化并进行备份
 */
​
#include <stdio.h>                      /**< 标准输入输出头文件,提供printf等函数 */
#include <stdlib.h>                     /**< 标准库头文件,提供free等函数 */
#include <string.h>                     /**< 字符串处理头文件,提供memset、snprintf等函数 */
​
#include <sys/inotify.h>                /**< inotify文件监控头文件,提供inotify相关结构体和函数 */
#include <sys/types.h>                  /**< 系统类型定义头文件,提供基本数据类型定义 */
#include <sys/epoll.h>                  /**< epoll I/O多路复用头文件,提供epoll相关函数 */
#include <pthread.h>                    /**< 线程头文件,提供pthread_create等线程函数 */
#include <sys/stat.h>                   /**< 文件状态头文件,提供stat结构体和函数 */
#include <sys/wait.h>                   /**< 进程等待头文件,提供wait相关函数 */
​
#include "db_api.h"                     /**< 数据库API头文件,提供数据库备份和完整性检查函数 */
#include "sysdemo.h"                    /**< 系统演示主头文件,包含系统函数和宏定义 */
#include "checkapi.h"                   /**< 检查API头文件,提供CRC32校验函数 */
#include "backup_db.h"                  /**< 数据库备份头文件,包含备份任务接口和常量 */
​
/**
 * @brief 文件监控结构体
 * @note 设计模式:状态保持器,维护每个被监控文件的状态信息
 *       数据流结构:大小约32字节(假设指针8字节,int 4字节,6个int=24字节,3个指针=24字节,实际更大)
 */
typedef struct 
{
    int id;          /**< inotify监测标识(watch descriptor),由inotify_add_watch返回 */
    int flag[2];     /**< 文件监测标志位数组:flag[0]=1级备份标志,flag[1]=2级备份标志 */
    int cnt[2];      /**< 文件备份次数记录数组:cnt[0]=1级备份次数,cnt[1]=2级备份次数 */
​
    char *path;      /**< 监测的文件所在目录路径 */
    char *file;      /**< 监测的文件名 */
    char *bakup_path;/**< 备份文件存储路径 */
    int err_cnt;     /**< 拷贝失败的错误次数计数器,低8位为1级错误,高8位为2级错误 */
}file_watch_t;
​
/**
 * @brief 文件监控管理器结构体
 * @note 设计模式:管理器模式,集中管理所有文件监控资源
 *       数据流结构:大小约24字节(假设指针8字节)
 */
typedef struct {
    int ntfd;         /**< inotify 文件描述符 */
    int epfd;         /**< epoll 文件描述符 */
    file_watch_t *wd; /**< 监测信息数组指针 */
    int wd_len;       /**< 监测的文件数量 */
}file_inotify_t;
​
/** 函数声明 */
void *run_database_backup_thread(void *arg); /**< 数据库备份线程函数 */
static int add_files_to_inotify(demofcg_t cfg, file_inotify_t *fitfy); /**< 初始化inotify监控 */
static int release_inotify(file_inotify_t *fitfy); /**< 释放inotify资源 */
static struct epoll_event *get_epoll_event(struct epoll_event events[MAX_EPOLL_EVENT_LEN], int event_num, int efd); /**< 获取epoll事件 */
​
static int compare_file(char *file, char *check_file, char val[16]); /**< 文件比较函数 */
static int gzip_db(char *file, char gzip_file[MAX_BUFFER_LEN]); /**< 数据库压缩函数 */
static void partition_error_exit(char *path); /**< 分区错误退出函数 */
​
static bool is_partition_ok(char *path); /**< 分区状态检查函数 */
​
/** 全局变量 */
static pthread_t bak_tid = 0;           /**< 备份线程ID,0表示线程未创建 */
static file_inotify_t fitfy;            /**< 文件监控管理器实例 */
​
/**
 * @brief 执行备份任务线程(主入口函数)
 * @param cfg 配置结构体,包含备份文件列表
 * @return int 成功返回0,失败返回-1
 * @note 设计模式:工厂方法,创建并启动备份监控线程
 *       性能分析:初始化inotify和epoll,创建独立线程进行备份监控
 */
int run_backup_task(demofcg_t cfg)
{
    /** 初始化inotify文件监控 */
    if (add_files_to_inotify(cfg, &fitfy) != 0) /**< 将配置中的文件添加到inotify监控 */
    {
        printf("init backup_file_task error !!\n"); /**< 初始化失败打印错误 */
        partition_error_exit("init-backup_file_task"); /**< 分区错误退出 */
    }
    printf("init backup_file_task ok !!\n"); /**< 初始化成功打印信息 */
​
    /** 创建备份线程 */
    if(pthread_create(&bak_tid, NULL, run_database_backup_thread, NULL)) /**< 创建数据库备份线程 */
    {
        printf("sysdemo creat backup_file_thread error !!\n"); /**< 线程创建失败打印错误 */
        partition_error_exit("start-backup_file_task"); /**< 分区错误退出 */
    }
    printf("sysdemo creat backup_file_thread ok !!\n"); /**< 线程创建成功打印信息 */
    return 0;                                          /**< 成功返回 */
}
​
/**
 * @brief 数据库文件轮询备份线程(核心业务逻辑)
 * @param arg 线程参数(未使用)
 * @return void* 线程返回值(总是NULL)
 * @note 备份流程说明:
 * (1)方式:双备份机制
 * (2)1级备份:主要是为了尽可能保持与源文件处于同步状态,(copy current->newbakup)
 * (3)2级备份:主要是为了保存相对于1级备份更为完整可靠的文件;
 *               先将1级保留下的文件newbakup进行完整性校验,如果完整性校验不通过,则删除1级所备份的文件;(check newbakup)
 *               再与2级当前的文件oldbakup进行md5/crc32校验,(compare newbakup ?= oldbakup)
 *               校验值相同则不拷贝文件,校验值不相同则将文件拷贝(copy newbakup->oldbakup)
 * (4)分区检测:当拷贝失败的次数达到一定值时,则检测分区:
 *               检测通过,则继续重新备份
 *               检测不通过,则退出系统
 * (5)备份结果:备份分区会存在 xxx 和 xxx.gz 文件;
 * (6)还原备份:先尝试还原1级保存下来的文件,因为实时性较优;
 *                1级还原失败,则还原2级保存下来的数据库,须先解压,再检验还原;
 *                2级也还原失败,则使用默认的数据库文件。
 *       设计模式:状态机模式,根据文件状态和错误计数决定备份策略
 *       性能分析:使用epoll非阻塞等待,减少CPU占用,双级备份平衡实时性和可靠性
 */
void *run_database_backup_thread(void *arg)
{
    struct epoll_event evt[MAX_EPOLL_EVENT_LEN]; /**< epoll事件数组,最大MAX_EPOLL_EVENT_LEN(10)个事件 */
    long oldbakup_time = get_sysuptime(); /**< 2级备份时间基准点,初始为当前系统运行时间 */
    int timeout_ms = BACKUP_INSPECTION_LEVEL_1*1000; /**< epoll等待超时时间,初始为1级检查间隔15秒 */
​
    /** 主监控循环 */
    while (1) /**< 无限循环,直到系统退出 */
    {
        int index; /**< 循环索引变量 */
        memset(evt, 0, sizeof(struct epoll_event)*MAX_EPOLL_EVENT_LEN); /**< 清零事件数组 */
        
        /** 等待epoll事件 */
        int ret = epoll_wait(fitfy.epfd, evt, MAX_EPOLL_EVENT_LEN, timeout_ms); /**< 等待文件变化事件 */
        if (ret < 0) /**< epoll错误 */
        {
            delay_s(BACKUP_INSPECTION_LEVEL_1); /**< 等待1级检查间隔时间 */
            continue; /**< 继续循环 */
        }
        
        /** 处理inotify文件变化事件 */
        if (ret > 0 && get_epoll_event(evt, ret, fitfy.ntfd) != NULL) /**< 有事件且是inotify文件描述符的事件 */
        {
            char recvbuf[MAX_INOTIFY_BUFFER_LEN] = {0}; /**< inotify事件接收缓冲区,大小1024字节 */
            int recvlen = read(fitfy.ntfd, recvbuf, MAX_INOTIFY_BUFFER_LEN); /**< 读取inotify事件 */
            if (recvlen <= 0) /**< 读取失败或没有数据 */
                continue; /**< 继续循环 */
                
            struct inotify_event *event = (struct inotify_event *)recvbuf; /**< 事件结构体指针 */
            int loop = 0; /**< 缓冲区偏移量 */
            
            /** 遍历所有事件 */
            while (loop < recvlen) /**< 循环处理缓冲区中的所有事件 */
            {
                for (index = 0; index < fitfy.wd_len; index++) /**< 遍历所有监控的文件 */
                {
                    if (event->wd != fitfy.wd[index].id) /**< 事件ID与监控ID不匹配 */
                        continue; /**< 跳过非目标文件 */
                        
                    /** 文件修改事件 */
                    if (event->mask & IN_MODIFY) /**< 文件被修改 */
                    {
                        fitfy.wd[index].flag[0] = 1; /**< 设置1级备份标志位,可以执行1级备份 */
                    }
                    /** 严重文件系统事件:源文件被删除、宿主目录被卸载、被移动 */
                    else if (event->mask & (IN_DELETE_SELF | IN_UNMOUNT | IN_MOVE_SELF)) /**< 文件被删除、卸载或移动 */
                    {
                        char result[MAX_BUFFER_LEN] = {0}; /**< 错误信息缓冲区 */
                        char *ptr = "delete"; /**< 默认错误类型为删除 */
                        if (event->mask & IN_UNMOUNT) /**< 目录卸载事件 */
                            ptr = "unmount"; /**< 错误类型改为卸载 */
                        if (event->mask & IN_MOVE_SELF) /**< 文件移动事件 */
                            ptr = "moved"; /**< 错误类型改为移动 */
                        snprintf(result, MAX_BUFFER_LEN, "%s/%s-%s", fitfy.wd[index].path, fitfy.wd[index].file, ptr); /**< 构造错误信息 */
                        partition_error_exit(result); /**< 分区错误退出系统 */
                    }
                }
                loop += sizeof(struct inotify_event) + event->len; /**< 移动到下一个事件位置 */
                event = (struct inotify_event *)(recvbuf + loop); /**< 更新事件指针 */
            }
            delay_s(BACKUP_WAIT_EVENT_FINISH);  /**< 事件发生后等待5秒,避免过于频繁的监听事件 */
            timeout_ms = BACKUP_WAIT_EVENT_FINISH * 1000; /**< 更新超时时间为等待时间 */
            continue; /**< 继续循环(跳过本次的备份检查) */
        }
        timeout_ms = BACKUP_INSPECTION_LEVEL_1 * 1000; /**< 重置超时时间为1级检查间隔 */
​
        /** 检查是否需要进行2级备份 */
        int oldbakup_flag=0; /**< 2级备份标志,0=不需要,1=需要 */
        long start = get_sysuptime(); /**< 获取当前系统运行时间 */
        if (start-oldbakup_time >= BACKUP_INSPECTION_LEVEL_2) /**< 距离上次2级备份超过60秒 */
        {
            oldbakup_time = start; /**< 更新2级备份时间基准点 */
            oldbakup_flag = 1; /**< 设置2级备份标志 */
        }
​
        /** 遍历所有监控文件进行备份处理 */
        for (index = 0; index < fitfy.wd_len; index++) /**< 遍历每个监控文件 */
        {
            char curfile[MAX_BUFFER_LEN] = {0}; /**< 当前源文件完整路径缓冲区 */
            char newbakup[MAX_BUFFER_LEN] = {0}; /**< 1级备份文件完整路径缓冲区 */
            snprintf(curfile, MAX_BUFFER_LEN, "%s/%s", fitfy.wd[index].path, fitfy.wd[index].file); /**< 构造源文件路径 */
            snprintf(newbakup, MAX_BUFFER_LEN, "%s/%s", fitfy.wd[index].bakup_path, fitfy.wd[index].file); /**< 构造1级备份文件路径 */
            
            /** 错误处理:1级备份连续失败达到阈值 */
            if ((fitfy.wd[index].err_cnt&0xFF) >= BACKUP_ERROR_CNT_LEVEL_1)  /**< 低8位错误计数>=5(1级错误) */
            {
                if (access(curfile, F_OK) != 0)    /**< 检查源文件是否存在 */
                    partition_error_exit(curfile); /**< 源文件丢失,分区错误退出 */
                if (is_partition_ok(fitfy.wd[index].path) == false)  /**< 检测数据分区是否正常 */
                    partition_error_exit(fitfy.wd[index].path); /**< 数据分区异常退出 */
                // if (is_partition_ok(fitfy.wd[index].bakup_path) == false) // 检测备份分区是否正常(注释掉)
                //     partition_error_exit(fitfy.wd[index].bakup_path);
                fitfy.wd[index].err_cnt &= 0x0000; /**< 清空错误计数器(通过分区检查后重置) */
            }
            // else if (((fitfy.wd[index].err_cnt>>8)&0xFF) >= BACKUP_ERROR_CNT_LEVEL_2) // 高8位错误计数>=5(2级错误,注释掉)
            // {
            //     if (is_partition_ok(fitfy.wd[index].bakup_path) == false) // 检测备份分区是否正常
            //         partition_error_exit(fitfy.wd[index].bakup_path);
            //     fitfy.wd[index].err_cnt &= 0x00FF; /**< 只清空高8位错误计数 */
            // }
            
            /** 检查源文件是否存在 */
            if (access(curfile, F_OK) != 0)    /**< 源文件丢失 */
            {
                fitfy.wd[index].err_cnt += 0x01; /**< 增加1级错误计数 */
                continue; /**< 跳过该文件的备份处理 */
            }
​
            /** 执行1级备份 */
            if (fitfy.wd[index].flag[0] == 1 || access(newbakup, F_OK) != 0) /**< 需要1级备份或1级备份文件不存在 */
            {
                remove(newbakup); /**< 删除旧的1级备份文件 */
                /** 执行1级备份,直接拷贝,不进行校验 */
                if (db_backup(curfile, newbakup) < 0)    /**< 1.1 直接拷贝 cur->new(调用数据库备份API) */
                {
                    fitfy.wd[index].err_cnt += 0x1;      /**< 1.2 拷贝失败则 err_cnt 低8位加1 */
                    fitfy.wd[index].flag[1] = 0; /**< 2级备份标志清零 */
                }
                else
                {
                    fitfy.wd[index].flag[0] = 0;         /**< 1.3 拷贝成功则清空1级备份标志位 */
                    fitfy.wd[index].flag[1] = 1; /**< 设置2级备份标志位(需要进一步校验) */
                    fitfy.wd[index].cnt[0]++; /**< 增加1级备份成功计数 */
                    fitfy.wd[index].err_cnt &= 0xFF00; /**< 清空低8位错误计数(保留高8位) */
                    sys_demo_msg_send(MNT_MODULE, SWDMNT_BACKUP_CONF_MSG, NULL, 0); /**< 发送备份完成消息 */
                }
            }
​
            /** 执行2级备份 */
            if (fitfy.wd[index].flag[1] == 1 && oldbakup_flag == 1) /**< 需要2级备份且达到2级备份时间 */
            {
                char oldbakup[MAX_BUFFER_LEN] = {0}; /**< 2级备份文件(压缩文件)路径缓冲区 */
                
                /** 执行2级备份,完整性校验,crc32校验,最后进行压缩 */
                if (db_integrity_check(newbakup) != 0)     /**< 2.1 校验new database文件的完整性 */
                {
                    fitfy.wd[index].flag[0] = 1; /**< 完整性校验失败,重新触发1级备份 */
                    continue; /**< 跳过2级备份 */
                }
​
                snprintf(oldbakup, MAX_BUFFER_LEN, "%s/%s", fitfy.wd[index].bakup_path, fitfy.wd[index].file); /**< 构造2级备份文件路径 */
                int gzret = gzip_db(newbakup, oldbakup);  /**< 2.2 比较new和old的md5/crc32校验值,相同不压缩,不相同则压缩 */
                if (gzret < 0) /**< 压缩失败 */
                {
                    fitfy.wd[index].err_cnt += 0x100;     /**< 2.3 压缩失败则 err_cnt 高8位加1 */
                }
                else /**< 压缩成功 */
                {
                    fitfy.wd[index].flag[1] = 0;          /**< 2.4 清空2级备份标志位 */
                    fitfy.wd[index].cnt[1]++; /**< 增加2级备份成功计数 */
                    fitfy.wd[index].err_cnt &= 0x00FF; /**< 清空高8位错误计数(保留低8位) */
                    sys_demo_msg_send(MNT_MODULE, SWDMNT_BACKUP_CONF_MSG, NULL, 0); /**< 发送备份完成消息 */
                }
            }
        }
    }
​
    release_inotify(&fitfy); /**< 理论上不会执行到这里,但保持资源释放的完整性 */
​
    return NULL; /**< 线程正常退出返回NULL */
}
​
/**
 * @brief 添加需要监测的文件到inotify监控系统
 * @param cfg json配置文件结构体信息
 * @param fitfy 文件监测结构体信息指针
 * @return int 返回1表示无需监测,返回0表示添加监测事件成功,返回-1表示监测失败
 * @note 设计模式:建造者模式,逐步构建文件监控系统
 *       性能分析:初始化inotify和epoll,涉及系统调用开销,但仅启动时执行一次
 */
static int add_files_to_inotify(demofcg_t cfg, file_inotify_t *fitfy)
{
    int index; /**< 循环索引变量 */
    memset(fitfy, 0, sizeof(file_inotify_t)); /**< 清零文件监控结构体,防止未初始化内存 */
​
    /** 检查是否需要监控文件 */
    if (cfg.bak_len == 0 || cfg.backup == NULL) /**< 备份配置为空或备份数组指针为空 */
        return 1; /**< 返回1表示无需监测任何文件 */
​
    /** 1. 初始化 inotify 文件描述符 */
    fitfy->ntfd = inotify_init(); /**< 创建inotify实例,返回文件描述符 */
    if(fitfy->ntfd == -1){ /**< inotify初始化失败 */
        perror("inotify_init()-error\n"); /**< 打印系统错误信息 */
        return -1; /**< 返回失败 */
    }
​
    /** 分配监控信息数组内存 */
    fitfy->wd = (file_watch_t *)malloc(cfg.bak_len * sizeof(file_watch_t)); /**< 根据备份配置数量分配内存 */
    if (fitfy->wd == NULL) /**< 内存分配失败 */
    {
        close(fitfy->ntfd); /**< 关闭inotify文件描述符 */
        return -1; /**< 返回失败 */
    }
    memset(fitfy->wd, 0, cfg.bak_len * sizeof(file_watch_t)); /**< 清零监控信息数组,数据流:按实际大小清零 */
​
    /** 2. 注册需要监测的文件以及监测类型 */
    uint32_t mask = IN_DELETE_SELF | IN_MOVE_SELF | IN_MODIFY | IN_UNMOUNT; /**< 监控事件掩码:删除自身、移动自身、修改、卸载 */
    for (index = 0; index < cfg.bak_len; index++) /**< 遍历所有备份配置 */
    {
        char file[MAX_BUFFER_LEN] = {0}; /**< 文件完整路径缓冲区 */
​
        /** 构造源文件完整路径 */
        snprintf(file, MAX_BUFFER_LEN, "%s/%s", cfg.backup[index].path, cfg.backup[index].file); /**< 拼接路径和文件名 */
        /** 添加文件到inotify监控 */
        fitfy->wd[fitfy->wd_len].id = inotify_add_watch(fitfy->ntfd, file, mask); /**< 注册监控,返回watch descriptor */
        if (fitfy->wd[fitfy->wd_len].id == -1) /**< 监控添加失败(文件可能不存在) */
        {
            printf("inotify_add_watch(%s)-error\n", file);  /**< 打印错误信息 */
            partition_error_exit(file);                     /**< 分区错误退出程序(文件系统异常) */
        }
        /** 保存监控信息 */
        fitfy->wd[fitfy->wd_len].path = cfg.backup[index].path; /**< 源文件路径 */
        fitfy->wd[fitfy->wd_len].file = cfg.backup[index].file; /**< 源文件名 */
        fitfy->wd[fitfy->wd_len].bakup_path = cfg.backup[index].backup_path; /**< 备份路径 */
        fitfy->wd_len++; /**< 增加监控文件计数 */
    }
    /** 检查是否有成功添加的监控 */
    if (fitfy->wd_len == 0) /**< 没有成功添加任何监控 */
    {
        close(fitfy->ntfd); /**< 关闭inotify文件描述符 */
        free(fitfy->wd); /**< 释放监控信息数组内存 */
        return -1; /**< 返回失败 */
    }
​
    /** 3. 初始化epoll文件描述符 */
    fitfy->epfd = epoll_create1(EPOLL_CLOEXEC); /**< 创建epoll实例,设置close-on-exec标志 */
    if(fitfy->epfd == -1){ /**< epoll创建失败 */
        perror("epoll_create1()-error\n"); /**< 打印系统错误信息 */
        release_inotify(fitfy); /**< 释放已分配的资源 */
        return -1; /**< 返回失败 */
    }
    
    /** 4. 添加epoll监听事件:监听inotify的可读事件 */
    struct epoll_event evt; /**< epoll事件结构体 */
    memset(&evt, 0, sizeof(evt)); /**< 清零事件结构体 */
    evt.events = EPOLLIN;    /**< 监听可读事件,默认是水平触发(LT模式) */
    evt.data.fd = fitfy->ntfd; /**< 关联inotify文件描述符 */
    if (epoll_ctl(fitfy->epfd, EPOLL_CTL_ADD, fitfy->ntfd, &evt) == -1) /**< 将inotify fd添加到epoll监控 */
    {
        perror("epoll_ctl()-error\n"); /**< 打印系统错误信息 */
        release_inotify(fitfy); /**< 释放已分配的资源 */
        return -1; /**< 返回失败 */
    }
​
    return 0; /**< 成功返回 */
}
​
/**
 * @brief 关闭并释放文件监测结构体信息
 * @param fitfy 文件监控结构体指针
 * @return int 总是返回0
 * @note 设计模式:资源清理器,对称于add_files_to_inotify
 *       性能分析:释放所有系统资源,防止资源泄漏
 */
static int release_inotify(file_inotify_t *fitfy)
{
    /** 关闭epoll文件描述符 */
    if (fitfy->epfd > 0) /**< epoll文件描述符有效 */
        close(fitfy->epfd); /**< 关闭epoll文件描述符 */
​
    /** 释放监控信息数组 */
    if (fitfy->wd != NULL) /**< 监控信息数组指针非空 */
    {
        int index; /**< 循环索引 */
        for (index = 0; index < fitfy->wd_len; index++) /**< 遍历所有监控项 */
        {
            inotify_rm_watch(fitfy->ntfd, fitfy->wd[index].id); /**< 移除inotify监控 */
        }
        free(fitfy->wd); /**< 释放监控信息数组内存 */
    }
​
    /** 关闭inotify文件描述符 */
    if (fitfy->ntfd > 0) /**< inotify文件描述符有效 */
        close(fitfy->ntfd); /**< 关闭inotify文件描述符 */
​
    memset(fitfy, 0, sizeof(file_inotify_t)); /**< 清零整个结构体,数据流:安全清理 */
    return 0; /**< 成功返回 */
}
​
/**
 * @brief 获取特定的事件(从epoll事件数组中查找inotify事件)
 * @param events 事件列表数组
 * @param event_num 事件数量
 * @param efd 监听的文件描述符(实际上未使用,使用全局fitfy.ntfd)
 * @return struct epoll_event* 找到的事件指针,未找到返回NULL
 * @note 设计模式:事件过滤器,从多个epoll事件中筛选目标事件
 */
static struct epoll_event *get_epoll_event(struct epoll_event events[MAX_EPOLL_EVENT_LEN], int event_num, int efd)
{
    int index; /**< 循环索引 */
    for (index = 0; index < event_num; index++) /**< 遍历所有epoll事件 */
    {
        if (events[index].data.fd == fitfy.ntfd) /**< 找到inotify文件描述符对应的事件 */
        {
            if (events[index].events & EPOLLIN) /**< 检查是否为可读事件 */
                return &events[index]; /**< 返回事件指针 */
        }
    }
    return NULL; /**< 未找到目标事件 */
}
​
/**
 * @brief 数据库文件压缩函数(带校验值比较)
 * @param file 源文件路径
 * @param gzip_file 压缩文件输出路径缓冲区
 * @return int 成功返回0,失败返回-1,文件相同返回-2
 * @note 设计模式:策略模式,根据校验值决定是否压缩
 *       性能分析:涉及文件校验和外部gzip命令执行,开销较大
 *       数据流结构:gzip_file缓冲区大小MAX_BUFFER_LEN(256字节)
 */
static int gzip_db(char *file, char gzip_file[MAX_BUFFER_LEN])
{
    int len = strlen(gzip_file); /**< 获取输出路径当前长度 */
    char val[16] = {0}; /**< 校验值存储缓冲区,16字节足够存储MD5或CRC32 */
    
    /** 构造校验文件路径(在原路径后追加.crc32) */
    snprintf(gzip_file+len, MAX_BUFFER_LEN-len, ".crc32"); /**< 添加.crc32扩展名 */
    int ret = compare_file(file, gzip_file, val); /**< 比较文件校验值 */
    if (ret <= 0) /**< 文件相同(ret=0)或比较失败(ret<0) */
        return ret; /**< 返回比较结果 */
​
    /** 执行压缩 */
    char cmd[MAX_BUFFER_LEN] = {0}; /**< 命令缓冲区 */
    snprintf(gzip_file+len, MAX_BUFFER_LEN-len, ".gz"); /**< 修改扩展名为.gz */
    snprintf(cmd, MAX_BUFFER_LEN, "busybox nice -n 19 gzip -c %s > %s", file, gzip_file); /**< 构造gzip命令,设置低优先级(nice值19) */
    int status = system(cmd); /**< 执行压缩命令 */
    if (-1 == status || WIFEXITED(status) != true || 0 != WEXITSTATUS(status)) /**< 检查命令执行结果 */
    {
        /** 压缩失败,清理临时文件 */
        snprintf(gzip_file+len, MAX_BUFFER_LEN-len, ".gz"); /**< 确保扩展名为.gz */
        remove(gzip_file);  /**< 删除压缩失败的文件 */
        snprintf(gzip_file+len, MAX_BUFFER_LEN-len, ".crc32"); /**< 修改扩展名为.crc32 */
        remove(gzip_file);  /**< 删除校验文件 */
        return -1; /**< 返回失败 */
    }
​
    /** 保存校验值到文件 */
    snprintf(gzip_file+len, MAX_BUFFER_LEN-len, ".crc32"); /**< 修改扩展名为.crc32 */
    FILE *fp = fopen(gzip_file, "w"); /**< 以写入模式打开校验文件 */
    if (fp == NULL) /**< 文件打开失败 */
        return -1; /**< 返回失败 */
​
    fwrite(val, 1, ret, fp); /**< 写入校验值(ret为校验值长度) */
    fclose(fp); /**< 关闭文件 */
​
    return 0; /**< 成功返回 */
}
​
/**
 * @brief 文件校验值比较(MD5或CRC32)
 * @param file 源文件路径
 * @param check_file 校验文件路径(存储之前计算的校验值)
 * @param val 校验值输出缓冲区
 * @return int 文件相同返回0,文件不同返回校验值长度,失败返回-1
 * @note 设计模式:模板方法模式,根据USE_MD5宏选择不同校验算法
 *       性能分析:MD5计算比CRC32更耗时但更安全
 */
static int compare_file(char *file, char *check_file, char val[16])
{
    int ret = -1; /**< 返回值,默认失败 */
    int wt_sz = 0; /**< 校验值字节大小 */
    unsigned char *wt_ptr = NULL; /**< 校验值数据指针 */
    
    /** 读取已保存的校验值(如果存在) */
    if (access(check_file, R_OK|W_OK) == 0) /**< 校验文件存在且可读写 */
    {
        FILE *fp = fopen(check_file, "rb"); /**< 以二进制读取模式打开校验文件 */
        if (fp != NULL) /**< 文件打开成功 */
        {
            fread(val, 1, 16, fp); /**< 读取校验值(最多16字节) */
            fclose(fp); /**< 关闭文件 */
        }
    }
​
    /** 根据编译选项选择校验算法 */
    #ifdef USE_MD5 /**< 如果启用MD5校验 */
    unsigned char md5val[16] = {0}; /**< MD5校验值缓冲区(16字节) */
    memset(md5val, 0xFF, 16); /**< 初始化为全0xFF(实际md5sum函数会覆盖) */
    md5sum(file, md5val); /**< 计算MD5校验值 */
    ret = memcmp(md5val, val, 16); /**< 比较新旧MD5值 */
    wt_sz = 16; /**< MD5校验值长度为16字节 */
    wt_ptr = md5val; /**< 指向MD5校验值 */
​
    #else /**< 使用CRC32校验(默认) */
    unsigned int crcval = 0xFFFFFFFF; /**< CRC32校验值变量 */
    crc32(file, &crcval); /**< 计算CRC32校验值 */
    ret = memcmp(&crcval, val, sizeof(unsigned int)); /**< 比较新旧CRC32值 */
    wt_sz = sizeof(unsigned int); /**< CRC32校验值长度为4字节 */
    wt_ptr = (unsigned char *)(&crcval); /**< 指向CRC32校验值 */
    #endif
    
    if (ret == 0) /**< 校验值相同 */
        return 0; /**< 返回0表示文件未改变 */
    
    memcpy(val, wt_ptr, wt_sz); /**< 将新校验值复制到输出缓冲区 */
    return wt_sz; /**< 返回校验值长度表示文件已改变 */
}
​
/**
 * @brief 由于文件所在的分区异常导致的系统退出处理
 * @param path 异常文件路径
 * @note 设计模式:错误处理器,处理文件系统异常情况
 *       性能分析:记录日志并发送消息,不实际退出系统(当前版本)
 */
static void partition_error_exit(char *path)
{
    /** 记录进程退出信息 */
    process_exit_info(path, getpid(), 0, SYSDEMO_EXIT_CORE_FROM_PARTITION); /**< 记录分区异常退出信息 */
    printf("%s exception !! core = 0x%02X\n", path, SYSDEMO_EXIT_CORE_FROM_PARTITION); /**< 打印异常信息 */
    
    /** 条件编译:是否实际退出系统(当前版本不退出) */
    #if 0 /**< 分区异常暂时不重启(注释掉退出代码) */
    exit(SYSDEMO_EXIT_CORE_FROM_PARTITION); /**< 退出程序并返回错误码 */
    #endif 
    
    /** 发送备份异常消息 */
    sys_demo_msg_send(NULL, SWDALL_BACKUP_CONF_ABNORMAL_MSG, NULL, 0); /**< 广播备份配置异常消息 */
}
​
/**
 * @brief 校验路径所在的分区是否正常(读写测试)
 * @param path 文件路径
 * @return true 分区正常
 * @return false 分区异常
 * @note 设计模式:健康检查器,通过实际读写测试验证分区状态
 *       性能分析:创建4KB测试文件,涉及磁盘I/O,但仅错误恢复时执行
 */
static bool is_partition_ok(char *path)
{
    if (path == NULL) /**< 路径指针为空 */
        return false; /**< 返回异常 */
​
    char file[MAX_BUFFER_LEN] = {0}; /**< 测试文件路径缓冲区 */
    snprintf(file, MAX_BUFFER_LEN, "%s/.partitiontmpfile", path); /**< 构造测试文件路径(隐藏文件) */
    
    /** 1. 检测该分区是否可写 */
    FILE *fp = fopen(file, "w"); /**< 以写入模式打开测试文件 */
    if (fp == NULL) /**< 文件打开失败(权限不足或分区只读) */
        return false; /**< 返回异常 */
    
    /** 写入测试数据(全0xFF) */
    char buffer[4096]; /**< 4KB测试缓冲区,数据流:固定大小 */
    int n = 0; /**< 已写入字节数 */
    int bufsz = 4096; /**< 缓冲区大小 */
    memset(buffer, 0xFF, bufsz); /**< 填充缓冲区为全0xFF */
​
    while (n < bufsz) /**< 循环写入直到写满缓冲区 */
    {
        int ret = fwrite(buffer+n, 1, bufsz-n, fp); /**< 写入数据 */
        if (ret == 0) /**< 写入失败 */
            break; /**< 跳出循环 */
        n += ret; /**< 更新已写入字节数 */
    }
    fclose(fp); /**< 关闭文件 */
    if (n != bufsz) /**< 写入字节数不足 */
    {
        remove(file); /**< 删除测试文件 */
        return false; /**< 返回异常 */
    }
​
    /** 2. 检测该分区是否可读 */
    fp = fopen(file, "rb"); /**< 以二进制读取模式重新打开文件 */
    if (fp == NULL) /**< 文件打开失败 */
        return false; /**< 返回异常 */
​
    /** 读取并验证数据 */
    n = 0; /**< 重置已读取字节数 */
    memset(buffer, 0, bufsz); /**< 清空缓冲区 */
    while (n < bufsz) /**< 循环读取直到读满缓冲区 */
    {
        int ret = fread(buffer+n, 1, bufsz-n, fp); /**< 读取数据 */
        if (ret == 0) /**< 读取失败或到达文件末尾 */
            break; /**< 跳出循环 */
        n += ret; /**< 更新已读取字节数 */
    }
    fclose(fp); /**< 关闭文件 */
    remove(file); /**< 删除测试文件 */
    
    if (n != bufsz) /**< 读取字节数不足 */
        return false; /**< 返回异常 */
    
    /** 验证读取的数据是否与写入的一致 */
    for (n = 0; n < bufsz; n++) /**< 遍历缓冲区每个字节 */
    {
        if (buffer[n] != 0xFF) /**< 数据不一致 */
            return false; /**< 返回异常 */
    }
​
    printf("current partition = %s is ok !!\n", path); /**< 打印分区正常信息 */
    return true; /**< 返回正常 */
}
​
/**
 * @brief 打印backup线程的全局信息(调试接口)
 * @note 设计模式:调试信息聚合器,集中展示备份模块状态
 *       性能分析:仅调试时使用,遍历所有监控项,可能有较多输出
 */
void debug_backup_handler(void)
{
    int index; /**< 循环索引 */
    /** 打印头部信息 */
    printf("--------------- debug_backup_handler ---------------\n"); /**< 分隔线 */
    printf("| epoll fd = %d\n", fitfy.epfd); /**< epoll文件描述符 */
    printf("| inotify fd = %d\n", fitfy.ntfd); /**< inotify文件描述符 */
    printf("| watch nums = %d\n", fitfy.wd_len); /**< 监控文件数量 */
    printf("----------------------------------------------------\n"); /**< 分隔线 */
    
    /** 遍历打印每个监控文件的详细信息 */
    for (index = 0; index < fitfy.wd_len; index++) /**< 遍历所有监控项 */
    {
        printf("| source path: %s\n", fitfy.wd[index].path); /**< 源文件路径 */
        printf("| bakup path: %s\n", fitfy.wd[index].bakup_path); /**< 备份路径 */
        printf("| file name: %s\n", fitfy.wd[index].file); /**< 文件名 */
        printf("| id = %d\n", fitfy.wd[index].id); /**< inotify监控ID */
        printf("| flag[0] = %d, flag[1] = %d\n", fitfy.wd[index].flag[0], fitfy.wd[index].flag[1]); /**< 备份标志位 */
        printf("| cnt[0] = %d, cnt[1] = %d\n", fitfy.wd[index].cnt[0], fitfy.wd[index].cnt[1]); /**< 备份次数 */
        printf("| err_cnt = %04x\n", fitfy.wd[index].err_cnt); /**< 错误计数器(十六进制显示) */
        printf("----------------------------------------------------\n"); /**< 每个监控项后的分隔线 */
    }
}
​

5. deadlock_check.c/h - 死锁检测

设计思想: 基于消息的监控
├── 为什么基于消息队列?
│   ├── 无侵入式监控
│   ├── 反映实际业务阻塞
│   └── 与系统架构匹配
├── 状态跟踪机制
│   ├── 连续计数,避免误报
│   ├── 定时检测,平衡开销
│   └── 模块化设计,易于扩展
└── 设计局限性
    ├── 只检测消息线程死锁
    ├── 预留其他检测接口
    └── 依赖消息系统实现
/**
 * @file deadlock_check.c
 * @brief 死锁检测模块实现文件,通过消息队列状态检测进程死锁
 */
​
#include <stdio.h>                      /**< 标准输入输出头文件,提供printf等函数 */
#include <stdlib.h>                     /**< 标准库头文件,提供malloc、free等函数 */
#include <string.h>                     /**< 字符串处理头文件,提供memset、strlen等函数 */
​
#include "sysconf_struct_types.h"       /**< 系统配置结构体类型定义头文件 */
#include "message.h"                    /**< 消息模块头文件,提供消息相关结构体和函数 */
​
#include "deadlock_check.h"             /**< 死锁检测模块头文件,包含本模块的函数声明和常量定义 */
​
/**
 * @defgroup MODULE_IDENTIFICATION 模块标识常量
 * @brief 用于模块识别的键值和掩码
 * @{
 */
#define MODULE_KEYCODE      (0x7a9ec500) /**< 模块键值常量,用于模块识别 */
#define KEYCODE_MASK        (0xff)       /**< 键值掩码,用于提取特定比特位 */
/** @} */ /* MODULE_IDENTIFICATION */
​
/**
 * @brief 系统模块名称数组
 * @note 数据流结构:字符串指针数组,以NULL结尾表示数组结束
 *       设计模式:注册表模式,集中管理系统所有模块信息
 */
static char * module_list[] =           /**< 模块名称字符串指针数组 */
{
    MNT_MODULE,         /**< 管理模块名称 */
    GUI_MODULE,         /**< 界面模块名称 */
    CANNETHOST_MODULE,  /**< 主机联网模块名称 */
    POWER_MODULE,       /**< 电源管理模块名称 */
    DECHIP_MODULE,      /**< 解码芯片模块名称 */
    PRINTER_MODULE,     /**< 打印机模块名称 */
    FACTORY_MODULE,     /**< 产测模块名称 */
    HAL_MODULE,         /**< HAL模块名称 */
    MACHINE_BUS_MODULE, /**< 机内总线模块名称 */
    LINKAGE_MODULE,     /**< 联动模块名称 */
    IOB_MODULE,         /**< 接口板模块名称 */
    HLOG_MODULE,        /**< 日志管理模块名称 */
    NULL,               /**< 数组结束标志,用于遍历时判断边界 */
};
​
/**
 * @brief 心跳消息结构体
 * @note 设计模式:状态保持器,跟踪每个模块的消息队列状态
 *       数据流结构:大小约为32字节(假设指针8字节,int 4字节,time_t 8字节)
 */
typedef struct{
    module_infos_t* msg;                /**< 模块信息结构体指针,指向目标模块的消息信息 */
    int deadlock_cnt;                   /**< 死锁疑似事件计数器,连续疑似死锁次数 */
​
    int read_index;                     /**< 用于记录首帧时间的读索引 */
    time_t read_index_time;             /**< 用于记录首帧的时间戳 */
}beat_msg;
​
/**
 * @brief 心跳消息列表数组,每个元素对应一个模块的状态跟踪
 * @note 数据流结构:静态数组,大小MODULE_NUM_MAX由系统定义
 *       性能分析:数组大小固定,内存占用可预测,访问效率高
 */
static beat_msg beat_msg_list[MODULE_NUM_MAX]; /**< 心跳消息状态跟踪数组,初始全零 */
​
/** 静态函数声明 */
static bool is_msg_deadlock(int pid);   /**< 检查消息线程是否死锁 */
static bool is_other_deadlock(int pid); /**< 检查其他原因导致的死锁(预留接口) */
​
/**
 * @brief 初始化心跳消息计数器的模块列表
 * @return int 总是返回0
 * @note 设计模式:初始化器模式,清空状态跟踪数组
 *       性能分析:O(1)复杂度,仅执行一次memset操作
 */
int init_beat_msg_list(void)
{
    memset(beat_msg_list, 0, sizeof(beat_msg_list)); /**< 清零整个状态跟踪数组 */
    return 0;                           /**< 总是成功返回 */
}
​
/**
 * @brief 检查指定进程是否发生死锁(主入口函数)
 * @param pid 进程ID
 * @return true 检测到死锁
 * @return false 未检测到死锁
 * @note 设计模式:策略模式,组合多种死锁检测方法
 *       性能分析:先检查消息死锁,再检查其他死锁,顺序执行
 */
bool check_deadlock(int pid)
{
    /** 优先检查消息线程死锁(主要检测方法) */
    if (is_msg_deadlock(pid) == true)   /**< 调用消息死锁检测 */
    {
        return true;                    /**< 检测到死锁,立即返回true */
    }
    /** 预留:检查其他原因导致的死锁 */
    if (is_other_deadlock(pid) == true) /**< 调用其他死锁检测(当前总是返回false) */
    {
        return true;                    /**< 检测到死锁,返回true */
    }
    return false;                       /**< 未检测到任何死锁,返回false */
}
​
/**
 * @brief 判断消息线程是否死锁
 * @param pid 进程ID
 * @return true 消息线程死锁
 * @return false 消息线程正常
 * @note 设计模式:状态机检测,基于消息队列的读写状态
 *       性能分析:遍历所有模块,对匹配的模块进行状态跟踪,可能有等待延迟
 */
static bool is_msg_deadlock(int pid)
{
    int msg_id;                         /**< 模块索引,用于遍历module_list数组 */
    for (msg_id = 0; module_list[msg_id] != NULL; msg_id++) /**< 遍历所有已注册模块 */
    {
        /** 根据模块名称获取模块信息 */
        module_infos_t* msg = get_module_infos_byname(module_list[msg_id]); /**< 获取模块信息结构体 */
        if (msg == NULL)               /**< 模块信息获取失败(模块可能未注册) */
            continue;                   /**< 跳过该模块 */
        if (msg->pid != pid)            /**< 模块的PID与目标PID不匹配 */
            continue;                   /**< 跳过非目标进程的模块 */
​
        /** 更新状态跟踪结构体 */
        beat_msg_list[msg_id].msg = msg; /**< 保存模块信息指针 */
        
        /** 检查消息队列状态 */
        if (msg->msg.msg_num == 0)      /**< 消息队列为空,表示消息线程正常处理消息 */
        {
            beat_msg_list[msg_id].deadlock_cnt = 0; /**< 重置死锁计数器 */
            continue;                   /**< 继续检查下一个模块 */
        }
​
        /** 消息队列非空,可能存在死锁,开始跟踪检测 */
        /** 1. 记录队列中的首帧消息状态 */
        int read_index = msg->msg.read_index;                           /**< 记录当前读指针位置 */
        time_t read_index_time = msg->msg.message[read_index].msg_time; /**< 记录当前读指针处消息的时间戳 */
        
        /** 如果是首次检测或计数器已清零,则初始化跟踪状态 */
        if (beat_msg_list[msg_id].deadlock_cnt > 0) /**< 已经处于跟踪状态 */
        {
            /** 保持之前记录的读指针和时间戳(持续跟踪同一帧消息) */
            read_index = beat_msg_list[msg_id].read_index;
            read_index_time = beat_msg_list[msg_id].read_index_time;
        }
        else                                       /**< 首次发现消息队列非空 */
        {
            /** 初始化跟踪状态 */
            beat_msg_list[msg_id].read_index = read_index;      /**< 保存读指针 */
            beat_msg_list[msg_id].read_index_time = read_index_time; /**< 保存时间戳 */
        }
​
        /** 2. 等待500毫秒:判断队列的第一帧可读消息是否被处理 */
        struct timeval timeout = {.tv_sec = 0, .tv_usec = 500*1000}; /**< 设置500ms超时 */
        select(0, NULL, NULL, NULL, &timeout); /**< 阻塞等待500ms,性能:系统调用开销 */
​
        /** 3. 检查消息是否被处理 */
        if (msg->msg.msg_num > 0 &&                    /**< 消息队列仍然非空 */
            read_index == msg->msg.read_index &&       /**< 读指针未移动 */
            read_index_time == msg->msg.message[msg->msg.read_index].msg_time) /**< 时间戳未变(同一帧消息) */
        {
            /** 消息未被处理,增加死锁疑似事件计数 */
            beat_msg_list[msg_id].deadlock_cnt++;
            /** 检查是否达到死锁判定阈值 */
            if (beat_msg_list[msg_id].deadlock_cnt >= DEADLOCK_SUSPECT_EVENT_MAX) /**< 连续达到最大疑似次数 */
            {
                /** 判定为死锁,打印告警信息 */
                printf("\033[35mpid = %d is deadlock, md_name = %s !!\n\033[0m", pid, msg->name); /**< 彩色输出死锁信息 */
                beat_msg_list[msg_id].deadlock_cnt = 0; /**< 重置计数器(可选) */
                return true;            /**< 返回true表示检测到死锁 */
            }
        }
        else                            /**< 消息已被处理,说明线程正常 */
        {
            beat_msg_list[msg_id].deadlock_cnt = 0; /**< 清空死锁疑似事件次数 */
        }
    }
    return false;                       /**< 遍历所有模块均未检测到死锁 */
}
​
/**
 * @brief 判断是否是其他原因导致的死锁(预留接口)
 * @param pid 进程ID
 * @return true 其他原因导致死锁
 * @return false 无其他原因死锁
 * @note 设计模式:预留扩展点,为未来的死锁检测方法提供接口
 *       当前实现总是返回false,表示尚未实现其他检测方法
 */
static bool is_other_deadlock(int pid)
{
    return false;                       /**< 预留接口,当前总是返回false */
}
​

五、数据结构设计树形分析

1. 核心数据结构评估

demofcg_t (配置总容器)
├── 优点
│   ├── 层次清晰,逻辑分组
│   ├── 指针+长度,动态大小
│   └── 内存连续,访问高效
├── 缺点
│   ├── 多层间接访问,cache不友好
│   ├── 字符串指针,需要额外内存管理
│   └── 没有版本控制字段
└── 改进建议
    ├── 添加配置版本字段
    ├── 考虑内存池管理字符串
    └── 添加配置校验标志

2. 进程配置结构体 js_process_t

字段分析
├── 必要字段 (8个)
│   ├── path/name/type - 进程标识
│   ├── pid - 运行时状态
│   ├── arg[]/arg_len - 启动参数
│   ├── wait - 启动等待
│   └── threshold - 监控阈值
├── 设计合理性
│   ├── 参数数组大小固定(DEMO_CONFIG_MAX_ARGUMENT=10)
│   ├── 阈值结构体内嵌,逻辑关联
│   └── 类型字段使用位掩码,支持组合
└── 潜在问题
    ├── arg数组可能浪费内存
    ├── 没有进程重启次数限制
    └── 缺乏优先级或CPU亲和性设置

3. 文件监控结构体 file_watch_t

位字段设计分析
├── err_cnt 双用途设计
│   ├── 低8位: 1级备份错误计数
│   ├── 高8位: 2级备份错误计数
│   └── 节省内存,但可读性差
├── flag[2] 标志位数组
│   ├── flag[0]: 1级备份需要
│   ├── flag[1]: 2级备份需要
│   └:: 清晰表达状态机
└:: cnt[2] 计数器数组
    ├── cnt[0]: 1级备份成功次数
    ├── cnt[1]: 2级备份成功次数
    └── 用于统计和调试

4. 内存使用优化分析

静态数据内存
├── CRC32表: 1024字节 (256*4)
├── 模块列表: 约13*8=104字节
├── 心跳消息数组: MODULE_NUM_MAX * sizeof(beat_msg)
└── 文件监控数组: cfg.bak_len * sizeof(file_watch_t)
​
运行时内存
├── 配置文件缓冲: 文件大小
├── JSON解析树: 可变大小
├── 进程配置数组: cfg.proc_len * sizeof(js_process_t)
└── 备份配置数组: cfg.bak_len * sizeof(js_backup_t)
​
内存管理策略
├── 启动时一次性分配
├── 运行期间少量动态分配
├── 字符串使用指针引用(不复制)
└── 适合嵌入式环境

六、架构设计综合评价

优点总结

  1. 模块化清晰:每个文件职责单一,接口明确

  2. 层次结构合理:从底层系统调用到高层业务逻辑分层清晰

  3. 错误处理全面:从分区错误到进程异常都有相应处理

  4. 资源管理严谨:对称的分配/释放模式,避免内存泄漏

  5. 可扩展性好:预留接口和条件编译支持功能扩展

  6. 性能考虑周全:epoll非阻塞、批量处理、查表算法等优化

可改进之处

  1. 配置热重载:当前需要重启才能加载新配置

  2. 更细粒度监控:只监控堆内存,缺少栈内存和文件描述符监控

  3. 日志系统增强:当前日志较为简单,缺少日志级别和轮转策略

  4. 测试覆盖率:缺乏单元测试和集成测试框架

  5. 配置文件验证:缺少JSON schema验证,配置错误可能运行时才发现

设计模式应用评估

  1. 状态模式:进程生命周期管理实现良好

  2. 观察者模式:文件监控系统是典型实现

  3. 策略模式:阈值处理和校验算法选择

  4. 工厂模式:配置对象创建

  5. 单例模式:全局配置管理

适合的应用场景

  1. 嵌入式Linux系统:资源受限,需要可靠守护进程

  2. 工业控制设备:需要长时间稳定运行

  3. 物联网网关:需要管理多个子进程

  4. 网络设备:需要进程监控和自动恢复

设计一个良好、实现稳健的系统守护进程框架,需要丰富的Linux系统编程经验和对嵌入式环境的深入理解。必须架构简洁性、可靠性和功能性之间取得了良好平衡。

第二部分 Makefile

# ================================================================
# sysdemo 模块 Makefile 配置文件
# 用于编译系统守护进程应用程序
# ================================================================
​
# ==================== 路径配置区域 ====================
# 根文件系统路径(相对路径,指向项目根目录)
ROOTFS_PATH = ../../
# 包含头文件路径,如果未定义则使用默认路径
INC_PATH ?= $(ROOTFS_PATH)include/
# 可执行文件安装路径,如果未定义则使用默认路径
BIN_PATH ?= $(ROOTFS_PATH)image/user_rootfs/bin
# 库文件路径,如果未定义则使用默认路径
LIBS_PATH ?= $(ROOTFS_PATH)image/user_rootfs/lib
​
# ==================== 目标文件配置 ====================
# 最终生成的可执行文件名
EXECUTABLE := sysdemo
# 库文件目录
LIBDIR:= $(LIBS_PATH)
# 需要链接的库列表:cjson(JSON解析)、message(消息模块)、pthread(线程)、db(数据库)、sysfunc(系统函数)
LIBS := cjson message pthread db sysfunc
​
# ==================== 源文件配置 ====================
# 当前本地路径
LOCAL_PATH := .
# 头文件包含路径列表:
# 1. 当前目录(用于包含本模块的头文件)
# 2. 项目公共include目录(用于包含系统公共头文件)
INCLUDEH := \
    $(LOCAL_PATH)/  \
    $(LOCAL_PATH)/../../include
​
# 自定义编译标志,可由外部Makefile传入(如调试标志、优化级别等)
DEFIND_FLAG ?=
​
# ==================== 自动文件发现 ====================
# 使用wildcard函数自动查找当前目录下所有.c源文件
SRCDIR := $(wildcard *.c)
# 包含路径变量赋值
INCLUDES := $(INCLUDEH)
# BIN_PATH := ~/nfs  # 注释掉的备用安装路径
​
# ==================== 编译器配置 ====================
# 交叉编译工具链:arm-linux-gcc(针对ARM架构的Linux系统)
CC:=arm-linux-gcc
# 编译标志:-g(包含调试信息) -Wall(显示所有警告) -lm(链接数学库)
CFLAGS:=-g -Wall -lm
# 预处理器标志(包含编译器标志)
CPPFLAGS := $(CFLAGS)
# 添加包含路径:将INCLUDES中的每个路径前加上-I前缀
CPPFLAGS += $(addprefix -I,$(INCLUDES)/)
# 自动生成依赖文件(.d文件),用于增量编译
CPPFLAGS += -MMD
# 添加自定义编译标志
CPPFLAGS += $(DEFIND_FLAG)
# 删除命令别名
RM-F := rm -f
​
# ==================== 文件列表生成 ====================
# 源文件列表
SRCS := $(SRCDIR)
# 对象文件列表:将.c文件替换为.o文件(模式替换)
OBJS := $(patsubst %.c,%.o,$(SRCS))
# 依赖文件列表:将.o文件替换为.d文件
DEPS := $(patsubst %.o,%.d,$(OBJS))
# 查找缺失的依赖文件(实际不存在的.d文件)
MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))
# 根据缺失的依赖文件找到对应的源文件
MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.c,$(MISSING_DEPS)))
​
# ==================== 构建目标 ====================
# 默认目标:构建可执行文件
all: $(EXECUTABLE)
​
# 安装目标:将编译好的可执行文件复制到目标路径
install :
    @cp $(EXECUTABLE) $(BIN_PATH)
    @echo " [target] $(BIN_PATH)/$(EXECUTABLE) "
    
# 清理目标:删除所有中间文件和目标文件
clean :
    @$(RM-F) *.o *.d
    @$(RM-F) $(OBJS_DIR)
    @$(RM-F) $(EXECUTABLE)
    @$(RM-F) $(BIN_PATH)/$(EXECUTABLE)
    @echo " [clean] sucess !!"
​
# ==================== 依赖处理 ====================
# 如果存在缺失的依赖文件,则删除对应的.o文件(强制重新编译)
ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
    @$(RM-F) $(patsubst %.d,%.o,$@)
endif
​
# 包含所有依赖文件(如果存在)
# -include表示如果文件不存在也不报错
-include $(DEPS)
​
# ==================== 链接规则 ====================
# 主链接规则:将所有的.o文件链接成可执行文件
# 1. 设置输出可执行文件名
# 2. 链接所有对象文件
# 3. 添加库搜索路径
# 4. 添加需要链接的库
# 5. 设置运行时库搜索路径
$(EXECUTABLE) : $(OBJS) 
    $(CC) $(CPPFLAGS) -o $(EXECUTABLE) $(OBJS) $(addprefix -L,$(LIBDIR)) $(addprefix -l,$(LIBS)) -Wl,-rpath=$(LIBDIR)
​
# ==================== 编译规则 ====================
# 通用编译规则:将.c文件编译成.o文件
# $@ 表示目标文件(.o文件)
# $< 表示第一个依赖文件(.c文件)
%.o:%.c
    $(CC) $(CPPFLAGS) -o $@ -c $<

程序/模块架构思想树形结构分析

整体架构层次

sysdemo系统守护进程
├── 核心管理层 (sysdemo_main.c)
│   ├── 进程生命周期管理
│   ├── 系统初始化
│   ├── 命令行参数处理
│   └── 信号处理
│
├── 配置管理模块 (demo_config.c/h)
│   ├── JSON配置文件解析
│   ├── 结构体定义
│   │   ├── demofcg_t (总配置)
│   │   ├── js_process_t (进程配置)
│   │   ├── js_bootcfg_t (启动配置)
│   │   ├── js_backup_t (备份配置)
│   │   └── js_threshold_t (阈值配置)
│   └── 配置加载/释放接口
│
├── 进程监控模块 (集成在main中)
│   ├── 进程状态检查
│   │   ├── PID获取
│   │   ├── 进程状态获取
│   │   ├── 堆内存监控
│   │   └── CPU使用率监控(预留)
│   ├── 阈值管理
│   │   ├── CPU阈值检测
│   │   ├── 内存阈值检测
│   │   └── 处理策略(重启等)
│   └── 死锁检测模块 (deadlock_check.c/h)
│       ├── 心跳消息监控
│       ├── 消息队列状态检查
│       └── 死锁判定逻辑
│
├── 备份管理模块 (file_inotify.c/backup_db.h)
│   ├── 文件监控子系统
│   │   ├── inotify初始化
│   │   ├── epoll事件循环
│   │   └── 文件变化检测
│   ├── 双级备份策略
│   │   ├── 1级备份(实时同步)
│   │   ├── 2级备份(可靠存档)
│   │   └── 完整性校验
│   └── 分区健康检查
│       ├── 读写测试
│       └── 错误恢复
│
├── 工具模块
│   ├── 校验模块 (checkapi.c/h)
│   │   ├── CRC32校验
│   │   └── MD5校验(条件编译)
│   └── 系统函数封装 (sysdemo.h)
│       ├── 时间相关函数
│       ├── 延时函数
│       └── 系统信息获取
│
└── 通信模块
    ├── 消息系统接口 (集成)
    ├── 进程间通信
    └── 系统状态报告

软件设计系统源码树形结构

项目根目录/
├── 系统守护进程模块 (sysdemo/)
│   ├── Makefile                    # 本文件 - 构建配置
│   ├── sysdemo_main.c              # 主程序入口
│   ├── sysdemo.h                   # 主头文件(宏定义、接口声明)
│   ├── demo_config.c               # 配置文件解析实现
│   ├── demo_config.h               # 配置结构体定义
│   ├── deadlock_check.c            # 死锁检测实现
│   ├── deadlock_check.h            # 死锁检测接口
│   ├── checkapi.c                  # 文件校验实现
│   ├── checkapi.h                  # 校验接口
│   ├── file_inotify.c              # 文件监控和备份实现
│   └── backup_db.h                 # 备份模块接口
│
├── 公共头文件目录 (include/)
│   ├── sysconf_struct_types.h      # 系统配置结构体类型
│   ├── system_functions.h          # 系统函数声明
│   ├── message.h                   # 消息系统接口
│   └── db_api.h                    # 数据库API接口
│
├── 第三方库依赖 (image/user_rootfs/lib/)
│   ├── libcjson.so                 # JSON解析库
│   ├── libmessage.so               # 消息模块库
│   ├── libdb.so                    # 数据库操作库
│   └── libsysfunc.so               # 系统函数库
│
├── 配置文件目录 (由代码定义)
│   ├── /mnt/user_data/config/democfg.json    # 主配置文件(首选)
│   └── /mnt/user_rootfs/bin/democfg.json     # 默认配置文件(备选)
│
└── 运行时目录
    ├── /mnt/user_data/log/         # 日志和core文件目录
    │   ├── core.log                # 进程终止记录
    │   └── *.core                  # 核心转储文件
    └── 备份文件目录 (由配置指定)

架构设计思想分析

1. 模块化设计
  • 高内聚低耦合:每个.c文件负责一个明确的功能模块

  • 接口清晰:通过.h文件定义模块间的接口契约

  • 条件编译:通过宏定义控制功能开关(如USE_MD5、ENABLE_PATCH_PATH)

2. 分层架构
  • 应用层:sysdemo_main.c - 业务逻辑协调

  • 服务层:配置管理、进程监控、文件备份

  • 工具层:校验算法、系统函数封装

  • 驱动层:inotify/epoll系统调用封装

3. 设计模式应用
  • 单例模式:全局配置结构体、JSON解析树

  • 观察者模式:inotify文件监控

  • 状态模式:进程生命周期管理

  • 策略模式:阈值处理策略、校验算法选择

  • 工厂模式:配置对象创建

4. 错误处理与容错
  • 分级错误处理:错误计数器、错误升级机制

  • 资源管理:对称的资源分配/释放模式

  • 健康检查:分区读写测试、进程状态验证

  • 优雅降级:主备配置文件、双级备份策略

5. 性能优化考虑
  • 增量编译:通过.d依赖文件实现

  • 事件驱动:epoll非阻塞I/O减少CPU占用

  • 延迟加载:配置文件按需解析

  • 资源复用:静态局部变量减少重复计算

6. 可维护性设计
  • 清晰的目录结构:源码、头文件、库文件分离

  • 自动化构建:Makefile自动处理依赖关系

  • 调试支持:调试信息输出、信号处理

  • 版本兼容:弃用功能的向后兼容

7. 安全考虑
  • 权限检查:文件可执行性验证

  • 输入验证:配置参数范围检查

  • 资源限制:日志文件轮转、最大行数限制

  • 进程隔离:独立线程处理备份任务

构建系统特点

  1. 交叉编译支持:针对ARM架构的交叉编译工具链

  2. 自动化依赖管理:通过-MMD自动生成和包含依赖关系

  3. 灵活的路径配置:通过变量覆盖支持不同部署环境

  4. 增量编译优化:缺失依赖检测避免不必要的重新编译

  5. 安装目标:支持一键部署到目标文件系统

这个架构体现了典型的Linux系统守护进程设计模式,结合了现代软件工程的模块化思想和传统Unix的简洁实用主义。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐