使用Named Pipe(FIFO)构建进程环时read/write调用致执行停滞
命名管道(FIFO)构建进程环时读写停滞的问题排查与解决
我在完成作业时尝试用Named Pipe(FIFO)构建进程环,调用read/write操作管道文件时,程序执行会在该点停滞。已查阅资料尝试多种方法数小时,仍不清楚原因。补充背景:基于Named Pipe的进程环需在n个进程间循环传递token,从进程i传递至i+1。附上相关代码:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <fcntl.h> #include <sys/stat.h> #include <string.h> #include <math.h> #include <errno.h> #define MAX 50 int main(int argc, char** argv) { // File descriptors for pipes i and i+1 and token int fd1, fd2, token = 0; pid_t pid; // Pipes path array char* fifos[2][MAX] = { "pipe1to2" , "pipe2to1" } ... // Create pipes for(int i =0 ; i < 2 ; i++){ char* fileToCreate = fifos[i]; if ((mkfifo(fileToCreate,S_IRWXU)) != 0) { if(errno == 17){ // If a file with the same name exists, this overwrites it unlink(fileToCreate); mkfifo(fileToCreate,S_IRWXU); }else{ printf("Unable to create a fifo; errno=%d\n",errno); exit(1); } } } while(true){ char* file = fifos[itr]; fd1 = open(file,O_WRONLY); if(fd1 == -1){ printf("Open error\n"); return 1; } if(write(fd1,token,sizeof(int)) == -1){ printf("Write error"); return 2; } close(fd1); ... } }
问题分析
导致程序停滞的核心原因集中在以下几点:
- FIFO打开阻塞特性:默认模式下,以
O_WRONLY打开FIFO时,若没有进程以读模式打开该管道,open会一直阻塞;同理O_RDONLY打开时无写端也会阻塞。你的代码里没有对应读端进程,必然卡在open或write步骤。 - 数组定义错误:
char* fifos[2][MAX]是二维数组,但初始化用了一维数据,访问fifos[i]会导致地址越界或非法访问。 - write参数错误:
write(fd1, token, sizeof(int))中直接传递token的值作为指针,实际应该传递&token(变量地址),否则会写入非法内存区域,或触发未定义行为。 - 进程环逻辑缺失:代码仅声明了
pid_t pid,但没有调用fork()创建子进程,单进程无法同时充当读写两端,必然阻塞。 - 未定义变量
itr:循环中使用的itr没有声明和赋值,属于未定义行为,会导致管道访问混乱。
修复方案与示例代码
以下是针对2个进程环的修正代码,核心解决上述问题:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <fcntl.h> #include <sys/stat.h> #include <string.h> #include <errno.h> #include <sys/wait.h> #define MAX_PROCESSES 2 #define FIFO_PREFIX "pipe" int main(int argc, char** argv) { int fd_read, fd_write, token; pid_t pid; char fifos[MAX_PROCESSES][20]; // 创建所有需要的FIFO for (int i = 0; i < MAX_PROCESSES; i++) { snprintf(fifos[i], sizeof(fifos[i]), "%s%dto%d", FIFO_PREFIX, i+1, (i+1)%MAX_PROCESSES +1); if (mkfifo(fifos[i], S_IRWXU) != 0) { if (errno == EEXIST) { unlink(fifos[i]); mkfifo(fifos[i], S_IRWXU); } else { perror("mkfifo failed"); exit(EXIT_FAILURE); } } } // 创建子进程,构建进程环 for (int i = 0; i < MAX_PROCESSES; i++) { pid = fork(); if (pid == -1) { perror("fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { int process_id = i + 1; // 每个进程读取前一个管道,写入下一个管道 int read_pipe_idx = (i == 0) ? MAX_PROCESSES -1 : i -1; int write_pipe_idx = i; // 先打开读管道(确保有写端后再处理写管道) fd_read = open(fifos[read_pipe_idx], O_RDONLY); if (fd_read == -1) { perror("open read pipe failed"); exit(EXIT_FAILURE); } fd_write = open(fifos[write_pipe_idx], O_WRONLY); if (fd_write == -1) { perror("open write pipe failed"); exit(EXIT_FAILURE); } // 循环传递token while (1) { if (read(fd_read, &token, sizeof(int)) == -1) { perror("read failed"); exit(EXIT_FAILURE); } printf("进程%d收到token:%d\n", process_id, token); token++; // 示例:修改token值 if (write(fd_write, &token, sizeof(int)) == -1) { perror("write failed"); exit(EXIT_FAILURE); } printf("进程%d发送token:%d\n", process_id, token); sleep(1); // 控制输出节奏 } close(fd_read); close(fd_write); exit(EXIT_SUCCESS); } } // 父进程初始化第一个token int first_pipe_idx = 0; fd_write = open(fifos[first_pipe_idx], O_WRONLY); if (fd_write == -1) { perror("open first pipe failed"); exit(EXIT_FAILURE); } token = 0; printf("父进程初始化token:%d\n", token); if (write(fd_write, &token, sizeof(int)) == -1) { perror("write initial token failed"); exit(EXIT_FAILURE); } close(fd_write); // 等待所有子进程结束 for (int i = 0; i < MAX_PROCESSES; i++) { wait(NULL); } // 清理FIFO文件 for (int i = 0; i < MAX_PROCESSES; i++) { unlink(fifos[i]); } return 0; }
关键修复点说明
- 修正FIFO数组:用一维数组存储管道路径,通过
snprintf生成规范命名 - 添加进程创建逻辑:通过
fork()生成子进程,每个进程对应环中的一个节点 - 调整open顺序:子进程先打开读管道再打开写管道,避免所有进程同时卡在写管道的阻塞等待
- 修正读写参数:传递
&token作为缓冲区地址,确保数据正确读写 - 初始化token:父进程负责写入初始token,启动整个环的传递流程
内容的提问来源于stack exchange,提问作者ntsay
相关产品推荐
相关产品推荐

