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

为何调用printf("new data: ")会无限打印,另一个printf却正常阻塞?

问题:read()监听文件新数据时的异常现象分析

需求是通过read()阻塞线程,直到目标文件写入新数据。现有如下C语言代码:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

void sigint_handler(int sig) {
    printf("received SINT, exiting gracefully, com mto carinho...\n");
    exit(0);
}

int main() {

    signal(SIGINT, sigint_handler);

    char buffer[256] = { 0 };
    int fd = open("foo.txt", O_RDONLY);
    
    if(fd == -1) {
        printf("error opening file\n");
        return -1;
    }

    while(1) {
        
        ssize_t nbytes = read(fd, buffer, sizeof(buffer));

        if(nbytes == -1) {
            printf("error reading file\n");
            close(fd);
            return 1;
        }
        
        //printf("new data: ");
        printf("%.*s", (int) nbytes, buffer);

    }

    close(fd);
    return 0;
}

运行时出现两种异常情况:

  • 启用注释的printf("new data: ")时,程序会无限打印该字符串
  • 仅保留printf("%.*s", (int)nbytes, buffer)时,程序打印完已有内容后阻塞,但无法响应文件新写入的数据

原因分析

1. 无限打印"new data: "的原因

当read()读取到文件末尾(EOF)时,会返回0而非阻塞。此时启用printf("new data: ")后,循环会无休无止地执行:

  • 文件初始内容读完后,后续每次read()都返回0
  • 代码中没有处理nbytes == 0的情况,循环直接进入下一次read(),同时每次都执行printf("new data: "),导致无限输出

2. 仅保留%.*s时阻塞但不响应新数据的原因

当read()返回0(EOF)时,printf("%.*s", 0, buffer)不会输出任何内容,此时循环再次调用read()。但普通磁盘文件的read()在到达EOF后会直接返回0,不会阻塞等待新数据——只有管道、FIFO、套接字这类特殊文件描述符,read()才会在EOF后阻塞等待新写入。常规文件不支持这种"监听新数据"的阻塞行为。


修复方法

要实现监听普通文件新数据的需求,需要结合文件偏移量检测或Linux原生的文件事件监听机制,以下是两种可行方案:

方案1:基于文件偏移量的轮询(简单实现)

每次读取后记录文件偏移量,定期检查文件大小变化,若文件变大则移动偏移量并读取新内容:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>

void sigint_handler(int sig) {
    printf("received SIGINT, exiting gracefully...\n");
    exit(0);
}

int main() {
    signal(SIGINT, sigint_handler);

    char buffer[256] = {0};
    int fd = open("foo.txt", O_RDONLY);
    if(fd == -1) {
        printf("error opening file\n");
        return -1;
    }

    off_t current_offset = lseek(fd, 0, SEEK_END); // 初始定位到文件末尾
    struct stat file_stat;

    while(1) {
        stat("foo.txt", &file_stat);
        if(file_stat.st_size > current_offset) {
            // 文件有新内容,移动偏移量并读取
            lseek(fd, current_offset, SEEK_SET);
            ssize_t nbytes = read(fd, buffer, sizeof(buffer)-1);
            if(nbytes > 0) {
                buffer[nbytes] = '\0';
                printf("new data: %s", buffer);
                current_offset += nbytes;
            }
        }
        sleep(1); // 轮询间隔,避免占用过高CPU
    }

    close(fd);
    return 0;
}

方案2:使用inotify机制(Linux原生高效方案)

利用Linux的inotify API监听文件的修改事件,当文件有新写入时再触发读取:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/inotify.h>
#include <sys/stat.h>

void sigint_handler(int sig) {
    printf("received SIGINT, exiting gracefully...\n");
    exit(0);
}

int main() {
    signal(SIGINT, sigint_handler);

    int fd = open("foo.txt", O_RDONLY);
    if(fd == -1) {
        printf("error opening file\n");
        return -1;
    }

    int inotify_fd = inotify_init();
    if(inotify_fd == -1) {
        perror("inotify_init failed");
        close(fd);
        return -1;
    }

    // 添加对foo.txt的修改事件监听
    int watch_fd = inotify_add_watch(inotify_fd, "foo.txt", IN_MODIFY);
    if(watch_fd == -1) {
        perror("inotify_add_watch failed");
        close(fd);
        close(inotify_fd);
        return -1;
    }

    char buffer[256] = {0};
    off_t current_offset = lseek(fd, 0, SEEK_END);

    while(1) {
        struct inotify_event event;
        // 阻塞等待文件事件
        ssize_t bytes_read = read(inotify_fd, &event, sizeof(event));
        if(bytes_read == -1) {
            perror("read inotify event failed");
            break;
        }

        if(event.mask & IN_MODIFY) {
            // 文件被修改,读取新内容
            lseek(fd, current_offset, SEEK_SET);
            ssize_t nbytes = read(fd, buffer, sizeof(buffer)-1);
            if(nbytes > 0) {
                buffer[nbytes] = '\0';
                printf("new data: %s", buffer);
                current_offset += nbytes;
            }
        }
    }

    inotify_rm_watch(inotify_fd, watch_fd);
    close(inotify_fd);
    close(fd);
    return 0;
}

编译命令:gcc filename.c -o filename(Linux环境默认支持inotify)


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 19:47:17