为什么在这种情况下 stdin fd 还没有准备好
Why in this case the stdin fd is not ready
根据 Linux Programmer's Manual,poll
可以等待一组文件描述符中的一个准备好执行 I/O。
根据我的理解,如果我将POLLIN
添加到events
,poll
将return与一个> 0
整数,当至少有一个可以阅读的 fd。
考虑以下代码,在这段代码中,我希望程序在我键入字符 \n
后立即回显我的输入。
int main(){
char buffer[maxn];
while (true) {
struct pollfd pfd[1];
std::memset(pfd, 0, sizeof pfd);
pfd[0].fd = STDIN_FILENO;
pfd[0].events = POLLIN;
int ret = poll(pfd, 1, 1000);
if (ret < 0) {
}
else if (ret == 0) {
}
else {
if ((pfd[0].revents & POLLIN) == POLLIN) {
int n;
n = fscanf(stdin, "%s", &buffer);
if(n > 0){
printf("data from stdin: %s\n", buffer);
}
}else if((pfd[1].revents & POLLHUP) == POLLHUP){
break;
}
}
}
}
当我输入时
aa bb cc dd
我认为 fscanf
没有从标准输入中检索所有数据,因为它只读取 aa
。所以当循环重新开始时,stdin 的 fd 应该仍然准备就绪。结果,(pfd[0].revents & POLLIN) == POLLIN
仍然存在,所以我认为我们可以看到以下输出
data from stdin: aa
data from stdin: bb
data from stdin: cc
data from stdin: dd
然而,实际上只打印了第一行。我在这里感到奇怪,我认为这与 epoll
的 Edge-triggered mode 类似。然而,poll
是水平触发的。
那么你能解释一下为什么 fscanf
会发生这种情况吗?
轮询在文件 描述符 级别工作,而 fscanf
在更高文件 句柄 级别工作。
在较高级别,C 运行时库可以自由缓存输入流,这种方式会影响您在较低级别看到的内容。
例如(这可能就是这里发生的情况),当你第一次 fscanf
你的单词 aa
时,整个 行 是从文件描述符和缓存,在第一个单词返回给你之前。
随后的 fscanf
(没有中间的 poll
)将首先检查缓存以获取下一个单词,如果不存在,它将返回到文件描述符以获得更多意见。
不幸的是,您在执行此操作之前检查轮询事件会导致问题。就文件 descriptor 级别而言,entire 行已被您的第一个 fscanf
读取,因此没有进一步的输入可用 -因此,poll
将等待此类信息 可用。
如果您更改:
,您可以看到实际效果
n = fscanf(stdin, "%s", buffer);
进入:
n = read(STDIN_FILENO, buffer, 3);
并将 printf
更改为:
printf("data from stdin: %*.*s\n", n, n, buffer);
在这种情况下,您 do 会在您按下 ENTER 键后立即获得您期望的输出:
data from stdin: aa
data from stdin: bb
data from stdin: cc
data from stdin: dd
请记住示例代码最多读取 三个 个字符(如 aa<space>
)而不是一个单词。更多的是说明问题是什么而不是给你解决方案(匹配你的问题"Can you explain why this happens?")。
解决方案不混合描述符和句柄I/O当后者的缓存会影响前者时。
根据 Linux Programmer's Manual,poll
可以等待一组文件描述符中的一个准备好执行 I/O。
根据我的理解,如果我将POLLIN
添加到events
,poll
将return与一个> 0
整数,当至少有一个可以阅读的 fd。
考虑以下代码,在这段代码中,我希望程序在我键入字符 \n
后立即回显我的输入。
int main(){
char buffer[maxn];
while (true) {
struct pollfd pfd[1];
std::memset(pfd, 0, sizeof pfd);
pfd[0].fd = STDIN_FILENO;
pfd[0].events = POLLIN;
int ret = poll(pfd, 1, 1000);
if (ret < 0) {
}
else if (ret == 0) {
}
else {
if ((pfd[0].revents & POLLIN) == POLLIN) {
int n;
n = fscanf(stdin, "%s", &buffer);
if(n > 0){
printf("data from stdin: %s\n", buffer);
}
}else if((pfd[1].revents & POLLHUP) == POLLHUP){
break;
}
}
}
}
当我输入时
aa bb cc dd
我认为 fscanf
没有从标准输入中检索所有数据,因为它只读取 aa
。所以当循环重新开始时,stdin 的 fd 应该仍然准备就绪。结果,(pfd[0].revents & POLLIN) == POLLIN
仍然存在,所以我认为我们可以看到以下输出
data from stdin: aa
data from stdin: bb
data from stdin: cc
data from stdin: dd
然而,实际上只打印了第一行。我在这里感到奇怪,我认为这与 epoll
的 Edge-triggered mode 类似。然而,poll
是水平触发的。
那么你能解释一下为什么 fscanf
会发生这种情况吗?
轮询在文件 描述符 级别工作,而 fscanf
在更高文件 句柄 级别工作。
在较高级别,C 运行时库可以自由缓存输入流,这种方式会影响您在较低级别看到的内容。
例如(这可能就是这里发生的情况),当你第一次 fscanf
你的单词 aa
时,整个 行 是从文件描述符和缓存,在第一个单词返回给你之前。
随后的 fscanf
(没有中间的 poll
)将首先检查缓存以获取下一个单词,如果不存在,它将返回到文件描述符以获得更多意见。
不幸的是,您在执行此操作之前检查轮询事件会导致问题。就文件 descriptor 级别而言,entire 行已被您的第一个 fscanf
读取,因此没有进一步的输入可用 -因此,poll
将等待此类信息 可用。
如果您更改:
,您可以看到实际效果n = fscanf(stdin, "%s", buffer);
进入:
n = read(STDIN_FILENO, buffer, 3);
并将 printf
更改为:
printf("data from stdin: %*.*s\n", n, n, buffer);
在这种情况下,您 do 会在您按下 ENTER 键后立即获得您期望的输出:
data from stdin: aa
data from stdin: bb
data from stdin: cc
data from stdin: dd
请记住示例代码最多读取 三个 个字符(如 aa<space>
)而不是一个单词。更多的是说明问题是什么而不是给你解决方案(匹配你的问题"Can you explain why this happens?")。
解决方案不混合描述符和句柄I/O当后者的缓存会影响前者时。