You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于C语言select()与管道的车载日志进程定时控制方案咨询

基于select() + pipe()的车载OBD数据记录程序实现方案

核心架构思路

你的需求核心是主进程定时触发GPS/OBD同步采集,同时让Video进程持续运行,用select()和pipe()可以完美实现这个逻辑,整体架构如下:

  • 主进程作为调度中心,负责:
    • 创建三个子进程,并建立与GPSLog、OBDLog的单向通信管道(主进程发触发指令,子进程接收)。
    • 用select()的超时机制实现500ms定时,每次超时后向GPS/OBD子进程发送采集信号。
    • 监控所有子进程的运行状态(比如异常退出时重启)。
  • GPSLog/OBDLog子进程:
    • 启动后阻塞在select()上,等待主进程的管道消息。
    • 收到触发信号后,执行数据采集、MySQL写入操作,完成后回到阻塞状态。
  • VideoLog子进程:
    • 启动后进入独立循环,持续执行视频数据记录逻辑,无需主进程干预(主进程仅需监控其存活状态)。

关键技术点说明

  1. 管道通信:主进程与GPS/OBD子进程各创建一个管道(pipe()),主进程持有写端,子进程持有读端,用于发送触发指令(比如一个简单的字节信号)。
  2. select()定时:主进程的select()调用设置timeout为500ms,当超时触发时,就向两个子进程的管道写端发送信号。
  3. 子进程阻塞等待:GPS/OBD子进程用select()监听管道读端,当有数据可读时,就执行采集逻辑,避免轮询浪费资源。

示例代码(C语言实现)

1. 主进程代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <string.h>

#define TRIGGER_SIGNAL 0x01
#define INTERVAL_MS 500

// 声明子进程函数
void gps_log_func(int pipe_r);
void obd_log_func(int pipe_r);

// 创建子进程并返回管道写端(仅用于GPS/OBD)
int create_child(const char *name, void (*child_func)(int)) {
    int pipe_fd[2];
    if (pipe(pipe_fd) == -1) {
        perror("pipe failed");
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        // 子进程关闭写端,保留读端
        close(pipe_fd[1]);
        printf("[%s] 子进程启动,等待触发信号\n", name);
        child_func(pipe_fd[0]);
        exit(EXIT_SUCCESS);
    } else {
        // 主进程关闭读端,保留写端
        close(pipe_fd[0]);
        printf("[Main] 创建子进程 %s,PID: %d\n", name, pid);
        return pipe_fd[1];
    }
}

// 监控子进程状态,异常退出时重启(可选)
void monitor_children() {
    pid_t pid;
    int status;
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
        printf("[Main] 子进程 %d 异常退出,准备重启\n", pid);
        // 这里可以根据pid判断是哪个子进程,然后添加重启逻辑
    }
}

int main() {
    // 创建VideoLog子进程:不需要管道,直接独立运行
    pid_t video_pid = fork();
    if (video_pid == -1) {
        perror("fork video failed");
        exit(EXIT_FAILURE);
    }
    if (video_pid == 0) {
        // VideoLog子进程逻辑:持续运行
        while (1) {
            printf("[VideoLog] 记录视频数据...\n");
            sleep(1); // 模拟视频采集间隔,实际根据需求调整
        }
        exit(EXIT_SUCCESS);
    } else {
        printf("[Main] 创建VideoLog子进程,PID: %d\n", video_pid);
    }

    // 创建GPSLog和OBDLog子进程,并获取管道写端
    int gps_pipe_w = create_child("GPSLog", gps_log_func);
    int obd_pipe_w = create_child("OBDLog", obd_log_func);

    // 主进程调度循环
    while (1) {
        // 设置select超时为500ms
        struct timeval timeout;
        timeout.tv_sec = 0;
        timeout.tv_usec = INTERVAL_MS * 1000;

        // 仅用select实现定时,无需监听读端
        fd_set read_fds;
        FD_ZERO(&read_fds);
        int ret = select(0, &read_fds, NULL, NULL, &timeout);

        if (ret == -1) {
            perror("select failed");
            continue;
        } else if (ret == 0) {
            // 超时触发:向GPS和OBD子进程发送信号
            printf("[Main] 触发GPS/OBD数据采集\n");
            write(gps_pipe_w, &TRIGGER_SIGNAL, sizeof(TRIGGER_SIGNAL));
            write(obd_pipe_w, &TRIGGER_SIGNAL, sizeof(TRIGGER_SIGNAL));
        }

        // 监控子进程状态
        monitor_children();
    }

    // 清理资源(实际中可能不会走到这里)
    close(gps_pipe_w);
    close(obd_pipe_w);
    waitpid(video_pid, NULL, 0);
    return 0;
}

