在管道上使用时,stdin上的select()的行为

时间:2017-04-10 08:14:51

标签: select pipe stdin

我试图理解在stdin上使用时对select()行为的观察,当它从管道接收数据时。

基本上我使用以下代码有一个简单的C程序: 的hello.c:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <termios.h>

int main(int argc, char *argv[])
{
  int flags, opt;
  int nsecs, tfnd;
  fd_set rfds;
  struct timeval tv;
  int retval;
  int stdin_fileno_p1 = STDIN_FILENO+1;
  char c;
  int n;

  /* Turn off canonical processing on stdin*/
  static struct termios oldt, newt;
  tcgetattr( STDIN_FILENO, &oldt);
  newt = oldt;
  newt.c_lflag &= ~(ICANON);
  tcsetattr( STDIN_FILENO, TCSANOW, &newt);

  while (1)
  {
    FD_ZERO(&rfds);
    FD_SET(STDIN_FILENO, &rfds);
    tv.tv_sec = 0;
    tv.tv_usec = 0;
    retval = select(stdin_fileno_p1, &rfds, NULL, NULL, &tv);

    if ( retval && (retval!=-1) )
    {
      n = read(STDIN_FILENO, &c, 1);
      write(STDOUT_FILENO, &c, 1);
    }
    else printf("No Data\n");
    usleep(100000);
  }
  tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
}

如果我按如下方式运行程序,我可以看到当程序运行时键入键时字符回显。未按下键时,按预期显示“无数据”。

./hello

但是,如果按如下方式使用程序,程序永远不会进入显示“无数据”的状态。而是重复显示最后一个字符“c”。

echo -n abc | ./hello

我对这一观察感到有些困惑,如果你能帮我理解观察到的行为,我将不胜感激。

1 个答案:

答案 0 :(得分:0)

问题是,当程序从STDIN_FILENO描述符中读取时,程序不会检测到文件结束条件。在echo写入c字符后,它将关闭管道的末尾,这将导致程序中的select立即返回,而read将返回0表明该描述符不再有数据可用。您的程序未检测到该情况。相反,它只是使用上一次成功write中缓冲区中剩余的任何字符调用read,然后重复循环。

要解决此问题,请在if (n==0) break;后执行read