我正在使用select系统等待输入。我也是在循环中这样做。 这是代码。
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调用仅在第一次正常工作。如果我在前5秒内输入,我得到yes作为输出但是在随后的迭代中fd(0)仍然未设置,无论我是否提供任何输入或不。任何想法如何解决这个问题。
答案 0 :(得分:3)
您的代码存在两个问题。
select
第一个是select
修改它给出的文件描述符集 - 在select
返回后,它们包含为I / O做好准备的文件描述符。这意味着如果超时在stdin上没有任何输入的情况下传递,rfds
将为空,并且对select
的下一次调用将等待空文件描述符集的输入 - 它将永远不会找到任何输入
stdin上的输入会将STDIN_FILENO
(0
)保留在集合中,但因为如果它出现了,你可以调用
FD_CLR(0,&rfds);
要从集合中删除stdin,select
也会在这种情况下等待空fd集。我知道为什么你把它放在那里,它与第二个问题有关(见下文)。在任何情况下,解决第一个问题的方法是在再次调用select
之前将stdin放回fd集:
FD_SET(0, &rfds);
第二个问题是,当你的程序在stdin上等待输入时,它永远不会消耗任何东西。这意味着如果您在每次使用select
调用FD_SET(0, &rfds);
之前修复文件描述符集,您的程序将在无限循环中反复打印“是”。
这是因为一旦输入等待在stdin中消耗,在该文件描述符上调用select
将导致select
检查是否有输入等待,认识到是,有,告诉你那个事实,你不用它做任何事情,只要求select
检查它是否仍在那里。无论你多久检查一次,它都是。
我不知道你究竟想要消耗什么输入,所以接下来就是猜测。如果用户输入内容,我假设您希望程序写“是”。当用户输入的东西并不总是清楚地定义时,但是通常的解释是说用户输入通常是基于行的 - 用户期望一旦他按下返回就会发生事情。然后,一种理智的方法是在数据出现时丢弃到下一个换行符,这样每次按下返回按钮都会产生“是”。这可能是这样的:
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;
}
答案 1 :(得分:1)
在某些实现中,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); };
您需要保留ns
的结果select
,并且需要处理错误案例。 rfds
后,您可能应该在 tv
之后检查select
和ns>0
...(<{1}})
正如我所评论的那样,您应该使用poll(2)代替select
(因为poll
更加友好C10K problem,并且因为系统的大小fd_set
是编译 - 限制最高文件描述符的时间)
请注意,如果 stdin 是 tty ,事情就相当复杂(因为通常 tty -s是内核缓冲,请参阅tty demystified页面。)
答案 2 :(得分:0)
从stdin获取数据后,代码使用FD_CLR(0,&amp; rfds)命令清除读取文件描述符集(rfds),因此在下一个循环中,您的读取文件描述符为空