如何在程序终止时正确关闭无限循环中使用的FIFO(命名管道)
实现方案
核心思路
通过注册信号处理函数捕获终止信号(CTRL+C触发的是SIGINT,主进程主动终止一般会给子进程发SIGTERM),在信号处理函数中修改全局的退出标志位,业务循环检测到标志位变化后跳出循环,执行文件描述符关闭、资源清理等逻辑后再退出进程。
注意:信号处理函数中仅做标志位修改即可,不要调用非异步信号安全的复杂函数,所有清理逻辑统一放在循环外执行
代码修改示例
写入端(writer)修改后代码
#include <signal.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> // 用volatile sig_atomic_t保证信号操作的原子性,避免编译器优化导致标志位不生效 volatile sig_atomic_t exit_flag = 0; void sig_handler(int sig) { exit_flag = 1; } int main() { int myfifo_fd; char buffer[81]; // 注册信号捕获逻辑,覆盖CTRL+C、主进程终止两种场景 signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); // 原有打开管道逻辑 myfifo_fd = open(myfifo, O_WRONLY); if (myfifo_fd < 0) { perror("open fifo failed"); return 1; } while(!exit_flag) { // 原有while(1)改为判断退出标志 if (fgets(buffer, 80, stdin) == NULL) { // 输入异常时也触发退出 break; } write(myfifo_fd, buffer, 81); } // 退出前统一清理资源 close(myfifo_fd); // 如果是当前进程创建的命名管道,可在此处加unlink(myfifo)删除管道文件 return 0; }
读取端(reader)修改后代码
#include <signal.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <string.h> volatile sig_atomic_t exit_flag = 0; void sig_handler(int sig) { exit_flag = 1; } int main() { int myfifo_fd; char buffer[81]; char last_command[81] = "0"; signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); myfifo_fd = open(myfifo, O_RDONLY | O_NONBLOCK); if (myfifo_fd < 0) { perror("open fifo failed"); return 1; } while(!exit_flag) { int read_len = read(myfifo_fd, buffer, 81); if (read_len > 0) { strncpy(last_command, buffer, sizeof(last_command)-1); } do_something(last_command); usleep(10000); // 非阻塞读场景加短休眠,避免空转占满CPU } close(myfifo_fd); return 0; }
注意事项
- 如果需要处理主进程异常崩溃的场景,可以额外给子进程注册
SIGHUP信号的处理逻辑,父进程退出后子进程会收到SIGHUP,同样触发退出清理流程 - 如果业务逻辑中存在可中断的阻塞调用,不需要额外修改,信号触发后阻塞调用会直接返回错误,循环会正常检测到退出标志
内容的提问来源于stack exchange,提问作者Mosterligue
相关产品推荐
相关产品推荐

