非阻塞获取字符

  • 平台:Linux 3.2.0 x86(Debian 7)
  • 编译器:GCC 4.7.2(Debian 4.7.2-5)

我正在写一个函数,如果一个字符已经存在于标准input中,它将从标准input读取单个字符。 如果标准input为空,则该函数假设不执行任何操作并返回-1。 我googled非阻塞input,并被指向poll()或select() 。 首先,我尝试使用select(),但我无法得到它的工作,所以我尝试了poll(),并得出了相同的结论。 我不确定这些函数做了什么,但是从我所了解的poll()的文档来看,如果我这样称呼:

struct pollfd pollfds; pollfds = STDIN_FILENO; pollfds.events = POLLIN; poll(pollfds, 1, 0); 

如果(pollfds.revents&POLLIN)将为真,如果“高优先级数据以外的数据可以不被阻塞地读取”。 但是poll()总是在我的testing情况下超时。 我如何testingfunction可能是问题,但我想要的function正是我所testing的。 这里是目前的function和testing情况。

 #include <poll.h> #include <stdio.h> #include <unistd.h> int ngetc(char *c) { struct pollfd pollfds; pollfds.fd = STDIN_FILENO; pollfds.events = POLLIN; poll(&pollfds, 1, 0); if(pollfds.revents & POLLIN) { //Bonus points to the persons that can tell me if //read() will change the value of '*c' if an error //occurs during the read read(STDIN_FILENO, c, 1); return 0; } else return -1; } //Test Situation: //Try to read a character left in stdin by an fgets() call int main() { int ret = 0; char c = 0; char str[256]; //Make sure to enter more than 2 characters so that the excess //is left in stdin by fgets() fgets(str, 2, stdin); ret = ngetc(&c); printf("ret = %i\nc = %c\n", ret, c); return 0; } 

Solutions Collecting From Web of "非阻塞获取字符"

你正在做错误的IO,POSIX手册和所有其他相关的文档明确说,永远不要混淆IO在FILE *和文件描述符上完成。 你已经非常公然地破坏了这条规则。 这个规则是有原因的,因为FILE *使用了缓冲 ,这意味着在调用fgets之后, read将不会被read ,因为fgets已经将所有待处理的数据读入保存在FILE *结构中的缓冲区中。

因此,由于没有办法检查ISO C IO方法是否会被阻塞,我们只能使用文件描述符。

既然我们知道STDIN_FILENO只是数字0,我们可以使用

 fcntl (0, F_SETFL, O_NONBLOCK); 

这将把文件描述符0上的所有read变成非阻塞模式,如果你想使用不同的文件描述符,那么你可以只留下0,然后用dup复制它。

这样,你可以完全远离poll并实施ngetc作为

 ssize_t ngetc (char *c) { return read (0, c, 1); } 

或更好,宏观

 #define ngetc(c) (read (0, (c), 1)) 

因此,你可以简单的实现你正在寻找的东西。

编辑:如果您仍然担心终端缓冲输入,您可以随时更改终端的设置,请参阅如何禁用xterm程序中的输入行缓冲? 有关如何执行此操作的更多信息。

编辑:一个人不能使用fgetc而不是read的原因是因为使用fgets将无法正常工作。 当其中一个FILE * IO函数运行时,它从关联的文件描述符中读取所有的数据。 但是,一旦发生这种情况, poll将永远不会返回,因为它正在等待一个始终为空的文件描述符,并且在read也会发生同样的情况。 因此,我建议你按照文档的建议, 不要混合 (IO使用fgetsfgetc等)和文件描述符 (使用readwrite等IO)

你的代码有两个问题。

  1. 根据poll 手册 ,分配0超时将立即返回

    如果timeout的值是0,poll()应该立即返回。 如果超时值是-1,则poll()应该阻塞,直到请求的事件发生或直到呼叫被中断为止。

  2. fgets不会做你所期望的,它是从stdio库并且将缓冲区读取。 假设你输入3个字母并按回车,在fgets之后,第三个字母将不可用于poll

所以注释掉fgets行,并在poll中将-1赋值为timeout,然后再次运行以查看是否您想要的。