You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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;
}

关键修复点说明

  1. 修正FIFO数组:用一维数组存储管道路径,通过snprintf生成规范命名
  2. 添加进程创建逻辑:通过fork()生成子进程,每个进程对应环中的一个节点
  3. 调整open顺序:子进程先打开读管道再打开写管道,避免所有进程同时卡在写管道的阻塞等待
  4. 修正读写参数:传递&token作为缓冲区地址,确保数据正确读写
  5. 初始化token:父进程负责写入初始token,启动整个环的传递流程

内容的提问来源于stack exchange,提问作者ntsay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 17:55:17