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

问题描述

以下代码在运行时不响应终端输入。我已经在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,&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]);
                else
                if (keys[i])
                    printf("Key '\\%03o' = 0x%02x = %u pressed\n",keys[i]);
                else
                    printf("NUL key (0) pressed\n");
            }
            fflush(stdout);
        }
    }
    
    // RESTORE TERMINAL SETTINGS
    tcsetattr(STDIN_FILENO,TCSAFLUSH,&tty_cfg_cache);

    return EXIT_SUCCESS;

}