为什么在这里调用 printf 会无限打印消息?

问题描述 投票:0回答:1

目标是阻塞线程,直到新数据写入文件。我正在使用

read()
来做到这一点。

#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);
它打印从 read() 读取的内容然后阻塞当前线程......是什么导致了那个我该如何解决?

c linux file-read
1个回答
1
投票

我很抱歉让这个答案不够完美。但由于还没有人回答过这个问题,我认为快速指出正确的方向可能已经有所帮助。

你想做的是看看

select()
poll()
系统调用,后者可能是最容易使用的。

这些旨在监视一组文件描述符并阻塞调用线程,直到它们的某些状态发生变化。您可以配置您感兴趣的状态变化类型。

另一方面,当没有可用数据时,

read()
系统调用不能保证可靠地阻塞等。

所以你的程序应该在循环中调用

select
(这需要一些乱七八糟的信号掩码)或
poll

您可以配置它,以便您的线程在输入和错误条件下都被唤醒。然后你可以区分两者,当有更多数据可用时,做一个非阻塞

read
.

对不起,但我现在太累了,无法提供一些实际的代码示例,但希望这会为您指明正确的方向。


所以我刚刚尝试使用

select
poll
不幸的是,这仅在监视套接字、管道或类似标准输入的东西时有效,但不适用于常规文件。

这是我的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <poll.h>

int
main(void)
{
    int fd;

    fd = open("foo.txt", O_RDONLY);

    if(fd == -1) {
        printf("error opening file\n");
        return -1;
    }

    // Comment this out to use your file instead of stdin.
    fd = STDIN_FILENO;

    while(1) {
        struct pollfd fds[1];
        char buffer[BUFSIZ];
        ssize_t nbytes;
        int ret;

        struct timeval timeout;
        fd_set readfds;
        int nready;

        timeout.tv_sec = 60;
        timeout.tv_usec = 0;
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);

        printf("loop\n");

        nready = select(fd + 1, &readfds, NULL, NULL, &timeout);

        if (nready == -1) {
            printf("select() failed.\n");
            break;
        }

        if (nready == 0) {
            printf("time out.\n");
            break;
        }

        if (FD_ISSET(fd, &readfds)) {
            printf("data available.\n");
        } else {
            printf("select() didn't report data available.\n");
        }

        fds[0].fd = fd;
        fds[0].events = POLLIN;
        fds[0].revents = 0;

        ret = poll(fds, 1, INFTIM);

        printf("poll() returned %d - %x\n", ret, fds[0].revents);
        if (ret != 1) {
            printf("poll failed with exit code %d, revents %x\n", ret, fds[0].revents);
            break;
        }

        if (fds[0].revents & (POLLERR | POLLHUP)) {
            printf("file has been closed.\n");
            break;
        }

        if ((fds[0].revents & (POLLIN | POLLRDNORM)) == 0) {
            printf("poll somehow didn't return POLLRDNORM.\n");
            break;
        }

        nbytes = read(fd, buffer, sizeof(buffer));

        if(nbytes == -1) {
            printf("error reading file\n");
            close(fd);
            return 1;
        }

        printf("new data: ");
        printf("%.*s\n", (int) nbytes, buffer);

        sleep(5);
    }

    close(fd);
    return 0;
}

我相信这里发生的事情是因为它是一个普通文件而不是套接字或管道,

EOF
条件总是直接可读的 - 因此
select()
poll()
将立即返回,发出信号“输入"(文件末尾)可用。

你可能想看看这样的东西:https://github.com/emcrisostomo/fswatch

或者,如果你在Linux上,直接使用

inotify
https://www.man7.org/linux/man-pages/man7/inotify.7.html

© www.soinside.com 2019 - 2024. All rights reserved.