基于C语言select()与管道的车载日志进程定时控制方案咨询
基于select() + pipe()的车载OBD数据记录程序实现方案
核心架构思路
你的需求核心是主进程定时触发GPS/OBD同步采集,同时让Video进程持续运行,用select()和pipe()可以完美实现这个逻辑,整体架构如下:
- 主进程作为调度中心,负责:
- 创建三个子进程,并建立与GPSLog、OBDLog的单向通信管道(主进程发触发指令,子进程接收)。
- 用select()的超时机制实现500ms定时,每次超时后向GPS/OBD子进程发送采集信号。
- 监控所有子进程的运行状态(比如异常退出时重启)。
- GPSLog/OBDLog子进程:
- 启动后阻塞在select()上,等待主进程的管道消息。
- 收到触发信号后,执行数据采集、MySQL写入操作,完成后回到阻塞状态。
- VideoLog子进程:
- 启动后进入独立循环,持续执行视频数据记录逻辑,无需主进程干预(主进程仅需监控其存活状态)。
关键技术点说明
- 管道通信:主进程与GPS/OBD子进程各创建一个管道(
pipe()),主进程持有写端,子进程持有读端,用于发送触发指令(比如一个简单的字节信号)。 - select()定时:主进程的
select()调用设置timeout为500ms,当超时触发时,就向两个子进程的管道写端发送信号。 - 子进程阻塞等待: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); }
注意事项
- MySQL连接:示例中每个子进程单独建立MySQL连接,车载环境下子进程数量少,这种方式简单可靠;如果需要优化,也可以引入连接池机制。
- 信号处理:建议添加信号处理逻辑(比如
SIGINT),让主进程和子进程优雅退出,清理管道和DB连接资源。 - 子进程重启:示例中的
monitor_children()是简化版,实际开发中可以根据PID判断退出的子进程类型,实现自动重启逻辑。 - 管道缓冲区:因为每次仅发送1字节的触发信号,管道缓冲区不会溢出,无需额外处理。
内容的提问来源于stack exchange,提问作者FloKl
相关产品推荐
相关产品推荐

