在 fd 0 上调用 "select" 需要 <Enter> 触发

Calling "select" on fd 0 requires <Enter> to fire

我正在尝试使用“select()”来测试一个键是否被敲击然后读取它。它有点工作,但如果在字符后按下{Enter}。
示例代码如下:

//  selectkb.c

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

//@ Main program
int
main(
  int argc,
  char **argv)
{
    int n;
    fd_set readfds;

    FD_ZERO( &readfds );
    FD_SET( 0, &readfds );
    printf( "calling select on fd 0...\n" );
    n = select( 1, &readfds, NULL, NULL, NULL );
    printf( "select reports %d fd ready\n", n );

    if( FD_ISSET( 0, &readfds ) ) {
      char c;
      printf( "select reports fd 0 ready.\n" );
      c = getchar();
      printf( "getchar returned \"%c\"\n", c );
    }
    else {
      printf( "fd 0 not ready.\n" );
    }

    return( 0 );
}

如果我按 A 没有任何反应, 但是如果我按 A{Enter},输出是:

calling select on fd 0...
select reports 1 fd ready
select reports fd 0 ready.
getchar returned "A"

如果我按 ABC{Enter} 输出是一样的

为什么需要 {Enter}?

(注意:我知道还有其他方法可以做到这一点,但在我实际的应用程序中,我 select 在一些套接字和 fd0 上,但为了简洁我省略了它)

我根据@Ben Voigt 的回复找到了解决方案。显然,默认情况下,终端在“规范”(熟)模式下运行,其中内核在按下 {Enter} 之前不会传送字符。解决方案是将终端设置为非规范(原始)模式。最简单的方法是使用 termios,如下面更新的代码所示。完成此操作后,字符将一个接一个地传送,并且 select() 的行为如我所愿。

//  selectkb.c

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


//@ Main program
int
main(
  int argc,
  char **argv)
{
    int n;
    fd_set readfds;
    struct termios attr;

    // SET RAW MODE
    tcgetattr( 0, &attr );
    attr.cflag &= ~ICANON;
    tcsetattr( 0, TCSANOW, &attr );

    FD_ZERO( &readfds );
    FD_SET( 0, &readfds );
    printf( "calling select on fd 0...\n" );
    n = select( 1, &readfds, NULL, NULL, NULL );
    printf( "select reports %d fd ready\n", n );

    if( FD_ISSET( 0, &readfds ) ) {
      char c;
      printf( "select reports fd 0 ready.\n" );
      c = getchar();
      printf( "getchar returned \"%c\"\n", c );
    }
    else {
      printf( "fd 0 not ready.\n" );
    }

    return( 0 );
}