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

跨进程读取fallocate创建文件的写入边界问题及解决方案

进程间文件读写同步问题及解决

我有两个进程:writer进程先通过fallocate(2)创建固定长度的空文件,再向其中写入数据;reader进程需要读取到writer最后写入的位置(即“边界”),之后等待writer写入新数据。

我编写了简单的示例程序,但reader进程读取速度略快,会在某一时刻触及文件边界。此时reader应回退一个数据元素并重试,直到读取到非零数据,但该逻辑仅成功一次,后续始终读取到0。不过通过终端复制文件时,所有预期数据都存在,说明数据已写入但无法被可靠读取。

最终通过在reader进程的fopen后添加setvbuf(fp, NULL, _IONBF, 0)禁用stdio缓冲解决了问题。


WRITER进程代码

/*
  g++ fWriter.cpp -o fWriter -g -pg
*/

#include <stdio.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
    printf("WRITER LAUNCHED\n");

    const int N = 100;

    FILE *fp = fopen("/tmp/someFile", "w");
    if (!fp)
    {
        perror("fopen");
        exit(1);
    }

    int   itemLen = sizeof(uint32_t);
    off_t     len = itemLen * N;

    int fd = fileno(fp);
    if (fallocate(fd, 0, 0, len) == -1)
    {
        fprintf(stderr, "fallocate failed: %s\n", strerror(errno));
        exit(1);
    }

    for (uint32_t i = 1; i <= N; i++)
    {
        int numBytes = fwrite((void*)&i, 1, itemLen, fp);
        if (numBytes == itemLen)
            printf("[%u] Wrote %u\n", i, i);
        else
            printf("Write error\n");

        fflush(fp);

        sleep(1);
    }

    fclose(fp);

    printf("WRITER COMPLETE\n");
}

READER进程代码

/*
  g++ fReader.cpp -o fReader -g -pg
*/

#include <stdio.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
    printf("READER LAUNCHED\n");

    FILE *fp = fopen("/tmp/someFile", "r");
    if (!fp)
    {
        perror("fopen");
        exit(1);
    }

    
    // 新增:禁用stdio缓冲
    setvbuf(fp, NULL, _IONBF, 0);

    int   i = 1;
    int   itemLen = sizeof(uint32_t);

    while (!feof(fp))
    {
        uint32_t val;

        int numBytes = fread((void*)&val, 1, itemLen, fp);

        if (numBytes != itemLen)
        {
            printf("Short read\n");
            continue;
        }

        printf("[%d] Read %u\n", i, val);

        // 等待新数据,回退并重试
        if (val == 0)
        {
            off_t curPos = ftello(fp);
            off_t newPos = curPos - itemLen;
            printf("Seek and try again. newPos %d\n", newPos);
            fseeko(fp, newPos, SEEK_SET);
        }

        i++;

        //usleep(1100000);
        usleep(900000);
    }

    fclose(fp);
}

READER进程日志

READER LAUNCHED
[1] Read 1
[2] Read 2
[3] Read 3
[4] Read 4
[5] Read 5
[6] Read 6
[7] Read 0
Seek and try again. newPos 24
[8] Read 7
[9] Read 8
[10] Read 9
[11] Read 10
[12] Read 11
[13] Read 0
Seek and try again. newPos 44
[14] Read 0
Seek and try again. newPos 44
[15] Read 0
Seek and try again. newPos 44
[16] Read 0
Seek and try again. newPos 44
[17] Read 0
Seek and try again. newPos 44
[18] Read 0
Seek and try again. newPos 44
[19] Read 0
Seek and try again. newPos 44
[20] Read 0
Seek and try again. newPos 44
[21] Read 0
Seek and try again. newPos 44
[22] Read 0
Seek and try again. newPos 44

问题核心原因

出现该问题的关键是stdio默认缓冲机制:reader使用fread时,默认采用全缓冲/行缓冲模式,当它提前读取到文件中未写入的0值区域时,这些0值会被缓存到stdio缓冲区中。后续即使writer写入了新数据,fread仍会优先从缓冲区读取旧的0值,而非磁盘上的最新内容,导致始终读取失败。

解决逻辑

在reader打开文件后立即调用setvbuf(fp, NULL, _IONBF, 0)禁用stdio缓冲,让fread每次直接从磁盘读取文件最新内容,确保能正确获取writer后续写入的数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 15:55:19