C语言子进程实现日志器程序阻塞无输出问题求助
C语言子进程日志器无输出问题排查
我用C语言实现了一个基于子进程的日志器,用来记录sensor_db.c中数据库文件的打开、写入、关闭操作,但运行后日志子进程一直处于等待状态,没有任何输出。调试时,子进程执行到read()函数就停住了。
相关代码
sensor_db.c
#include <stdio.h> #include <stdbool.h> #include "config.h" #include <errno.h> #include <stdlib.h> #include <unistd.h> #include "logger.h" #include <fcntl.h> #include <sys/wait.h> // status 1: file opening error // status 2: fork failure // status 3: writing to db file failure // status 4: file failed to close // status 5: pipe creation failure /* definition of global vars */ FILE* open_db(char *filename, bool append) { FILE *fp; if (append == true) { fp = fopen(filename, "a"); } else { fp = fopen(filename, "w"); } create_log_process(); // Error handling for file opening if (fp == NULL) { // create_log_process(); write_to_log_process("ERROR: could not open db file."); perror("Error occurred during file opening"); exit(1); } // Communicate with the log process write_to_log_process("Data file opened."); return fp; } int insert_sensor(FILE * f, sensor_id_t id, sensor_value_t value, sensor_ts_t ts) { if (fprintf(f, "%d, %lf, %ld \n", id, value, ts) < 0) { write_to_log_process("ERROR: could not write to db file."); perror("Error writing to file"); fclose(f); exit(3); } // Communicate with the log process write_to_log_process("Data inserted."); return 0; } int close_db(FILE * f) { if ( fclose(f) == EOF) { write_to_log_process("ERROR: could not close db file."); perror("Error closing file"); exit(4); }; // Communicate with the log process write_to_log_process("Data file closed."); end_log_process(); return 0; }
logger.c
#include <time.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <string.h> #include <errno.h> #define BUFFER_SIZE 1000 // status 1: file opening error // status 2: fork failure // status 3: writing to log file failure // status 4: file failed to close // status 5: pipe creation failure // status 6: writing to log file failure /*global variables (for file opening + pipe)*/ FILE *fp; int fd[2], pid; int i = 0; int create_log_process() { // log file opening fp = fopen("gateway.log", "a"); if (fp == NULL) { perror("Error occurred during log file opening"); exit(1); } // creation of communication and child process if (pipe(fd) == -1) { perror("Error occurred during pipe creation"); exit(5); }; pid = fork(); // error handle if (pid == -1) { perror("fork failed"); exit(2); } else if (pid == 0) { close(fd[1]); // closing write_end. char received_message[BUFFER_SIZE]; size_t message_length; while(read(fd[0], &message_length, sizeof(size_t)) > 0) { if ((read(fd[0], received_message, message_length)) > 0) { printf("test\n"); printf("child process reads: %s \n", received_message); // timestamp creation. time_t timer; time(&timer); struct tm *timestamp_info; timestamp_info = localtime(&timer); char timestamp[20]; strftime(timestamp, sizeof(timestamp), "%a %b %d %X %Y", timestamp_info); // writing message to log file. if (fprintf(fp, "%d - %s - %s\n", i++, timestamp, received_message) < 0) { perror("Error occurred during writing to log file"); fclose(fp); }; printf("test\n"); } } close(fd[0]); fclose(fp); exit(0); } return 0; } // //int write_to_log_process(char *msg) { // if (pid > 0) { // char message[BUFFER_SIZE]; // strncpy(message, msg, sizeof(message) - 1); // message[sizeof(message) - 1] = '\0'; // Ensure null-termination // if (write(fd[1], message, strlen(message) + 1) == -1) { // perror("Error occurred during writing to pipe"); // exit(3); // } // // opmerking!!! sizeof(msg) don't use, = size of the pointer! use buffer_size... // // with buffer_size you know for sure the nbytes you want to copy at most + is configurable. // } // return 0; //} int write_to_log_process(char *msg) { size_t msg_length = strlen(msg) + 1; write(fd[1], &msg_length, sizeof(size_t)); // writing the length to be read. if (write(fd[1], msg, strlen(msg) + 1) == -1) { // writing the message as well. perror("Error occurred during writing to pipe"); exit(3); } return 0; } // opmerking!!! sizeof(msg) don't use, = size of the pointer! use buffer_size... // with buffer_size you know for sure the nbytes you want to copy at most + is configurable. int end_log_process() { close(fd[0]); close(fd[1]); wait(NULL); return 0; }
测试代码
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <stdbool.h> #include <time.h> #include <sys/types.h> #include "sensor_db.h" #include "logger.h" #include "config.h" int main() { FILE *f = open_db("sensor_db.csv", true); sleep(1); sensor_id_t id = 1; sensor_value_t v = 0.001; sensor_ts_t ts = time(NULL); insert_sensor(f, id, v, ts); id = 2; v = 0.002; ts = time(NULL); insert_sensor(f, id, v, ts); id = 3; v = 0.003; ts = time(NULL); insert_sensor(f, id, v, ts); sleep(5); insert_sensor(f, 4, v, ts); close_db(f); return 0; }
问题根源及修复方案
1. 父进程未关闭管道读端,导致子进程阻塞
创建管道和子进程后,父进程没有关闭管道的读端fd[0],管道读端在父进程中一直处于打开状态,子进程的while(read(fd[0], ...) > 0)循环永远不会遇到EOF,会一直阻塞等待数据。
修复:在create_log_process的父进程分支中添加关闭读端的代码:
int create_log_process() { // ... 现有代码 ... pid = fork(); if (pid == -1) { perror("fork failed"); exit(2); } else if (pid == 0) { // 子进程代码保持不变 } else { // 父进程关闭管道读端 close(fd[0]); } return 0; }
2. write调用未处理部分写入情况
管道是字节流,write可能只写入部分数据,导致子进程读取的长度或消息不完整,进而阻塞。需要确保所有数据都写入管道。
修复:封装一个确保全部写入的函数,替换原write调用:
ssize_t full_write(int fd, const void *buf, size_t count) { size_t remaining = count; const char *p = buf; while (remaining > 0) { ssize_t n = write(fd, p, remaining); if (n == -1) { if (errno == EINTR) continue; // 被信号中断,重试 return -1; } remaining -= n; p += n; } return count; } // 修改write_to_log_process int write_to_log_process(char *msg) { size_t msg_length = strlen(msg) + 1; if (full_write(fd[1], &msg_length, sizeof(size_t)) == -1) { perror("Error writing length to pipe"); exit(3); } if (full_write(fd[1], msg, msg_length) == -1) { perror("Error writing message to pipe"); exit(3); } return 0; }
3. 日志文件缓冲区未手动刷新
日志文件fp默认是行缓冲模式,子进程写入日志后如果不手动刷新,缓冲区数据可能不会立即写入文件,导致看起来没有输出。
修复:在fprintf后添加缓冲区刷新操作:
if (fprintf(fp, "%d - %s - %s\n", i++, timestamp, received_message) < 0) { perror("Error occurred during writing to log file"); fclose(fp); exit(6); }; fflush(fp); // 手动刷新缓冲区
内容的提问来源于stack exchange,提问作者dsdfs
相关产品推荐
相关产品推荐