2. GPSLog和OBDLog子进程函数实现

#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
#include <mysql/mysql.h>

#define TRIGGER_SIGNAL 0x01

// MySQL连接初始化(示例)
MYSQL* init_mysql() {
    MYSQL *conn = mysql_init(NULL);
    if (!mysql_real_connect(conn, "localhost", "user", "password", "obd_db", 0, NULL, 0)) {
        fprintf(stderr, "MySQL连接失败: %s\n", mysql_error(conn));
        exit(EXIT_FAILURE);
    }
    return conn;
}

// GPSLog子进程逻辑
void gps_log_func(int pipe_r) {
    MYSQL *conn = init_mysql();
    fd_set read_fds;

    while (1) {
        FD_ZERO(&read_fds);
        FD_SET(pipe_r, &read_fds);

        // 阻塞等待主进程的触发信号
        int ret = select(pipe_r + 1, &read_fds, NULL, NULL, NULL);
        if (ret == -1) {
            perror("select in GPSLog failed");
            continue;
        }

        if (FD_ISSET(pipe_r, &read_fds)) {
            char signal;
            read(pipe_r, &signal, sizeof(signal));
            if (signal == TRIGGER_SIGNAL) {
                // 模拟GPS数据采集
                char gps_data[100] = "39.9042,116.4074,120"; // 纬度,经度,速度
                printf("[GPSLog] 采集到数据: %s\n", gps_data);

                // 写入MySQL
                char query[256];
                snprintf(query, sizeof(query), "INSERT INTO gps_log(lat, lng, speed, timestamp) VALUES ('%s', '%s', '%s', NOW())", 
                         strtok(gps_data, ","), strtok(NULL, ","), strtok(NULL, ","));
                if (mysql_query(conn, query)) {
                    fprintf(stderr, "GPS写入DB失败: %s\n", mysql_error(conn));
                } else {
                    printf("[GPSLog] 数据成功写入DB\n");
                }
            }
        }
    }

    mysql_close(conn);
    close(pipe_r);
}

// OBDLog子进程逻辑(与GPS类似,仅采集数据不同)
void obd_log_func(int pipe_r) {
    MYSQL *conn = init_mysql();
    fd_set read_fds;

    while (1) {
        FD_ZERO(&read_fds);
        FD_SET(pipe_r, &read_fds);

        int ret = select(pipe_r + 1, &read_fds, NULL, NULL, NULL);
        if (ret == -1) {
            perror("select in OBDLog failed");
            continue;
        }

        if (FD_ISSET(pipe_r, &read_fds)) {
            char signal;
            read(pipe_r, &signal, sizeof(signal));
            if (signal == TRIGGER_SIGNAL) {
                // 模拟OBD数据采集
                char obd_data[100] = "90,2.5,14.7"; // 发动机转速,冷却液温度,空燃比
                printf("[OBDLog] 采集到数据: %s\n", obd_data);

                // 写入MySQL
                char query[256];
                snprintf(query, sizeof(query), "INSERT INTO obd_log(rpm, coolant_temp, air_fuel_ratio, timestamp) VALUES ('%s', '%s', '%s', NOW())", 
                         strtok(obd_data, ","), strtok(NULL, ","), strtok(NULL, ","));
                if (mysql_query(conn, query)) {
                    fprintf(stderr, "OBD写入DB失败: %s\n", mysql_error(conn));
                } else {
                    printf("[OBDLog] 数据成功写入DB\n");
                }
            }
        }
    }

    mysql_close(conn);
    close(pipe_r);
}

注意事项

  1. MySQL连接:示例中每个子进程单独建立MySQL连接,车载环境下子进程数量少,这种方式简单可靠;如果需要优化,也可以引入连接池机制。
  2. 信号处理:建议添加信号处理逻辑(比如SIGINT),让主进程和子进程优雅退出,清理管道和DB连接资源。
  3. 子进程重启:示例中的monitor_children()是简化版,实际开发中可以根据PID判断退出的子进程类型,实现自动重启逻辑。
  4. 管道缓冲区:因为每次仅发送1字节的触发信号,管道缓冲区不会溢出,无需额外处理。

内容的提问来源于stack exchange,提问作者FloKl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:45:57