select() 系统调用未按预期工作

The select() system call isn't working as expected

我正在使用 select 系统等待 input.Also 我正在循环执行此操作。 这是代码。

int main()
{
    fd_set rfds;
    struct timeval tv;  

    FD_ZERO(&rfds);
    FD_SET(0,&rfds);

    tv.tv_sec = 5;
        tv.tv_usec = 0;

    while(1)
    {
        select(1,&rfds,NULL,NULL,&tv);
        if(FD_ISSET(0,&rfds))
        {
            write(STDOUT_FILENO,"yes",3);
            FD_CLR(0,&rfds);    
        }

        tv.tv_sec = 5;
            tv.tv_usec = 0; 
    }   
    return 0;

}

现在的问题是 select 调用仅在第一个 time.If 时工作正常 我在前 5 秒内输入 我得到 yes 作为输出 但随后在接下来的迭代中 fd( 0) 保持未设置,无论我是否提供任何输入或 not.Any 想法如何解决此问题。

在某些实现中 select(2) 允许修改文件描述符集和超时。所以你应该在 select

之前的循环中设置这些
while(1) {
  FD_ZERO(&rfds);
  FD_SET(0,&rfds);
  tv.tv_sec = 5;
  tv.tv_usec = 0;
  int ns = select(1,&rfds,NULL,NULL,&tv);
  if (ns < 0 && errno == EINTR) continue;
  else if (ns < 0) { perror("select"); exit(EXIT_FAILURE); };

您需要保留 select 的结果 ns,并且需要处理错误情况。您可能应该检查 rfdstv 您的 select - 当它成功时 (ns>0)...

正如我评论的那样,您应该友好地使用 poll(2) instead of select (since poll is more C10K problem,并且由于您系统的 fd_set 的大小是 compile-time limiting the highest file descriptor )

请注意,如果您的 stdintty 事情就相当复杂(因为通常 tty-s 是 内核 缓冲的,请参阅 tty demystified 页)。

您的代码有两个问题。

问题一:select 空文件描述符集

首先是 select 修改了给定的文件描述符集——在 select returns 之后,它们包含为 [=56= 准备的文件描述符].这意味着如果超时没有对 stdin 进行任何输入,rfds 将为空,并且对 select 的下一次调用将等待空文件描述符集上的输入——它永远不会找到任何.

stdin 上的输入将在集合中保留 STDIN_FILENO(即 0),但是如果它出现,您调用

FD_CLR(0,&rfds);

从集合中删除 stdin,select 在这种情况下也会等待一个空的 fd 集合。不过,我知道你为什么把它放在那里,它与第二个问题有关(见下文)。无论如何,第一个问题的解决方法是在再次调用 select 之前将 stdin 放回 fd 集中:

FD_SET(0, &rfds);

问题二:输入永远在 stdin 中

第二个问题是,当您的程序等待 stdin 上的输入时,它从不使用任何输入。这意味着如果您在每次使用 FD_SET(0, &rfds); 调用 select 之前修复文件描述符集,您的程序最终将在无限循环中一遍又一遍地打印 "yes"。

这是因为一旦输入等待在 stdin 中被使用,在该文件描述符上调用 select 将导致 select 检查是否有输入在等待,识别是的,有,告诉你知道这个事实,于是你什么都不做,只是问 select 检查它是否仍然存在。不管你多久检查一次,它是什么。

我不知道你到底想如何使用输入,所以接下来的部分是猜测。我假设您希望程序在用户输入内容时写入 "yes"。当用户输入某些内容时,并不总是明确定义,但一种常见的解释是用户输入通常是基于行的——用户希望一旦他按下 return 就会发生事情。那么,一个明智的方法是在数据出现时丢弃直到下一个换行符,这样每次按下 return 按钮都会产生一个 "yes"。这可能看起来像这样:

while ((c = getchar()) != EOF) && c != '\n'); // discard until end-of-line

int c;.

放在一起

总而言之,这可能会满足您的要求:

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

int main()
{
  fd_set rfds;
  struct timeval tv;
  int c;

  FD_ZERO(&rfds);
  FD_SET(0,&rfds);

  tv.tv_sec = 5;
  tv.tv_usec = 0;

  while(1)
  {
    select(1,&rfds,NULL,NULL,&tv);
    if(FD_ISSET(0, &rfds))
    {
      puts("yes");
      while((c = getchar()) != EOF && c != '\n'); // consume input
    } else {
      puts("no");
      FD_SET(0, &rfds); // place stdin back in the fd set
    }

    tv.tv_sec = 5;
    tv.tv_usec = 0; 
  }

  return 0;
}

从标准输入获取数据后,代码会使用 FD_CLR(0,&rfds) 命令清除读取文件描述符集 (rfds),因此在下一个循环中,您的读取文件描述符为空