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
,并且需要处理错误情况。您可能应该检查 rfds
和 tv
在 您的 select
- 当它成功时 (ns>0
)...
正如我评论的那样,您应该友好地使用 poll(2) instead of select
(since poll
is more C10K problem,并且由于您系统的 fd_set
的大小是 compile-time limiting the highest file descriptor )
请注意,如果您的 stdin 是 tty 事情就相当复杂(因为通常 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),因此在下一个循环中,您的读取文件描述符为空
我正在使用 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
,并且需要处理错误情况。您可能应该检查 rfds
和 tv
在 您的 select
- 当它成功时 (ns>0
)...
正如我评论的那样,您应该友好地使用 poll(2) instead of select
(since poll
is more C10K problem,并且由于您系统的 fd_set
的大小是 compile-time limiting the highest file descriptor )
请注意,如果您的 stdin 是 tty 事情就相当复杂(因为通常 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),因此在下一个循环中,您的读取文件描述符为空