为什么 epoll_wait 不响应终端输入?

Why is epoll_wait not responding to terminal input?

下面的代码,当运行时,不响应终端输入。我在 Debian 10.5.0 和 CentOS 7 下有 运行 这个,结果相同。我错过了什么?

#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>

int main(void) {

    char buffer[4096];
    int fd = epoll_create1(EPOLL_CLOEXEC);

    struct epoll_event event;
    int ctl_ret;
    event.events = EPOLLIN;
    event.data.fd = STDIN_FILENO;
    ctl_ret = epoll_ctl(fd, EPOLL_CTL_ADD, STDIN_FILENO, &event);
    if (ctl_ret) {
        perror("epoll_ctl");
        return 1;
    }

    int nr_event;
    for (;;) {
        fprintf(stderr, "Starting epoll_wait\n");
        nr_event = epoll_wait(fd, &event, 1, -1);
        if (nr_event < 0) {
            perror("epoll_wait");
            return 1;
        }
        fprintf(stderr, "Reading: %d\n", event.data.fd);
        printf("%ld\n", read(0, buffer, sizeof(buffer)));
    }

}

正如 Basile Starynkevitch 提到的,我需要通过 termios(3) 更新终端配置。

我在下面提供了修改后的代码以供参考。对于所需的更改,请参阅 // TERMINAL SETUP// EPOLL SETUP 注释之间的代码。

#include <errno.h>
#include <linux/input.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <termios.h>
#include <unistd.h>

int main(void) {

    // TERMINAL SETUP
    struct termios tty_cfg, tty_cfg_cache;

    // Check that fd is a TTY
    if (!isatty(STDIN_FILENO)) {
        fprintf(stderr, "Standard input is not a terminal.\n");
        return EXIT_FAILURE;
    }

    // Save old terminal configuration
    if (tcgetattr(STDIN_FILENO, &tty_cfg_cache) == -1 ||
        tcgetattr(STDIN_FILENO, &tty_cfg) == -1) {
        fprintf(stderr, "Cannot get terminal settings: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    // Set new terminal configuration
    tty_cfg.c_iflag &= ~(IGNBRK | BRKINT | PARMRK);
    tty_cfg.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN | TOSTOP);
    tty_cfg.c_cc[VMIN] = 0;
    tty_cfg.c_cc[VTIME] = 0;
    tty_cfg.c_cc[VSTART] = 0;
    tty_cfg.c_cc[VSTOP] = 0;
    if (tcsetattr(STDIN_FILENO, TCSANOW, &tty_cfg) == -1) {
        const int  saved_errno = errno;
        tcsetattr(STDIN_FILENO, TCSANOW, &tty_cfg_cache);
        fprintf(stderr, "Cannot set terminal settings: %s.\n", strerror(saved_errno));
        return EXIT_FAILURE;
    }

    // EPOLL SETUP
    // Create epoll
    int epfd = epoll_create1(EPOLL_CLOEXEC);

    // Add stdin control interface to epoll
    struct epoll_event event;
    int ctl_ret;
    event.events = EPOLLIN;
    event.data.fd = STDIN_FILENO;
    ctl_ret = epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &event);
    if (ctl_ret) {
        perror("epoll_ctl");
        return 1;
    }

    // LOOP AND MONITOR EPOLL
    int nr_event;
    unsigned char keys[16];
    ssize_t n;
    size_t i, done;
    done = 0;
    while (!done) {
        
        // Start epoll_wait
        nr_event = epoll_wait(epfd, &event, 1, -1);
        if (nr_event < 0) {
            perror("epoll_wait");
            return 1;
        }
        
        // Read STDIN_FILENO
        n = read(STDIN_FILENO, keys, sizeof keys);
        if (n > 0) {
            for (i = 0; i < n; i++) {
                // Quit if 'q' or 'Q' is pressed
                if (keys[i] == 'q' || keys[i] == 'Q')
                    done = 1;
                if (keys[i] >= 32 && keys[i] <= 126)
                    printf("Key '%c' = 0x%02x = %u pressed\n", keys[i], keys[i], keys[i]);
                else
                if (keys[i])
                    printf("Key '\%03o' = 0x%02x = %u pressed\n", keys[i], keys[i], keys[i]);
                else
                    printf("NUL key (0) pressed\n");
            }
            fflush(stdout);
        }
    }
    
    // RESTORE TERMINAL SETTINGS
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &tty_cfg_cache);

    return EXIT_SUCCESS;

}