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

使用fread&fwrite优化进程间管道100MB文件传输性能

管道传输文件优化:用fread/fwrite替代逐字符读写

核心问题根源

逐字符的getc/putc每次仅处理1字节,会触发频繁的系统调用,内核态与用户态切换开销极大,这是100MB文件耗时超2.5分钟的主要原因。

优化方案关键步骤

pipe返回的是文件描述符(fd[0]读端、fd[1]写端),要结合fread/fwrite,需先通过fdopen()将文件描述符转换为标准FILE*指针,再使用块读写操作,同时配置合适大小的缓冲区:

  1. 文件描述符转FILE*指针

    • 写端:FILE* pipe_writer = fdopen(fd[1], "w");
    • 读端:FILE* pipe_reader = fdopen(fd[0], "r");
      转换后即可直接使用标准I/O库的fread/fwrite函数。
  2. 设置大尺寸缓冲区
    默认标准I/O缓冲区较小,手动设置更大的缓冲区(如64KB、128KB)能进一步减少系统调用次数:

    #define BUFFER_SIZE (64 * 1024) // 64KB缓冲区
    setvbuf(pipe_writer, NULL, _IOFBF, BUFFER_SIZE); // 全缓冲模式
    setvbuf(pipe_reader, NULL, _IOFBF, BUFFER_SIZE);
    
  3. 块读写循环
    用fread批量读取源文件,再通过fwrite批量写入管道;读端同理,批量读取管道内容后写入目标文件。

完整优化代码示例

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

#define BUFFER_SIZE (64 * 1024) // 64KB缓冲区

int main() {
    int fd[2];
    pid_t pid;
    FILE *src_file, *dest_file;

    // 打开源文件和目标文件
    src_file = fopen("source_100mb.bin", "rb");
    dest_file = fopen("dest_100mb.bin", "wb");
    if (!src_file || !dest_file) {
        perror("File open failed");
        exit(EXIT_FAILURE);
    }

    // 创建管道
    if (pipe(fd) == -1) {
        perror("Pipe failed");
        exit(EXIT_FAILURE);
    }

    pid = fork();
    if (pid == -1) {
        perror("Fork failed");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) { // 子进程:读管道,写目标文件
        close(fd[1]); // 关闭不需要的写端
        FILE* pipe_reader = fdopen(fd[0], "r");
        if (!pipe_reader) {
            perror("fdopen reader failed");
            exit(EXIT_FAILURE);
        }
        setvbuf(pipe_reader, NULL, _IOFBF, BUFFER_SIZE);

        char buffer[BUFFER_SIZE];
        size_t bytes_read;
        while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, pipe_reader)) > 0) {
            fwrite(buffer, 1, bytes_read, dest_file);
        }

        fclose(pipe_reader);
        fclose(dest_file);
        exit(EXIT_SUCCESS);
    } else { // 父进程:读源文件,写管道
        close(fd[0]); // 关闭不需要的读端
        FILE* pipe_writer = fdopen(fd[1], "w");
        if (!pipe_writer) {
            perror("fdopen writer failed");
            exit(EXIT_FAILURE);
        }
        setvbuf(pipe_writer, NULL, _IOFBF, BUFFER_SIZE);

        char buffer[BUFFER_SIZE];
        size_t bytes_read;
        while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, src_file)) > 0) {
            fwrite(buffer, 1, bytes_read, pipe_writer);
        }

        fclose(pipe_writer);
        fclose(src_file);
        wait(NULL); // 等待子进程完成
        exit(EXIT_SUCCESS);
    }
}

额外注意事项

  • 必须关闭不需要的管道端(如父进程关闭fd[0],子进程关闭fd[1]),否则管道不会触发EOF,导致读端无限阻塞。
  • 写完管道后,关闭FILE*指针会自动刷新缓冲区,无需手动fflush(若中途需要强制刷新可调用该函数)。
  • 缓冲区大小可根据系统调整,一般4KB~128KB都能获得较好的性能,过大可能浪费内存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 13:15:28