Linux-实践

1.写日志/写json

main.c

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include "log.h"
#include "common_config.h"

#define INFO_PRINT(fmt, ...) \
    do { \
        fprintf(stderr, "[INFO] " fmt "\n", ##__VA_ARGS__); \
        log_write(LOG_LEVEL_INFO, fmt "\n", ##__VA_ARGS__); \
    } while (0)

#define APP_VERSION "V132.1.01"
// __DATE__ 和 __TIME__ 是编译器预定义宏,会在编译时自动替换为当前日期和时间
#define APP_VERSION_BUILD APP_VERSION "(built " __DATE__ " " __TIME__ ")"

// 全局标志位,用于控制程序退出
volatile int keep_running = 1;

// 2. 模拟信号处理函数
void signal_handler(int sig) {
    printf("\n[Signal Handler] Received signal %d. Exiting gracefully...\n", sig);
    keep_running = 0; // 修改标志位,让主循环退出
}

int main() {
    // 3. 注册信号处理 (对应 main.c 中的 signal 调用)
    signal(SIGINT, signal_handler);  // Ctrl+C
    signal(SIGTERM, signal_handler); // kill 命令

    
    //初始化日志系统
    if(ecu_log_init()<0)
    {
        printf("ERROR: Failed to initialize log system!\n");
        return -1;
    }
    pid_t pid = getpid();  // 获取当前进程的PID
    printf("我的进程号是: %d\n", pid);

        //本地系统参数配置
    if(local_system_init()<0)
        return -1;

    
    // 4. 打印版本信息 (对应 main.c 中的 INFO_PRINT)
    printf("========================================\n");
    INFO_PRINT("Application version: %s\n", APP_VERSION_BUILD);
    printf("========================================\n");
    printf("Program is running. Press Ctrl+C to stop.\n");

    // 5. 模拟主循环
    while (keep_running) {
        // 休眠 1 秒,避免占用过多 CPU,同时等待信号
        sleep(1);
        printf("."); 
        fflush(stdout); // 确保立即输出
    }

    printf("\nMain exited cleanly.\n");
    return 0;
}

实验现象:

1 写日志:

路径/home/aurio/logs/ecu_log

2 写json

路径/home/aurio/config.json

makefile

cpp 复制代码
# Makefile for ECU Project

# Compiler settings
CC = gcc
CFLAGS = -Wall -Wextra -O2 -I. -std=c99 -D_POSIX_C_SOURCE=200809L -DG_DEBUG_ENABLE=1
LDFLAGS = -lpthread -lm -lrt

# Source files
SRCS = cJSON.c common_config.c main.c log.c
OBJS = $(SRCS:.c=.o)
TARGET = ecu_program

# Default target
all: $(TARGET)

# Link executable
$(TARGET): $(OBJS)
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
	@echo "Build completed: $(TARGET)"

# Compile .c files to .o files
%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

# Clean build files
clean:
	rm -f $(OBJS) $(TARGET)
	@echo "Clean completed."

.PHONY: all clean

分析makefile

Makefile 文档分析

概述

这是一个用于构建 ECU(Electronic Control Unit,电子控制单元)项目的 Makefile 文件。它定义了编译规则、源文件、目标文件和构建流程,用于自动化构建名为 ecu_program的可执行程序。

文件结构分析
1. 编译器设置
复制代码
CC = gcc
CFLAGS = -Wall -Wextra -O2 -I. -std=c99 -D_POSIX_C_SOURCE=200809L -DG_DEBUG_ENABLE=1
LDFLAGS = -lpthread -lm -lrt
变量 说明
CC gcc 指定使用 GCC 编译器
CFLAGS -Wall -Wextra -O2 -I. -std=c99 -D_POSIX_C_SOURCE=200809L -DG_DEBUG_ENABLE=1 编译选项: • -Wall -Wextra:启用所有警告和额外警告 • -O2:启用优化级别 2 • -I.:包含当前目录作为头文件搜索路径 • -std=c99:使用 C99 标准 • -D_POSIX_C_SOURCE=200809L:定义 POSIX 特性测试宏 • -DG_DEBUG_ENABLE=1:启用调试输出宏
LDFLAGS -lpthread -lm -lrt 链接选项: • -lpthread:链接 POSIX 线程库 • -lm:链接数学库 • -lrt:链接实时库(用于 clock_gettime等函数)
2. 源文件和目标文件定义
复制代码
SRCS = cJSON.c common_config.c main.c log.c
OBJS = $(SRCS:.c=.o)
TARGET = ecu_program
变量 说明
SRCS cJSON.c common_config.c main.c log.c 源文件列表,包含 4 个 C 源文件
OBJS $(SRCS:.c=.o) 目标文件列表,将 .c后缀替换为 .o
TARGET ecu_program 最终生成的可执行文件名
3. 默认目标
复制代码
all: $(TARGET)
  • 默认目标是 all,依赖于 $(TARGET)

  • 执行 makemake all时,会构建 ecu_program可执行文件

4. 链接可执行文件规则
复制代码
$(TARGET): $(OBJS)
    $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
    @echo "Build completed: $(TARGET)"
元素 说明
目标 $(TARGET)(即 ecu_program
依赖 $(OBJS)(即所有 .o文件)
命令 1. 使用 gcc将所有 .o文件链接成可执行文件 2. 输出构建完成信息
5. 编译规则
复制代码
%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@
元素 说明
目标 %.o(任意 .o文件)
依赖 %.c(对应的同名 .c文件)
命令 编译单个 .c文件为 .o文件
6. 清理规则
复制代码
clean:
    rm -f $(OBJS) $(TARGET)
    @echo "Clean completed."
元素 说明
目标 clean
依赖
命令 1. 删除所有 .o文件和可执行文件 2. 输出清理完成信息
7. 伪目标声明
复制代码
.PHONY: all clean
  • 声明 allclean为伪目标,表示它们不是实际的文件

  • 确保即使存在同名文件,make也会执行这些目标的命令

构建流程
  1. 执行 make时,首先查找默认目标 all

  2. all依赖于$(TARGET)(即ecu_program`)

  3. ecu_program依赖于所有 .o文件

  4. 通过模式规则 %.o: %.c编译每个 .c文件为对应的 .o文件

  5. 将所有 .o文件链接为最终的可执行文件

项目结构

基于 Makefile 中的源文件列表,可以推断项目包含以下模块:

文件 描述
cJSON.c cJSON 库实现,用于 JSON 解析
common_config.c 通用配置管理模块
main.c 主程序入口
log.c 日志系统模块
注意事项
  1. 依赖关系:此 Makefile 使用简单的模式规则,没有自动处理头文件依赖

  2. 并行构建 :此 Makefile 支持并行构建(通过 make -j

  3. 可移植性:使用 POSIX 标准,适合 Linux/Unix 系统

  4. 调试支持 :通过 DG_DEBUG_ENABLE=1启用了调试输出

3.定时器触发

在makefile里面 加入 mytime.c

main.c

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include "log.h"
#include "common_config.h"
#include "mytimer.h"  // 包含新的定时器头文件

#define INFO_PRINT(fmt, ...) \
    do { \
        fprintf(stderr, "[INFO] " fmt "\n", ##__VA_ARGS__); \
        log_write(LOG_LEVEL_INFO, fmt "\n", ##__VA_ARGS__); \
    } while (0)

#define APP_VERSION "V132.1.01"
#define APP_VERSION_BUILD APP_VERSION "(built " __DATE__ " " __TIME__ ")"

// 全局标志位,用于控制程序退出
volatile int keep_running = 1;

// 信号处理函数 - 处理 Ctrl+C 和 kill 命令
void signal_handler(int sig) {
    printf("\n[Signal Handler] Received signal %d. Exiting gracefully...\n", sig);
    log_write(LOG_LEVEL_INFO, "Received signal %d. Exiting gracefully...\n", sig);
    
    // 停止定时器
    if (sig == SIGINT || sig == SIGTERM) {
        stop_timer();
    }
    
    keep_running = 0; // 修改标志位,让主循环退出
}

int main() {
    pid_t pid = getpid();  // 获取当前进程的PID
    
    // 注册信号处理函数
    signal(SIGINT, signal_handler);  // Ctrl+C
    signal(SIGTERM, signal_handler); // kill 命令
    
    // 初始化日志系统
    if (ecu_log_init() < 0) {
        printf("ERROR: Failed to initialize log system!\n");
        return -1;
    }
    
    printf("我的进程号是: %d\n", pid);
    log_write(LOG_LEVEL_INFO, "Process started with PID: %d\n", pid);
    
    // 本地系统参数配置 json文件
    if (local_system_init() < 0) {
        return -1;
    }
    
    // 初始化定时器信号处理
    if (init_timer_signal() < 0) {
        printf("ERROR: Failed to initialize timer signal handler!\n");
        log_write(LOG_LEVEL_ERROR, "Failed to initialize timer signal handler\n");
        return -1;
    }
    
    #if 0
    // 设置定时器:3秒后触发 就触发一次
    if (setup_timer(3, 0) < 0) {
        printf("ERROR: Failed to setup timer!\n");
        log_write(LOG_LEVEL_ERROR, "Failed to setup timer\n");
        return -1;
    }
    #endif
    

    // 设置周期定时器:3秒后触发
    if (setup_periodic_timer(3, 0) < 0) {
        printf("ERROR: Failed to setup timer!\n");
        log_write(LOG_LEVEL_ERROR, "Failed to setup timer\n");
        return -1;
    }
    
    // 打印版本信息
    printf("========================================\n");
    INFO_PRINT("Application version: %s\n", APP_VERSION_BUILD);
    printf("========================================\n");
    printf("Program is running. Timer will expire in 3 seconds. Press Ctrl+C to stop.\n");
    
    // 主循环
    int loop_count = 0;
    while (keep_running) {
        // 模拟业务逻辑处理
        usleep(500 * 1000); // 每次循环休眠 500ms
        loop_count++;
        printf("Working... loop count: %d\n", loop_count);
        
        // 检查定时器是否触发
        if (is_timer_triggered()) {
            // 重置标志位
            reset_timer_flag();
            
            printf("\n[EVENT] Timer Expired! SIGALRM received.\n");
            
            // 这里可以添加具体的业务逻辑
            // 例如:execute_shutdown_command();
            
            // 注意:这里不退出循环,程序继续运行直到用户按下 Ctrl+C
            
            // 可选:设置新的定时器
             //setup_timer(5, 0); // 5秒后再次触发
        }
    }
    
    // 清理工作
    stop_timer();  // 停止定时器
    
    printf("\nMain exited cleanly.\n");
    log_write(LOG_LEVEL_INFO, "Program exited cleanly.\n");
    
    return 0;
}

实验现象 现在是3s定时器触发;周期性的

相关推荐
lzh200409192 小时前
手撕线程池:巩固Linux线程知识
linux·c++
Godspeed Zhao2 小时前
从零开始学AI14——最大似然估计与对数损失函数
算法·逻辑回归·最大似然
流年如夢2 小时前
排序算法详解
数据结构·算法·排序算法
会编程的土豆2 小时前
Go 语言中的 `new` 关键字(创建指针)
java·算法·golang
落叶_Jim2 小时前
2026年Nginx配置HTTPS全流程-从零到自动续期实战指南
运维·nginx·https
南宫萧幕2 小时前
HEV能量管理建模实战:从零搭建 Simulink 物理环境到 Python(DQN) 强化学习联合仿真调通
开发语言·python·算法·matlab·汽车·控制
x_yeyue2 小时前
2026第十七届蓝桥杯c++B组省赛题解
笔记·算法·蓝桥杯·acm·题解
handler012 小时前
【C++ 算法竞赛基础】数论篇:核心公式、经典例题与高频模板
开发语言·c++·算法·蓝桥杯·数论·最大公约数·最小公倍数