Linux下C程序:主线程正常的函数在线程内无法执行问题排查
问题分析与修复:Linux多线程inotify文件复制程序异常
问题描述
我正在编写一款Linux环境下的C程序,功能是将当前目录中的文件内容追加至公共文件。程序要求线程数量可调且线程为永久线程(执行完毕后等待再次触发执行)。下方代码将新创建的文件分配给响应最快的线程,由线程调用copiar_archivo函数完成内容追加,但该函数完全未执行,程序直接跳过调用并删除文件。我已验证该函数在主线程中指定具体文件名时可正常工作,怀疑问题与线程环境或inotify事件相关,请问遗漏了什么?
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <sys/inotify.h> #define EVENT_SIZE (sizeof (struct inotify_event)) #define BUF_LEN (1024 * (EVENT_SIZE + 16)) #define NOM_FICH "FICHERO.csv" void *hilo_func(void *arg); void copiar_archivo(char *nombre_archivo_x, char *nombre_archivo_y); int n_hilos = 2; int fd, wd; int main(void) { pthread_t hilos[n_hilos]; int i, rc; int thread_numbers[n_hilos]; fd = inotify_init(); if (fd < 0) { perror("Error al inicializar inotify"); exit(EXIT_FAILURE); } wd = inotify_add_watch(fd, ".", IN_CREATE); if (wd < 0) { perror("Error al agregar directorio para observación"); exit(EXIT_FAILURE); } for (i = 0; i < n_hilos; i++) { thread_numbers[i] = i + 1; rc = pthread_create(&hilos[i], NULL, hilo_func, &thread_numbers[i]); if (rc) { perror("Error al crear hilo"); exit(EXIT_FAILURE); } } for (i = 0; i < n_hilos; i++) { rc = pthread_join(hilos[i], NULL); if (rc) { perror("Error al esperar hilo"); exit(EXIT_FAILURE); } } inotify_rm_watch(fd, wd); close(fd); return 0; } void *hilo_func(void *arg) { int num_hilo = *(int *) arg; char buf[BUF_LEN]; int length, i = 0; while (1) { length = read(fd, buf, BUF_LEN); printf("Hilo %d: AWARE\n", num_hilo); if (length < 0) { perror("Error al leer evento inotify"); exit(EXIT_FAILURE); } while (i < length) { struct inotify_event *event = (struct inotify_event *) &buf[i]; if (event->mask & IN_CREATE) { printf("Hilo %d: archivo '%s' creado\n", num_hilo, event->name); //PROBLEM HERE char nom_destino[100] = NOM_FICH; copiar_archivo(event->name, nom_destino); sleep(1); remove(event->name); } i += EVENT_SIZE + event->len; } i = 0; } pthread_exit(NULL); } void copiar_archivo(char *nombre_archivo_x, char *nombre_archivo_y) { printf("EY, al menos funciona :)"); FILE *archivo_x, *archivo_y; char buffer[1024]; size_t n; archivo_x = fopen(nombre_archivo_x, "rb"); if (archivo_x == NULL) { perror("Error al abrir archivo X"); exit(EXIT_FAILURE); } archivo_y = fopen(nombre_archivo_y, "ab"); if (archivo_y == NULL) { perror("Error al abrir archivo Y"); exit(EXIT_FAILURE); } while ((n = fread(buffer, 1, sizeof buffer, archivo_x)) > 0) { fwrite(buffer, 1, n, archivo_y); } if (ferror(archivo_x)) { perror("Error al leer archivo X"); exit(EXIT_FAILURE); } fclose(archivo_x); fclose(archivo_y); }
核心问题与修复方案
1. 文件创建事件的时机错误
IN_CREATE事件触发时,文件仅完成创建动作,但内容可能还未写入完成,甚至创建文件的进程还未关闭文件句柄。此时直接调用copiar_archivo会导致fopen失败,而代码中exit(EXIT_FAILURE)会直接终止整个进程——由于标准输出缓冲未刷新,你看不到错误提示,误以为函数被跳过。
2. 错误处理逻辑过于激进
copiar_archivo中遇到文件打开失败直接调用exit,会导致整个程序终止,而不是让线程处理错误后继续等待下一个事件。
3. 输出缓冲区未强制刷新
printf的输出默认是行缓冲,若没有换行符或手动刷新,进程退出时缓冲区内容可能未被打印,导致你看不到函数执行的提示。
具体修复步骤
(1)修改inotify监听事件类型
将监听掩码从IN_CREATE改为IN_CREATE | IN_CLOSE_WRITE,只在文件写入完成并关闭时(IN_CLOSE_WRITE事件)执行复制操作:
wd = inotify_add_watch(fd, ".", IN_CREATE | IN_CLOSE_WRITE);
(2)调整线程事件处理逻辑
区分IN_CREATE和IN_CLOSE_WRITE事件,仅在文件写入完成后执行复制:
while (i < length) { struct inotify_event *event = (struct inotify_event *) &buf[i]; if (event->mask & IN_CREATE) { printf("Hilo %d: archivo '%s' creado\n", num_hilo, event->name); fflush(stdout); } if (event->mask & IN_CLOSE_WRITE) { printf("Hilo %d: archivo '%s'写入完成,开始复制\n", num_hilo, event->name); fflush(stdout); char nom_destino[100] = NOM_FICH; copiar_archivo(event->name, nom_destino); sleep(1); remove(event->name); } i += EVENT_SIZE + event->len; }
(3)修复copiar_archivo的错误处理与输出
- 强制刷新输出缓冲区,确保提示信息能及时显示
- 将
exit改为返回,避免单个线程错误导致整个进程终止:
void copiar_archivo(char *nombre_archivo_x, char *nombre_archivo_y) { printf("EY, al menos funciona :)\n"); fflush(stdout); // 强制刷新缓冲区 FILE *archivo_x, *archivo_y; char buffer[1024]; size_t n; archivo_x = fopen(nombre_archivo_x, "rb"); if (archivo_x == NULL) { perror("Error al abrir archivo X"); fflush(stderr); return; // 返回而非终止进程 } archivo_y = fopen(nombre_archivo_y, "ab"); if (archivo_y == NULL) { perror("Error al abrir archivo Y"); fflush(stderr); fclose(archivo_x); return; } while ((n = fread(buffer, 1, sizeof buffer, archivo_x)) > 0) { fwrite(buffer, 1, n, archivo_y); } if (ferror(archivo_x)) { perror("Error al leer archivo X"); fflush(stderr); } fclose(archivo_x); fclose(archivo_y); }
内容的提问来源于stack exchange,提问作者Alfonso Moreno
相关产品推荐
相关产品推荐

