非阻塞获取角色

Joh*_*inz 10 c linux stdin polling

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

我正在编写一个函数,如果stdin中已存在一个字符,则从stdin中读取单个字符.如果stdin为空,则该函数假设不执行任何操作并返回-1.我用Google搜索非阻塞输入并指向poll()select().首先我尝试使用select(),但我无法让它工作,所以我尝试了poll()并得出了相同的结论.我不确定这些函数究竟是做什么的,但是根据我对poll()的文档的理解,如果我这样调用它:

struct pollfd pollfds;
pollfds = STDIN_FILENO;
pollfds.events = POLLIN;
poll(pollfds, 1, 0);
Run Code Online (Sandbox Code Playgroud)

if(pollfds.revents&POLLIN)如果"可以在不阻塞的情况下读取高优先级数据以外的数据",则为真.但是poll()总是在我的测试情况下超时.我如何测试函数可能是问题,但我想要的功能正是我正在测试的.这是当前的功能和测试情况.

#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;
}
Run Code Online (Sandbox Code Playgroud)

ran*_*ame 9

您正在做错误的IO,POSIX手册和所有其他相关文档明确表示永远不要混淆在FILE *s和文件描述符上完成的IO .你非常公然违反了这条规则.此规则已到位,因为FILE *s使用缓冲这意味着在调用之后fgets将没有任何东西read可以获取,因为fgets已经将所有待处理数据读入保留在FILE *结构中的缓冲区中.

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

因为我们知道这STDIN_FILENO只是数字0,所以我们可以使用

fcntl (0, F_SETFL, O_NONBLOCK);
Run Code Online (Sandbox Code Playgroud)

这将把read文件描述符0上的所有内容转换为非阻塞模式,如果你想使用不同的文件描述符,那么你可以单独留0,然后只是dup用来复制它.

这样一来,就可以从远离poll完全和实施ngetc

ssize_t 
ngetc (char *c)
{
  return read (0, c, 1);
}
Run Code Online (Sandbox Code Playgroud)

或者更好,一个宏

#define ngetc(c) (read (0, (c), 1))
Run Code Online (Sandbox Code Playgroud)

因此,您可以获得所需内容的简单实现.

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

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