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

管道写入时进程挂起问题排查:IPC程序异常分析

管道写入阻塞排查:缓冲区有剩余空间却挂起

我正在开发一个进程间通信(IPC)程序,遇到了一个奇怪的问题:管道明明还有剩余空间,但写入操作却阻塞了进程。

远程主机的管道缓冲区大小为8192字节,我通过以下代码确认:

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main() {

    int fd[2];
    pipe(fd);

    printf("Pipe size: %d\n", fcntl(fd[1], F_GETPIPE_SZ));

    close(fd[1]);
    close(fd[0]);

    return 0;
}

程序逻辑说明

我创建了16个进程,每个进程对应一个独立管道。进程按树形结构组织:

  • 根进程编号为0
  • 子进程编号遵循规则:2*k+1、2*k+2(k为父进程编号)

流程分为两步:

  1. 每个进程向自己的子进程管道写入512B数据,子进程读取这些消息
  2. 最后每个进程向所有管道各发送一条512B消息

按理论计算:

  • 根进程的管道会被写入16*512B=8192字节,刚好填满缓冲区
  • 其他管道会被写入(16+1)*512B=8704字节,但其中额外的512B消息会被读取,所以实际占用应该不会超过8192字节,不会触发阻塞

最小复现代码

以下是复现问题的最小示例(无实际业务功能,仅用于复现阻塞问题):

#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>

#define NO_OF_PROCESSES 16
#define NO_OF_MESSAGES 1
#define ROOT 0

#define ERROR_CHECK(result) \
do { \
    if ((result) == -1) { \
        fprintf(stderr, "Error at line %d\n", __LINE__); \
        exit(1); \
    } \
} while (0)

#define NOT_PARTIAL(result) \
do { \
    if ((result) != 512) { \
        fprintf(stderr, "Error at line %d\n", __LINE__); \
        exit(1); \
    } \
} while (0)

void close_pipes(int fd[NO_OF_PROCESSES][2]) {
    for (int i = 0; i < NO_OF_PROCESSES; i++) {
        ERROR_CHECK(close(fd[i][0]));
        ERROR_CHECK(close(fd[i][1]));
    }
}

void child_code(int fd[NO_OF_PROCESSES][2], int child_id) {

    void* message = malloc(512);
    if (message == NULL)
        exit(EXIT_FAILURE);

    memset(message, 0, 512);

    int l = 2 * child_id + 1;
    int r = 2 * child_id + 2;

    // 每个进程向子进程发送消息
    if (child_id == ROOT || l < NO_OF_PROCESSES) { // 根进程或其他父进程

        if (child_id != ROOT)
            for (int i = 0; i < NO_OF_MESSAGES; i++)
                NOT_PARTIAL(read(fd[child_id][0], message, 512));

        if (l < NO_OF_PROCESSES)
            for (int i = 0; i < NO_OF_MESSAGES; i++)
                NOT_PARTIAL(write(fd[l][1], message, 512));

        if (r < NO_OF_PROCESSES)
            for (int i = 0; i < NO_OF_MESSAGES; i++)
                NOT_PARTIAL(write(fd[r][1], message, 512));
    }
    else { // 叶子进程
        for (int i = 0; i < NO_OF_MESSAGES; i++)
            NOT_PARTIAL(read(fd[child_id][0], message, 512));
    }

    printf("Ok, process %d\n", child_id);

    // 进程向所有管道发送一条消息
    for (int i = 0; i < NO_OF_PROCESSES; i++) {
        int pipe_size = 0;
        ioctl(fd[i][1], FIONREAD, &pipe_size);
        printf("Check_1, process %d, there are %d bytes in the pipe, iteration %d\n", child_id, pipe_size, i);
        NOT_PARTIAL(write(fd[i][1], message, 512));
        printf("Check_2, process %d\n", child_id);
        fflush(stdout);
    }

    free(message);

    printf("Finished, process %d\n", child_id);
}

int main() {

    // 每个子进程对应一个管道
    int fd[NO_OF_PROCESSES][2];
    for (int i = 0; i < NO_OF_PROCESSES; i++) {
        ERROR_CHECK(pipe(fd[i]));
    }

    // 创建子进程
    for (int i = 0; i < NO_OF_PROCESSES; i++) {

        int fork_result = fork();
        ERROR_CHECK(fork_result);

        if (fork_result == 0) { // 子进程逻辑
            child_code(fd, i);
            close_pipes(fd);
            return 0;
        }
    }

    close_pipes(fd);

    // 等待所有子进程结束
    for (int i = 0; i < NO_OF_PROCESSES; i++) {
        ERROR_CHECK(wait(NULL));
    }

    return 0;
}

问题现象

程序无法正常终止,部分进程挂起。输出的最后几行如下:

Ok, process 12
Check_1, process 2, there are 7168 bytes in the pipe, iteration 15
Check_2, process 2
Check_1, process 12, there are 7680 bytes in the pipe, iteration 0
Finished, process 2
Check_2, process 12
Check_1, process 12, there are 7680 bytes in the pipe, iteration 1

可以看到,进程12在写入操作时挂住了,没有输出后续的Check_2, process 12。而所有16个进程都打印了Ok,说明树形结构的消息已经全部被读取完成。

补充测试结果:

  • 当进程数为15或更少时,程序可以正常运行(此时管道最多写入8192字节)
  • 在管道缓冲区容量更大的系统上,代码也能正常运行

请问我哪里出错了?进程为什么会挂起?如果你运行代码没问题,大概率是你的管道缓冲区大小和我的不同。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 13:05:54