From 33252501c3fd6d3498f56e8184ffff6100a40a2e Mon Sep 17 00:00:00 2001 From: ypc <15051963820@163.com> Date: Tue, 7 Jul 2026 13:47:24 +0800 Subject: [PATCH] =?UTF-8?q?[=E6=B3=A8=E9=87=8A=E8=A7=84=E8=8C=83]=20?= =?UTF-8?q?=E5=A4=B4=E6=96=87=E4=BB=B6=E5=85=A8=E9=83=A8=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E5=A3=B0=E6=98=8E=E5=8A=A0=E8=AF=A6=E7=BB=86=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=20+=20myLog.c=20=E5=85=A8=E9=9D=A2=E6=B3=A8=E9=87=8A=20+=20?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=A3=8E=E6=A0=BC=E8=AE=B0=E5=BF=86=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - myLog.h: 所有函数/macro 加 @brief/@param/@return 注释 - myFunc.h: 65 个函数全部加详细注释,结构体成员逐行注释 - myLog.c: 每个内部函数加功能注释、关键逻辑行间注释、}后空行、return前空行 - 代码风格规范已写入 /memories/code-style.md --- release/inc/myFunc.h | 253 ++++++++++++++++++++++++----- release/inc/myLog.h | 47 +++++- src/public/libfunc/inc/myFunc.h | 253 ++++++++++++++++++++++++----- src/public/liblog/inc/myLog.h | 47 +++++- src/public/liblog/src/myLog.c | 273 ++++++++++++++++++++++++++------ 5 files changed, 719 insertions(+), 154 deletions(-) diff --git a/release/inc/myFunc.h b/release/inc/myFunc.h index 6cd97bb..15d3cde 100644 --- a/release/inc/myFunc.h +++ b/release/inc/myFunc.h @@ -1,6 +1,8 @@ /** * @file myFunc.h - * @brief 基础工具函数 — 校验/字符串/时间/文件/进程/环境/IPC + * @brief 基础工具函数接口 — 65 个函数,7 大分类 + * @details 校验计算 / 字符串转换 / 时间处理 / 文件目录操作 / + * 进程管理 / 环境变量路径 / IPC 消息队列 */ #ifndef _MY_FUNC_H_ @@ -13,141 +15,306 @@ extern "C" { #endif -/* ---- 系统时间 ---- */ +/** + * @brief 系统时间结构体(位域存储,用于嵌入式装置内部表示) + * @note year: 7bit(0-127+1900), month:4bit(1-12), day:5bit(1-31) + * hour:5bit(0-23), min:6bit(0-59), ms:16bit(0-59999) + */ typedef struct { - uint32_t ms : 16; - uint32_t min : 6; - uint32_t : 2; - uint32_t hour: 5; - uint32_t : 3; - uint32_t day : 5; - uint32_t week: 3; - uint32_t month:4; - uint32_t : 4; - uint32_t year: 7; - uint32_t : 1; + uint32_t ms : 16; /* 毫秒(实际存储秒*1000+毫秒,范围 0-59999) */ + uint32_t min : 6; /* 分钟 0-59 */ + uint32_t : 2; /* 保留 */ + uint32_t hour: 5; /* 小时 0-23 */ + uint32_t : 3; /* 保留 */ + uint32_t day : 5; /* 日期 1-31 */ + uint32_t week: 3; /* 星期 */ + uint32_t month:4; /* 月份 1-12 */ + uint32_t : 4; /* 保留 */ + uint32_t year: 7; /* 年份偏移(实际值-1900),范围 0-127 */ + uint32_t : 1; /* 保留 */ } stru_time_sys; -/* ---- 日历时间 ---- */ +/** + * @brief 日历时间结构体(Unix 时间 + 微秒) + */ typedef struct { - time_t sec; - time_t usec; + time_t sec; /* Unix 时间戳(秒) */ + time_t usec; /* 微秒部分 */ } stru_time_cal; -/* ---- IPC 消息文本 ---- */ +/** + * @brief IPC 消息文本载荷 + */ typedef struct { - int qid; - int type; - int len; - char text[4096]; + int qid; /* 响应队列 ID */ + int type; /* 响应类型(enum_ipc_resp) */ + int len; /* 有效数据长度 */ + char text[4096]; /* 消息内容 */ } stru_ipc_text; -/* ---- IPC 响应类型 ---- */ +/** + * @brief IPC 响应类型 + */ typedef enum { - IPC_RESP_NONE = 1, - IPC_RESP_ONCE, - IPC_RESP_ALWAYS + IPC_RESP_NONE = 1, /* 无需响应 */ + IPC_RESP_ONCE, /* 单次响应 */ + IPC_RESP_ALWAYS /* 持续响应 */ } enum_ipc_resp; -/* ---- IPC 消息 ---- */ +/** + * @brief IPC 消息(System V 消息队列格式) + */ typedef struct { - long id; - stru_ipc_text t; + long id; /* 消息类型 ID */ + stru_ipc_text t; /* 消息载荷 */ } stru_ipc_msg; -/* ---- 文件描述头(256字节) ---- */ +/** + * @brief 文件描述头结构(256 字节,含 CRC 校验) + * @note flag 魔数为 0xA55AA55AA55AA55A + */ #pragma pack(1) typedef struct { - uint64_t flag; - char ver[16]; - char author[16]; - char modify[32]; - uint16_t crc; - char desc[128]; - char bak[54]; + uint64_t flag; /* 魔数标识 0xA55AA55AA55AA55A */ + char ver[16]; /* 版本号 */ + char author[16]; /* 作者 */ + char modify[32]; /* 修改时间 YYYY-MM-DD HH-MM-SS */ + uint16_t crc; /* CRC16 校验 */ + char desc[128]; /* 附加描述 */ + char bak[54]; /* 对齐填充(总计 256 字节) */ } stru_file_info; #pragma pack() -/* ===== 校验 ===== */ +/* ================================================================ + * 校验计算函数(CRC16 使用 Modbus 多项式 0xA001) + * ================================================================ */ + +/** 8 位累加校验和 */ uint8_t func_cal_sum_byte(const uint8_t *d, uint32_t n); + +/** 16 位累加校验和 */ uint16_t func_cal_sum_word(const uint8_t *d, uint32_t n); + +/** CRC16(Modbus 多项式,初始值 0xFFFF) */ uint16_t func_cal_crc16(const uint8_t *d, uint32_t n); + +/** CRC32(标准多项式,初始值 0xFFFFFFFF) */ uint32_t func_cal_crc32(const uint8_t *d, uint32_t n); + +/** 文件 CRC16(4KB 缓冲区流式计算) */ uint16_t func_cal_file_crc16(const char *path); + +/** 文件 CRC32(4KB 缓冲区流式计算) */ uint32_t func_cal_file_crc32(const char *path); -/* ===== 字符串转换 ===== */ +/* ================================================================ + * 字符串转换函数 + * ================================================================ */ + +/** 单个十六进制字符 → 数值('F'→15) */ uint8_t func_hex_ch(char c); + +/** 单个十进制字符 → 数值('9'→9) */ uint8_t func_dec_ch(char c); + +/** 十六进制字符串 "FF" → int 255 */ int func_hex2dec(char *s); + +/** 十进制字符串 "255" → int 255(支持负号) */ int func_dec2dec(char *s); + +/** + * @brief 十六进制字符串 → 字节缓冲区 + * @param s 源字符串(如 "0102FF") + * @param dst 目标缓冲区 + * @param len [out]实际字节数 + * @return 0 成功,-1 奇数长度 + */ int func_hex2buf(const char *s, char *dst, int *len); + +/** + * @brief int → 十进制字符串,右对齐前补零 + * @param width 最小宽度(不足补 0,如 width=4 val=255 → "0255") + */ char *func_dec2str(char *buf, int sz, int val, int width); + +/** int → 十六进制字符串(width=0 时不补零) */ char *func_hex2str(char *buf, int sz, int val, int width); + +/** float → 字符串 */ char *func_float2str(char *buf, int sz, float f, int width); + +/** 字节数组 → 十六进制字符串(如 {0x01,0xFF} → "01FF") */ char *func_buf2str(char *buf, int sz, const char *src, int n); + +/** 字符串按空格分割为 argc/argv */ int func_str2argv(char *s, uint8_t *argc, char *argv[], uint8_t max); -/* ===== 时间 ===== */ +/* ================================================================ + * 时间处理函数 + * ================================================================ */ + +/** 系统时间 → "YYYY-MM-DD HH:MM:SS.mmm" 格式字符串 */ char *func_time_sys2str(char *buf, int sz, stru_time_sys *t); + +/** 字符串 → 系统时间,解析失败返回 -1 */ int func_str2time_sys(const char *s, stru_time_sys *t); + +/** 获取当前时间(type: 1=stru_time_sys, 2=stru_time_cal) */ int func_get_time(void *t, int type); + +/** 设置系统时间(需要 root 权限) */ int func_set_time(void *t, int type); + +/** 系统时间 → 日历时间 */ void func_time_sys2cal(stru_time_sys *src, stru_time_cal *dst); + +/** 日历时间 → 系统时间 */ void func_time_cal2sys(stru_time_cal *src, stru_time_sys *dst); -/* ===== 文件目录 ===== */ +/* ================================================================ + * 文件目录操作函数 + * ================================================================ */ + +/** 判断目录是否存在(返回 1=存在, 0=不存在) */ int func_dir_exist(const char *p); + +/** 递归创建目录 */ int func_make_dirs(const char *p); + +/** 递归删除目录(含所有内容) */ int func_del_dirs(const char *p); + +/** 通过系统命令删除目录(rm -rf) */ int func_del_dirs_cmd(const char *p); + +/** 判断文件是否存在(返回 1=存在, 0=不存在) */ int func_file_exist(const char *p); + +/** 获取文件大小(字节),失败返回 0 */ uint32_t func_file_size(const char *p); + +/** 读取文件到预分配缓冲区,out 返回实际读取字节数 */ int func_read_file(const char *p, char *buf, uint32_t cap, uint32_t *out); + +/** 读取文件到动态分配缓冲区(调用者负责 free) */ uint8_t *func_read_file_alloc(const char *p, uint32_t *out); + +/** 写缓冲区到文件(覆盖模式) */ int func_write_file(const char *p, const char *buf, uint32_t n); + +/** + * @brief 读取结构化的文件描述头 + * @param check 1=校验 flag 和 CRC,0=直接读取 + */ int func_read_file_info(const char *p, stru_file_info *info, int check); -/* ===== 进程 ===== */ +/* ================================================================ + * 进程管理函数(基于 /proc 文件系统) + * ================================================================ */ + +/** 获取当前进程名称 */ int func_proc_self_name(char *buf, uint32_t sz); + +/** 通过 PID 获取进程名称 */ int func_proc_name_by_pid(int pid, char *buf, uint32_t sz); + +/** 获取当前进程 PID */ int func_proc_pid(void); + +/** 通过进程名称获取 PID(返回 -1 表示未找到) */ int func_proc_pid_by_name(const char *name); + +/** 通过 PID 判断进程是否存在 */ int func_proc_exist_by_pid(int pid); + +/** 通过名称判断进程是否存在 */ int func_proc_exist_by_name(const char *name); + +/** 通过 PID 获取进程可执行文件路径 */ int func_proc_path_by_pid(int pid, char *buf, uint32_t sz); + +/** 获取当前进程可执行文件路径 */ int func_proc_self_path(char *buf, uint32_t sz); + +/** 获取当前进程所在目录 */ int func_proc_self_dir(char *buf, uint32_t sz); -/* ===== 环境变量路径 ===== */ +/* ================================================================ + * 环境变量路径函数 + * ================================================================ */ + +/** 获取工作目录路径(环境变量 WORK_PATH) */ int func_get_work_path(char *buf, uint32_t sz); + +/** 获取系统配置路径(SYS_CFG_PATH) */ int func_get_syscfg_path(char *buf, uint32_t sz); + +/** 获取应用根路径(APP_ROOT_PATH) */ int func_get_app_root(char *buf, uint32_t sz); + +/** 获取历史数据根路径(HIS_ROOT_PATH) */ int func_get_his_root(char *buf, uint32_t sz); + +/** 获取日志根路径(LOG_ROOT_PATH) */ int func_get_log_root(char *buf, uint32_t sz); + +/** 获取数据库根路径(DBC_ROOT_PATH) */ int func_get_dbc_root(char *buf, uint32_t sz); + +/** 获取脚本路径(SHELL_PATH) */ int func_get_shell_path(char *buf, uint32_t sz); + +/** 获取更新包路径(UPDATE_PATH) */ int func_get_update_path(char *buf, uint32_t sz); + +/** 拼接应用路径:APP_ROOT_PATH/app_name */ int func_get_app_path(const char *app, char *buf, uint32_t sz); + +/** 拼接应用配置路径:APP_ROOT_PATH/app_name/ext */ int func_get_app_cfg(const char *app, const char *ext, char *buf, uint32_t sz); + +/** 拼接应用历史路径:HIS_ROOT_PATH/app_name */ int func_get_app_his(const char *app, char *buf, uint32_t sz); + +/** 拼接应用日志路径:LOG_ROOT_PATH/app_name */ int func_get_app_log(const char *app, char *buf, uint32_t sz); -/* ===== IPC 消息队列 ===== */ +/* ================================================================ + * IPC 消息队列函数(System V 消息队列) + * ================================================================ */ + +/** 按进程名创建消息队列 */ int func_ipc_create_by_name(char *name, int *qid); + +/** 按 PID 创建消息队列 */ int func_ipc_create_by_pid(int pid, int *qid); + +/** 按进程名获取消息队列 ID(不存在则创建) */ int func_ipc_get_by_name(const char *name, int *qid); + +/** 按 PID 获取消息队列 ID(不存在则创建) */ int func_ipc_get_by_pid(int pid, int *qid); + +/** 发送原始字节到消息队列 */ int func_ipc_send_buf(int qid, uint8_t *tx, uint32_t n); + +/** 从消息队列接收原始字节 */ int func_ipc_recv_buf(int qid, uint8_t *rx, uint32_t n); + +/** 发送结构体消息 */ int func_ipc_send_msg(int qid, stru_ipc_msg *m); + +/** 接收结构体消息 */ int func_ipc_recv_msg(int qid, stru_ipc_msg *m); + +/** 删除消息队列 */ int func_ipc_delete(int qid); #ifdef __cplusplus diff --git a/release/inc/myLog.h b/release/inc/myLog.h index d7b3d42..c6aabdc 100644 --- a/release/inc/myLog.h +++ b/release/inc/myLog.h @@ -1,6 +1,9 @@ /** * @file myLog.h * @brief 异步日志系统接口(纯 C) + * @details 提供 INFO/ERROR 两级异步日志,非阻塞写入。 + * 支持控制台彩色输出、文件输出、文件按大小轮转。 + * 首次调用 log_prt 时自动初始化,无需手动 init。 */ #ifndef _MY_LOG_H_ @@ -11,24 +14,52 @@ extern "C" { #endif +/** 日志级别:INFO(普通信息) */ #define LOG_INFO 0 +/** 日志级别:ERROR(错误信息,控制台红色输出) */ #define LOG_ERROR 1 -/** 初始化(首次调用 log_prt 时自动触发,也可手动调用) */ +/** + * @brief 手动初始化日志系统(可选,log_prt 自动懒初始化) + * @param dir 日志文件目录路径,传 NULL 则默认 /tmp/logs + * @return 0 成功 + */ int log_init(const char *dir); -/** 核心日志函数(非阻塞,消息入队后立即返回) */ +/** + * @brief 核心日志打印函数(非阻塞,消息入队列后立即返回) + * @param level 日志级别 LOG_INFO 或 LOG_ERROR + * @param file 源文件名(通常传 __FILE__) + * @param func 函数名(通常传 __FUNCTION__) + * @param line 行号(通常传 __LINE__) + * @param fmt printf 风格格式化字符串 + * @param ... 可变参数 + */ void log_prt(uint32_t level, const char *file, const char *func, uint32_t line, const char *fmt, ...); -void log_file_on(void); /* 开启文件写入 */ -void log_file_off(void); /* 关闭文件写入 */ -void log_console_on(void); /* 开启控制台输出 */ -void log_console_off(void); /* 关闭控制台输出 */ -void log_flush(void); /* 等待队列清空 */ -void log_cleanup(void); /* 停止线程、释放资源 */ +/** 开启日志文件写入 */ +void log_file_on(void); +/** 关闭日志文件写入 */ +void log_file_off(void); + +/** 开启控制台彩色输出 */ +void log_console_on(void); + +/** 关闭控制台输出 */ +void log_console_off(void); + +/** 等待异步队列清空(阻塞直到所有消息写入完成) */ +void log_flush(void); + +/** 停止工作线程、关闭文件、释放消息池内存 */ +void log_cleanup(void); + +/** INFO 级别日志宏 */ #define LOG_I(fmt, ...) log_prt(LOG_INFO, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) + +/** ERROR 级别日志宏 */ #define LOG_E(fmt, ...) log_prt(LOG_ERROR, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) #ifdef __cplusplus diff --git a/src/public/libfunc/inc/myFunc.h b/src/public/libfunc/inc/myFunc.h index 6cd97bb..15d3cde 100644 --- a/src/public/libfunc/inc/myFunc.h +++ b/src/public/libfunc/inc/myFunc.h @@ -1,6 +1,8 @@ /** * @file myFunc.h - * @brief 基础工具函数 — 校验/字符串/时间/文件/进程/环境/IPC + * @brief 基础工具函数接口 — 65 个函数,7 大分类 + * @details 校验计算 / 字符串转换 / 时间处理 / 文件目录操作 / + * 进程管理 / 环境变量路径 / IPC 消息队列 */ #ifndef _MY_FUNC_H_ @@ -13,141 +15,306 @@ extern "C" { #endif -/* ---- 系统时间 ---- */ +/** + * @brief 系统时间结构体(位域存储,用于嵌入式装置内部表示) + * @note year: 7bit(0-127+1900), month:4bit(1-12), day:5bit(1-31) + * hour:5bit(0-23), min:6bit(0-59), ms:16bit(0-59999) + */ typedef struct { - uint32_t ms : 16; - uint32_t min : 6; - uint32_t : 2; - uint32_t hour: 5; - uint32_t : 3; - uint32_t day : 5; - uint32_t week: 3; - uint32_t month:4; - uint32_t : 4; - uint32_t year: 7; - uint32_t : 1; + uint32_t ms : 16; /* 毫秒(实际存储秒*1000+毫秒,范围 0-59999) */ + uint32_t min : 6; /* 分钟 0-59 */ + uint32_t : 2; /* 保留 */ + uint32_t hour: 5; /* 小时 0-23 */ + uint32_t : 3; /* 保留 */ + uint32_t day : 5; /* 日期 1-31 */ + uint32_t week: 3; /* 星期 */ + uint32_t month:4; /* 月份 1-12 */ + uint32_t : 4; /* 保留 */ + uint32_t year: 7; /* 年份偏移(实际值-1900),范围 0-127 */ + uint32_t : 1; /* 保留 */ } stru_time_sys; -/* ---- 日历时间 ---- */ +/** + * @brief 日历时间结构体(Unix 时间 + 微秒) + */ typedef struct { - time_t sec; - time_t usec; + time_t sec; /* Unix 时间戳(秒) */ + time_t usec; /* 微秒部分 */ } stru_time_cal; -/* ---- IPC 消息文本 ---- */ +/** + * @brief IPC 消息文本载荷 + */ typedef struct { - int qid; - int type; - int len; - char text[4096]; + int qid; /* 响应队列 ID */ + int type; /* 响应类型(enum_ipc_resp) */ + int len; /* 有效数据长度 */ + char text[4096]; /* 消息内容 */ } stru_ipc_text; -/* ---- IPC 响应类型 ---- */ +/** + * @brief IPC 响应类型 + */ typedef enum { - IPC_RESP_NONE = 1, - IPC_RESP_ONCE, - IPC_RESP_ALWAYS + IPC_RESP_NONE = 1, /* 无需响应 */ + IPC_RESP_ONCE, /* 单次响应 */ + IPC_RESP_ALWAYS /* 持续响应 */ } enum_ipc_resp; -/* ---- IPC 消息 ---- */ +/** + * @brief IPC 消息(System V 消息队列格式) + */ typedef struct { - long id; - stru_ipc_text t; + long id; /* 消息类型 ID */ + stru_ipc_text t; /* 消息载荷 */ } stru_ipc_msg; -/* ---- 文件描述头(256字节) ---- */ +/** + * @brief 文件描述头结构(256 字节,含 CRC 校验) + * @note flag 魔数为 0xA55AA55AA55AA55A + */ #pragma pack(1) typedef struct { - uint64_t flag; - char ver[16]; - char author[16]; - char modify[32]; - uint16_t crc; - char desc[128]; - char bak[54]; + uint64_t flag; /* 魔数标识 0xA55AA55AA55AA55A */ + char ver[16]; /* 版本号 */ + char author[16]; /* 作者 */ + char modify[32]; /* 修改时间 YYYY-MM-DD HH-MM-SS */ + uint16_t crc; /* CRC16 校验 */ + char desc[128]; /* 附加描述 */ + char bak[54]; /* 对齐填充(总计 256 字节) */ } stru_file_info; #pragma pack() -/* ===== 校验 ===== */ +/* ================================================================ + * 校验计算函数(CRC16 使用 Modbus 多项式 0xA001) + * ================================================================ */ + +/** 8 位累加校验和 */ uint8_t func_cal_sum_byte(const uint8_t *d, uint32_t n); + +/** 16 位累加校验和 */ uint16_t func_cal_sum_word(const uint8_t *d, uint32_t n); + +/** CRC16(Modbus 多项式,初始值 0xFFFF) */ uint16_t func_cal_crc16(const uint8_t *d, uint32_t n); + +/** CRC32(标准多项式,初始值 0xFFFFFFFF) */ uint32_t func_cal_crc32(const uint8_t *d, uint32_t n); + +/** 文件 CRC16(4KB 缓冲区流式计算) */ uint16_t func_cal_file_crc16(const char *path); + +/** 文件 CRC32(4KB 缓冲区流式计算) */ uint32_t func_cal_file_crc32(const char *path); -/* ===== 字符串转换 ===== */ +/* ================================================================ + * 字符串转换函数 + * ================================================================ */ + +/** 单个十六进制字符 → 数值('F'→15) */ uint8_t func_hex_ch(char c); + +/** 单个十进制字符 → 数值('9'→9) */ uint8_t func_dec_ch(char c); + +/** 十六进制字符串 "FF" → int 255 */ int func_hex2dec(char *s); + +/** 十进制字符串 "255" → int 255(支持负号) */ int func_dec2dec(char *s); + +/** + * @brief 十六进制字符串 → 字节缓冲区 + * @param s 源字符串(如 "0102FF") + * @param dst 目标缓冲区 + * @param len [out]实际字节数 + * @return 0 成功,-1 奇数长度 + */ int func_hex2buf(const char *s, char *dst, int *len); + +/** + * @brief int → 十进制字符串,右对齐前补零 + * @param width 最小宽度(不足补 0,如 width=4 val=255 → "0255") + */ char *func_dec2str(char *buf, int sz, int val, int width); + +/** int → 十六进制字符串(width=0 时不补零) */ char *func_hex2str(char *buf, int sz, int val, int width); + +/** float → 字符串 */ char *func_float2str(char *buf, int sz, float f, int width); + +/** 字节数组 → 十六进制字符串(如 {0x01,0xFF} → "01FF") */ char *func_buf2str(char *buf, int sz, const char *src, int n); + +/** 字符串按空格分割为 argc/argv */ int func_str2argv(char *s, uint8_t *argc, char *argv[], uint8_t max); -/* ===== 时间 ===== */ +/* ================================================================ + * 时间处理函数 + * ================================================================ */ + +/** 系统时间 → "YYYY-MM-DD HH:MM:SS.mmm" 格式字符串 */ char *func_time_sys2str(char *buf, int sz, stru_time_sys *t); + +/** 字符串 → 系统时间,解析失败返回 -1 */ int func_str2time_sys(const char *s, stru_time_sys *t); + +/** 获取当前时间(type: 1=stru_time_sys, 2=stru_time_cal) */ int func_get_time(void *t, int type); + +/** 设置系统时间(需要 root 权限) */ int func_set_time(void *t, int type); + +/** 系统时间 → 日历时间 */ void func_time_sys2cal(stru_time_sys *src, stru_time_cal *dst); + +/** 日历时间 → 系统时间 */ void func_time_cal2sys(stru_time_cal *src, stru_time_sys *dst); -/* ===== 文件目录 ===== */ +/* ================================================================ + * 文件目录操作函数 + * ================================================================ */ + +/** 判断目录是否存在(返回 1=存在, 0=不存在) */ int func_dir_exist(const char *p); + +/** 递归创建目录 */ int func_make_dirs(const char *p); + +/** 递归删除目录(含所有内容) */ int func_del_dirs(const char *p); + +/** 通过系统命令删除目录(rm -rf) */ int func_del_dirs_cmd(const char *p); + +/** 判断文件是否存在(返回 1=存在, 0=不存在) */ int func_file_exist(const char *p); + +/** 获取文件大小(字节),失败返回 0 */ uint32_t func_file_size(const char *p); + +/** 读取文件到预分配缓冲区,out 返回实际读取字节数 */ int func_read_file(const char *p, char *buf, uint32_t cap, uint32_t *out); + +/** 读取文件到动态分配缓冲区(调用者负责 free) */ uint8_t *func_read_file_alloc(const char *p, uint32_t *out); + +/** 写缓冲区到文件(覆盖模式) */ int func_write_file(const char *p, const char *buf, uint32_t n); + +/** + * @brief 读取结构化的文件描述头 + * @param check 1=校验 flag 和 CRC,0=直接读取 + */ int func_read_file_info(const char *p, stru_file_info *info, int check); -/* ===== 进程 ===== */ +/* ================================================================ + * 进程管理函数(基于 /proc 文件系统) + * ================================================================ */ + +/** 获取当前进程名称 */ int func_proc_self_name(char *buf, uint32_t sz); + +/** 通过 PID 获取进程名称 */ int func_proc_name_by_pid(int pid, char *buf, uint32_t sz); + +/** 获取当前进程 PID */ int func_proc_pid(void); + +/** 通过进程名称获取 PID(返回 -1 表示未找到) */ int func_proc_pid_by_name(const char *name); + +/** 通过 PID 判断进程是否存在 */ int func_proc_exist_by_pid(int pid); + +/** 通过名称判断进程是否存在 */ int func_proc_exist_by_name(const char *name); + +/** 通过 PID 获取进程可执行文件路径 */ int func_proc_path_by_pid(int pid, char *buf, uint32_t sz); + +/** 获取当前进程可执行文件路径 */ int func_proc_self_path(char *buf, uint32_t sz); + +/** 获取当前进程所在目录 */ int func_proc_self_dir(char *buf, uint32_t sz); -/* ===== 环境变量路径 ===== */ +/* ================================================================ + * 环境变量路径函数 + * ================================================================ */ + +/** 获取工作目录路径(环境变量 WORK_PATH) */ int func_get_work_path(char *buf, uint32_t sz); + +/** 获取系统配置路径(SYS_CFG_PATH) */ int func_get_syscfg_path(char *buf, uint32_t sz); + +/** 获取应用根路径(APP_ROOT_PATH) */ int func_get_app_root(char *buf, uint32_t sz); + +/** 获取历史数据根路径(HIS_ROOT_PATH) */ int func_get_his_root(char *buf, uint32_t sz); + +/** 获取日志根路径(LOG_ROOT_PATH) */ int func_get_log_root(char *buf, uint32_t sz); + +/** 获取数据库根路径(DBC_ROOT_PATH) */ int func_get_dbc_root(char *buf, uint32_t sz); + +/** 获取脚本路径(SHELL_PATH) */ int func_get_shell_path(char *buf, uint32_t sz); + +/** 获取更新包路径(UPDATE_PATH) */ int func_get_update_path(char *buf, uint32_t sz); + +/** 拼接应用路径:APP_ROOT_PATH/app_name */ int func_get_app_path(const char *app, char *buf, uint32_t sz); + +/** 拼接应用配置路径:APP_ROOT_PATH/app_name/ext */ int func_get_app_cfg(const char *app, const char *ext, char *buf, uint32_t sz); + +/** 拼接应用历史路径:HIS_ROOT_PATH/app_name */ int func_get_app_his(const char *app, char *buf, uint32_t sz); + +/** 拼接应用日志路径:LOG_ROOT_PATH/app_name */ int func_get_app_log(const char *app, char *buf, uint32_t sz); -/* ===== IPC 消息队列 ===== */ +/* ================================================================ + * IPC 消息队列函数(System V 消息队列) + * ================================================================ */ + +/** 按进程名创建消息队列 */ int func_ipc_create_by_name(char *name, int *qid); + +/** 按 PID 创建消息队列 */ int func_ipc_create_by_pid(int pid, int *qid); + +/** 按进程名获取消息队列 ID(不存在则创建) */ int func_ipc_get_by_name(const char *name, int *qid); + +/** 按 PID 获取消息队列 ID(不存在则创建) */ int func_ipc_get_by_pid(int pid, int *qid); + +/** 发送原始字节到消息队列 */ int func_ipc_send_buf(int qid, uint8_t *tx, uint32_t n); + +/** 从消息队列接收原始字节 */ int func_ipc_recv_buf(int qid, uint8_t *rx, uint32_t n); + +/** 发送结构体消息 */ int func_ipc_send_msg(int qid, stru_ipc_msg *m); + +/** 接收结构体消息 */ int func_ipc_recv_msg(int qid, stru_ipc_msg *m); + +/** 删除消息队列 */ int func_ipc_delete(int qid); #ifdef __cplusplus diff --git a/src/public/liblog/inc/myLog.h b/src/public/liblog/inc/myLog.h index d7b3d42..c6aabdc 100644 --- a/src/public/liblog/inc/myLog.h +++ b/src/public/liblog/inc/myLog.h @@ -1,6 +1,9 @@ /** * @file myLog.h * @brief 异步日志系统接口(纯 C) + * @details 提供 INFO/ERROR 两级异步日志,非阻塞写入。 + * 支持控制台彩色输出、文件输出、文件按大小轮转。 + * 首次调用 log_prt 时自动初始化,无需手动 init。 */ #ifndef _MY_LOG_H_ @@ -11,24 +14,52 @@ extern "C" { #endif +/** 日志级别:INFO(普通信息) */ #define LOG_INFO 0 +/** 日志级别:ERROR(错误信息,控制台红色输出) */ #define LOG_ERROR 1 -/** 初始化(首次调用 log_prt 时自动触发,也可手动调用) */ +/** + * @brief 手动初始化日志系统(可选,log_prt 自动懒初始化) + * @param dir 日志文件目录路径,传 NULL 则默认 /tmp/logs + * @return 0 成功 + */ int log_init(const char *dir); -/** 核心日志函数(非阻塞,消息入队后立即返回) */ +/** + * @brief 核心日志打印函数(非阻塞,消息入队列后立即返回) + * @param level 日志级别 LOG_INFO 或 LOG_ERROR + * @param file 源文件名(通常传 __FILE__) + * @param func 函数名(通常传 __FUNCTION__) + * @param line 行号(通常传 __LINE__) + * @param fmt printf 风格格式化字符串 + * @param ... 可变参数 + */ void log_prt(uint32_t level, const char *file, const char *func, uint32_t line, const char *fmt, ...); -void log_file_on(void); /* 开启文件写入 */ -void log_file_off(void); /* 关闭文件写入 */ -void log_console_on(void); /* 开启控制台输出 */ -void log_console_off(void); /* 关闭控制台输出 */ -void log_flush(void); /* 等待队列清空 */ -void log_cleanup(void); /* 停止线程、释放资源 */ +/** 开启日志文件写入 */ +void log_file_on(void); +/** 关闭日志文件写入 */ +void log_file_off(void); + +/** 开启控制台彩色输出 */ +void log_console_on(void); + +/** 关闭控制台输出 */ +void log_console_off(void); + +/** 等待异步队列清空(阻塞直到所有消息写入完成) */ +void log_flush(void); + +/** 停止工作线程、关闭文件、释放消息池内存 */ +void log_cleanup(void); + +/** INFO 级别日志宏 */ #define LOG_I(fmt, ...) log_prt(LOG_INFO, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) + +/** ERROR 级别日志宏 */ #define LOG_E(fmt, ...) log_prt(LOG_ERROR, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) #ifdef __cplusplus diff --git a/src/public/liblog/src/myLog.c b/src/public/liblog/src/myLog.c index ead8d2c..74631fd 100644 --- a/src/public/liblog/src/myLog.c +++ b/src/public/liblog/src/myLog.c @@ -22,112 +22,155 @@ #include /* ========== 可配置参数 ========== */ + +/** 消息池预分配大小 */ #define POOL_SIZE 10000 +/** 单次批量处理消息数上限 */ #define BATCH_MAX 100 +/** 空闲等待间隔(毫秒) */ #define FLUSH_MS 100 +/** 单日志文件最大 10MB */ #define FILE_MAX_SIZE (10 * 1024 * 1024) +/** 保留历史文件数 */ #define FILE_MAX_KEEP 5 +/** 单条日志内容上限 4KB */ #define CONTENT_MAX 4096 /* ========== 数据结构 ========== */ -/** 单条日志消息 */ +/** + * @brief 单条日志消息(消息池节点 + 队列节点合一) + */ typedef struct { - uint32_t level; - char time_str[64]; - char src_info[128]; /* "文件名/函数:行号" */ - char content[CONTENT_MAX]; - struct log_msg *next; /* 空闲链表 / 队列链表 */ + uint32_t level; /* 日志级别 LOG_INFO/LOG_ERROR */ + char time_str[64]; /* 时间戳 YYYY-MM-DD HH:MM:SS.mmm */ + char src_info[128]; /* "文件名/函数:行号" */ + char content[CONTENT_MAX]; /* 格式化后的日志内容 */ + struct log_msg *next; /* 空闲链表/队列链表指针 */ } stru_log_msg; -/** 线程安全队列 */ +/** + * @brief 线程安全消息队列(生产者-消费者模式) + */ typedef struct { - stru_log_msg *head; - stru_log_msg *tail; - int count; - pthread_mutex_t lock; - pthread_cond_t cond_pop; - pthread_cond_t cond_push; + stru_log_msg *head; /* 队头 */ + stru_log_msg *tail; /* 队尾 */ + int count; /* 当前消息数 */ + pthread_mutex_t lock; /* 互斥锁 */ + pthread_cond_t cond_pop; /* 消费者等待条件 */ + pthread_cond_t cond_push; /* 生产者等待条件(队列满) */ } stru_msg_queue; -/** 日志系统全局状态 */ +/** + * @brief 日志系统全局状态 + */ typedef struct { - stru_msg_queue q; - pthread_t worker; - volatile int running; - FILE *fp; - long file_bytes; - int to_file; - int to_console; - char dir[256]; + stru_msg_queue q; /* 消息队列 */ + pthread_t worker; /* 工作线程 */ + volatile int running; /* 线程运行标志 */ + FILE *fp; /* 当前日志文件句柄 */ + long file_bytes; /* 当前文件已写入字节数 */ + int to_file; /* 文件输出开关 */ + int to_console; /* 控制台输出开关 */ + char dir[256]; /* 日志目录路径 */ } stru_log_state; LOCAL stru_log_state g_log; /* ========== 消息池(预分配,无运行时 malloc) ========== */ -LOCAL stru_log_msg *g_pool_free; -LOCAL stru_log_msg g_pool_buf[POOL_SIZE]; + +LOCAL stru_log_msg *g_pool_free; /* 空闲链表头 */ +LOCAL stru_log_msg g_pool_buf[POOL_SIZE]; /* 预分配数组 */ LOCAL pthread_mutex_t g_pool_lock = PTHREAD_MUTEX_INITIALIZER; LOCAL pthread_once_t g_init_once = PTHREAD_ONCE_INIT; -/** 初始化消息池:把数组元素链成空闲链表 */ +/** + * @brief 初始化消息池:把预分配数组链成空闲链表 + * @detail 只在首次调用时执行一次(由 global_init 调用) + */ LOCAL void pool_init(void) { pthread_mutex_lock(&g_pool_lock); + if (g_pool_free != NULL) { pthread_mutex_unlock(&g_pool_lock); + return; } + + /* 链成空闲链表 */ for (int i = 0; i < POOL_SIZE - 1; i++) { g_pool_buf[i].next = &g_pool_buf[i + 1]; } + g_pool_buf[POOL_SIZE - 1].next = NULL; g_pool_free = &g_pool_buf[0]; + pthread_mutex_unlock(&g_pool_lock); } -/** 从池中取一个空闲消息块(失败返回 NULL) */ +/** + * @brief 从池中取一个空闲消息块 + * @return 成功返回指针,池耗尽返回 NULL + */ LOCAL stru_log_msg *pool_alloc(void) { pthread_mutex_lock(&g_pool_lock); + stru_log_msg *m = g_pool_free; + if (m) { g_pool_free = m->next; } + pthread_mutex_unlock(&g_pool_lock); + return m; } -/** 归还消息块到池 */ +/** + * @brief 归还消息块到池 + */ LOCAL void pool_free(stru_log_msg *m) { if (!m) { return; } + pthread_mutex_lock(&g_pool_lock); + m->next = g_pool_free; g_pool_free = m; + pthread_mutex_unlock(&g_pool_lock); } /* ========== 队列操作 ========== */ -/** 入队(满则阻塞) */ +/** + * @brief 入队(生产者端) + * @detail 队列满时阻塞等待消费者腾出空间 + */ LOCAL void q_push(stru_log_msg *m) { pthread_mutex_lock(&g_log.q.lock); + + /* 队列满则等待 */ while (g_log.q.count >= POOL_SIZE) { pthread_cond_wait(&g_log.q.cond_push, &g_log.q.lock); } + m->next = NULL; + + /* 尾插 */ if (!g_log.q.head) { g_log.q.head = m; @@ -136,40 +179,59 @@ LOCAL void q_push(stru_log_msg *m) { g_log.q.tail->next = m; } + g_log.q.tail = m; g_log.q.count++; + pthread_cond_signal(&g_log.q.cond_pop); pthread_mutex_unlock(&g_log.q.lock); } -/** 出队(空则等待或返回 NULL) */ +/** + * @brief 出队(消费者端) + * @detail 队列空且线程运行中时阻塞等待 + * @return 成功返回消息指针,线程停止返回 NULL + */ LOCAL stru_log_msg *q_pop(void) { pthread_mutex_lock(&g_log.q.lock); + while (g_log.q.count == 0 && g_log.running) { pthread_cond_wait(&g_log.q.cond_pop, &g_log.q.lock); } + if (g_log.q.count == 0) { pthread_mutex_unlock(&g_log.q.lock); + return NULL; } + + /* 头摘 */ stru_log_msg *m = g_log.q.head; + g_log.q.head = m->next; + if (!g_log.q.head) { g_log.q.tail = NULL; } + g_log.q.count--; + pthread_cond_signal(&g_log.q.cond_push); pthread_mutex_unlock(&g_log.q.lock); + return m; } /* ========== 文件管理 ========== */ -/** 拼接日志文件路径:dir/app.log 或 dir/app.log.N */ +/** + * @brief 拼接日志文件路径 + * @param idx 0=当前文件(dir/app.log), N>=1=历史文件(dir/app.log.N) + */ LOCAL void file_path(char *buf, size_t sz, int idx) { if (idx == 0) @@ -182,38 +244,49 @@ LOCAL void file_path(char *buf, size_t sz, int idx) } } -/** 创建日志目录 */ +/** + * @brief 确保日志目录存在,不存在则创建 + * @return 0 成功 + */ LOCAL int ensure_dir(void) { if (!g_log.dir[0]) { + /* 默认放到 /tmp/logs */ snprintf(g_log.dir, sizeof(g_log.dir), "/tmp/logs"); } + struct stat st; + if (stat(g_log.dir, &st) != 0) { return mkdir(g_log.dir, 0755); } + return 0; } -/** 轮转日志文件:app.log→.1→.2→...→.N(max),删除最旧的 */ +/** + * @brief 轮转日志文件 + * @detail app.log→app.log.1→app.log.2→...→app.log.N(max)→删除 + */ LOCAL void rotate_files(void) { char old[512]; char neo[512]; + /* 关闭当前文件 */ if (g_log.fp) { fclose(g_log.fp); g_log.fp = NULL; } - /* 删除最旧 */ + /* 删除最旧的历史文件 */ file_path(old, sizeof(old), FILE_MAX_KEEP); remove(old); - /* 依次后移 */ + /* 历史文件依次后移一位 */ for (int i = FILE_MAX_KEEP - 1; i >= 1; i--) { file_path(old, sizeof(old), i); @@ -221,25 +294,32 @@ LOCAL void rotate_files(void) rename(old, neo); } - /* 当前文件 → .1 */ + /* 当前文件重命名为 .1 */ file_path(old, sizeof(old), 0); file_path(neo, sizeof(neo), 1); rename(old, neo); + g_log.file_bytes = 0; } -/** 打开当前日志文件 */ +/** + * @brief 打开当前日志文件(追加模式,行缓冲) + * @return 成功返回 FILE*, 失败返回 NULL + */ LOCAL FILE *open_file(void) { if (ensure_dir() != 0) { return NULL; } + char path[512]; + file_path(path, sizeof(path), 0); - /* 检查已有文件大小 */ + /* 检查已有文件大小,超出则先轮转 */ struct stat st; + if (stat(path, &st) == 0) { if (st.st_size > FILE_MAX_SIZE) @@ -253,21 +333,28 @@ LOCAL FILE *open_file(void) } FILE *fp = fopen(path, "a"); + if (fp) { - setlinebuf(fp); + setlinebuf(fp); /* 行缓冲模式 */ } + return fp; } /* ========== 格式化 ========== */ -/** 当前时间字符串:YYYY-MM-DD HH:MM:SS.mmm */ +/** + * @brief 当前时间戳字符串 "YYYY-MM-DD HH:MM:SS.mmm" + */ LOCAL void fmt_time(char *buf, size_t sz) { struct timeval tv; + gettimeofday(&tv, NULL); + struct tm *t = localtime(&tv.tv_sec); + if (t) { snprintf(buf, sz, "%04d-%02d-%02d %02d:%02d:%02d.%03d", @@ -281,70 +368,91 @@ LOCAL void fmt_time(char *buf, size_t sz) } } -/** 提取文件名(去掉路径) 并拼接成 "file/func:line" */ +/** + * @brief 格式化文件来源信息 "文件名/函数名:行号" + * @detail 自动去除文件路径前缀,仅保留文件名 + */ LOCAL void fmt_src(char *buf, size_t sz, const char *file, const char *func, uint32_t line) { if (!file || !func) { snprintf(buf, sz, "?:?:0"); + return; } + + /* 提取纯文件名(去掉路径前缀) */ const char *p = strrchr(file, '/'); + if (!p) { p = strrchr(file, '\\'); } + const char *name = p ? p + 1 : file; + snprintf(buf, sz, "%s/%s:%u", name, func, line); } /* ========== 输出 ========== */ -/** 批量写控制台 + 文件 */ +/** + * @brief 批量写入控制台 + 文件 + * @detail 控制台带颜色输出,文件带级别标记 + */ LOCAL void output_batch(stru_log_msg **batch, int cnt) { if (cnt == 0) { return; } + static const char *titles[] = { "INFO", "ERROR" }; static const char *colors[] = { COLOR_WHITE, COLOR_RED }; - /* 控制台(带颜色) */ + /* 控制台彩色输出 */ if (g_log.to_console) { for (int i = 0; i < cnt; i++) { stru_log_msg *m = batch[i]; + printf("%s[%s] [%s%s%s] %s\n", colors[m->level], m->time_str, colors[m->level], titles[m->level], COLOR_RESET, m->content); } + fflush(stdout); } - /* 文件 */ + /* 文件输出 */ if (g_log.to_file) { if (!g_log.fp) { g_log.fp = open_file(); } + if (g_log.fp) { for (int i = 0; i < cnt; i++) { stru_log_msg *m = batch[i]; int n = fprintf(g_log.fp, "[%s] [%s] %s\n", - titles[m->level], m->time_str, m->content); + titles[m->level], + m->time_str, m->content); + if (n > 0) { g_log.file_bytes += n; } } + fflush(g_log.fp); + + /* 超出限制则轮转 */ if (g_log.file_bytes > FILE_MAX_SIZE) { rotate_files(); @@ -355,9 +463,14 @@ LOCAL void output_batch(stru_log_msg **batch, int cnt) /* ========== 工作线程 ========== */ +/** + * @brief 日志消费线程主循环 + * @detail 批量从队列取消息 → 输出 → 归还消息池 + */ LOCAL void *worker_thread(void *arg) { (void)arg; + while (g_log.running) { stru_log_msg *batch[BATCH_MAX]; @@ -367,16 +480,20 @@ LOCAL void *worker_thread(void *arg) for (int i = 0; i < BATCH_MAX; i++) { stru_log_msg *m = q_pop(); + if (!m) { break; } + batch[cnt++] = m; } if (cnt > 0) { output_batch(batch, cnt); + + /* 归还消息块到池 */ for (int i = 0; i < cnt; i++) { pool_free(batch[i]); @@ -384,45 +501,67 @@ LOCAL void *worker_thread(void *arg) } else { + /* 队列空,休眠等待 */ usleep(FLUSH_MS * 1000); } } + return NULL; } /* ========== 全局初始化(pthread_once) ========== */ +/** + * @brief 全局初始化(由 pthread_once 确保只执行一次) + * @detail 初始化消息池 + 启动工作线程 + */ LOCAL void global_init(void) { pool_init(); - /* g_log.q 已在声明时用 {0} 初始化 */ + + /* 状态字段已在声明时用 {0} 初始化 */ g_log.q.head = NULL; g_log.q.tail = NULL; g_log.q.count = 0; g_log.to_console = 1; g_log.to_file = 0; g_log.running = 1; + pthread_create(&g_log.worker, NULL, worker_thread, NULL); } /* ========== 公开 API ========== */ +/** + * @brief 初始化日志系统 + * @param dir 日志文件目录(NULL 则默认 /tmp/logs) + * @return 0 成功 + */ int log_init(const char *dir) { if (dir && dir[0]) { snprintf(g_log.dir, sizeof(g_log.dir), "%s", dir); } + pool_init(); g_log.to_file = 1; + return 0; } +/** + * @brief 核心日志打印(非阻塞) + * @detail 首次调用 pthread_once 自动触发全局初始化。 + * 从消息池获取空闲块 → 填充时间/来源/内容 → 入队。 + * 消息池耗尽时静默丢弃本条日志。 + */ void log_prt(uint32_t level, const char *file, const char *func, uint32_t line, const char *fmt, ...) { - /* 懒初始化 */ + /* 懒初始化:仅首次调用执行 */ pthread_once(&g_init_once, global_init); + if (level > LOG_ERROR || !fmt || !g_log.running) { return; @@ -430,24 +569,33 @@ void log_prt(uint32_t level, const char *file, const char *func, /* 从消息池取空闲块 */ stru_log_msg *m = pool_alloc(); + if (!m) { - return; + return; /* 池耗尽,静默丢弃 */ } + /* 填充消息字段 */ m->level = level; fmt_time(m->time_str, sizeof(m->time_str)); fmt_src(m->src_info, sizeof(m->src_info), file, func, line); + /* 格式化日志内容 */ va_list ap; + va_start(ap, fmt); - int pos = snprintf(m->content, sizeof(m->content), "[%s] ", m->src_info); + + int pos = snprintf(m->content, sizeof(m->content), + "[%s] ", m->src_info); + if (pos < (int)sizeof(m->content)) { vsnprintf(m->content + pos, sizeof(m->content) - pos, fmt, ap); } + va_end(ap); + /* 入队(生产者端) */ q_push(m); } @@ -471,6 +619,9 @@ void log_console_off(void) g_log.to_console = 0; } +/** + * @brief 等待消息队列清空(阻塞直到所有日志写入完成) + */ void log_flush(void) { for (;;) @@ -478,39 +629,57 @@ void log_flush(void) pthread_mutex_lock(&g_log.q.lock); int c = g_log.q.count; pthread_mutex_unlock(&g_log.q.lock); + if (c == 0) { break; } - usleep(10000); + + usleep(10000); /* 10ms 轮询 */ } } +/** + * @brief 停止日志系统并释放所有资源 + * @detail 停止工作线程 → 刷新文件 → 释放队列残留消息 + */ void log_cleanup(void) { if (!g_log.running) { return; } + + /* 通知线程停止 */ g_log.running = 0; pthread_cond_signal(&g_log.q.cond_pop); + + /* 等待线程退出 */ pthread_join(g_log.worker, NULL); + + /* 关闭文件 */ if (g_log.fp) { fclose(g_log.fp); g_log.fp = NULL; } - /* 释放池中残留消息 */ + + /* 释放队列中残留消息 */ pthread_mutex_lock(&g_log.q.lock); + stru_log_msg *cur = g_log.q.head; + while (cur) { stru_log_msg *tmp = cur; + cur = cur->next; pool_free(tmp); } - g_log.q.head = NULL; - g_log.q.tail = NULL; + + g_log.q.head = NULL; + g_log.q.tail = NULL; g_log.q.count = 0; + pthread_mutex_unlock(&g_log.q.lock); }