select()响应stdin,但不响应/dev/tty

select()响应stdin,但不响应/dev/tty,c,select,console,termios,C,Select,Console,Termios,这是一个非常类似的问题,但我似乎没有犯相同的错误FD_集作为OP那里 在运行Ubuntu20.04 LTS for Desktop的Linux机器上,我试图在阻塞模式下无条件调用fgetc和非阻塞模式下从控制台读取一个无缓冲字符,并反复询问select是否值得调用fgetc 如果我正在阅读标准文本,这两种模式都可以工作。但是,如果我想独立于stdin是否被重定向而从控制台显式读取,我的理解是应该打开/dev/tty。这也适用于阻塞模式,但不适用于非阻塞模式:尽管疯狂按键,select仍会在每个超

这是一个非常类似的问题,但我似乎没有犯相同的错误FD_集作为OP那里

在运行Ubuntu20.04 LTS for Desktop的Linux机器上,我试图在阻塞模式下无条件调用fgetc和非阻塞模式下从控制台读取一个无缓冲字符,并反复询问select是否值得调用fgetc

如果我正在阅读标准文本,这两种模式都可以工作。但是,如果我想独立于stdin是否被重定向而从控制台显式读取,我的理解是应该打开/dev/tty。这也适用于阻塞模式,但不适用于非阻塞模式:尽管疯狂按键,select仍会在每个超时结束时返回0。我不知道为什么stdin和/dev/tty在这方面的行为会有所不同。他们的termios旗帜看起来是一样的

$gcc foo.c 美元/年 c_iflag=25862 c_of lag=5 c_cflag=191 c_lflag=35387 好 在阻塞模式下从标准输入读取字符“g” 我们在等。。。0 0 在非阻塞模式下从stdin读取字符“g” $./a.out/dev/tty c_iflag=25862 c_of lag=5 c_cflag=191 c_lflag=35387 好 在阻塞模式下从/dev/tty读取字符“g” 我们在等。。。0 0 0 0 0 0 0 0 0 0 太慢:在非阻塞模式下从/dev/tty不读取任何内容 $ggggggggggg 如果我试图使用/dev/console或/dev/tty0,我只会因为fopen中的一个空文件*而失败。这是foo.c的源代码。我错过了什么

#include <stdio.h>
#include <sys/select.h>
#include <termios.h>

int main( int argc, const char * argv[] )
{
    
    const char * name = "stdin";
    FILE * filePointer = stdin;
    int fileDescriptor;
    struct timeval timeout;
    struct termios term;
    fd_set rdset;
    int result, iCycle, nCycles = 10;
    char c;

    if( argc > 1 ) /* open the user-specified file instead of stdin */
    {
        name = argv[ 1 ];
        filePointer = fopen( name, "r" );
    }
    if( !filePointer ) return fprintf( stderr, "failed to open %s", name );
    
    fileDescriptor = fileno( filePointer );

    /* make things unbuffered, non-canonical, non-echoey */
    setvbuf( filePointer, NULL, _IONBF, 0 );
    tcgetattr( fileDescriptor, &term );
    printf("c_iflag = %d\n", term.c_iflag);
    printf("c_oflag = %d\n", term.c_oflag);
    printf("c_cflag = %d\n", term.c_cflag);
    printf("c_lflag = %d\n", term.c_lflag);
    term.c_lflag &= ~ICANON;
    term.c_lflag &= ~ECHO;
    tcsetattr( fileDescriptor, TCSANOW, &term );
    
    /* get an unbuffered character (blocking mode) */
    fprintf( stdout, "\nWell?? " );
    fflush( stdout );
    c = fgetc( filePointer );
    fprintf( stdout, "\nread character '%c' in blocking mode from %s\n", c, name );
    fflush( stdout );

    /* get an unbuffered character (non-blocking mode) */
    fprintf( stdout, "\nWe're waiting... " );
    fflush( stdout );
    for( iCycle = 0; iCycle < nCycles; iCycle++ )
    {
        FD_ZERO( &rdset );
        FD_SET( fileDescriptor, &rdset );
        timeout.tv_sec  = 1;
        timeout.tv_usec = 0;
        result = select( 1, &rdset, NULL, NULL, &timeout );
        if( result > 0 ) break;
        fprintf( stdout, "%d ", result );
        fflush( stdout );
        if( result > 0 ) break;
    }
    if( iCycle < nCycles )
    {
        c = fgetc( filePointer );
        fprintf( stdout, "\nread character '%c' in non-blocking mode from %s\n", c, name );
        fflush( stdout );
    }
    else
    {
        fprintf( stdout, "\ntoo slow: read nothing in non-blocking mode from %s\n", name );
        fflush( stdout );
    }
    
    /* restore canonicality (avoid having to type `reset` after every run) */
    term.c_lflag |= ICANON;
    term.c_lflag |= ECHO;
    tcsetattr( fileDescriptor, TCSANOW, &term );
    if( filePointer != stdin ) fclose( filePointer );
    return 0;
}

问题是要选择的第一个参数。选择仅检查每个FD集中的第一个nfds描述符。由于您指定了1,它只检查FD 0上的输入,而忽略了用于/dev/tty的FD

将其更改为:

select(fileDescriptor+1, &rdset, NULL, NULL, &timeout );

那个作品谢谢你。但我很困惑。据我所知,fileDescriptor只是一个句柄,在我的例子中,它的值3是任意的。这不是计数。但你是说我应该用它来计数?我应该要求select检查集合的前4个描述符,据我所知,我只向。。。?你说它会检查FD0。。。为什么FD0会出现在场景中?同样,我的理解肯定有缺陷,因为我认为fd0意味着stdin,我不希望它检查stdin…第一个参数必须高于您希望它检查的最大FD。因此,如果值为3,则需要至少检查FDs 0到3,因此计数必须为4。即使在集合中只设置了1个FD,也需要知道要查找的最大FD。