Nonblocking Get Character

雨燕双飞 提交于 2019-12-03 12:11:15
randomusername

You're doing IO incorrectly, the POSIX manual and all other related documentation explicitly says never to mix IO done on FILE *s and file descriptors. You have very blatantly broken this rule. This rule is in place because FILE *s use buffering an this means that after a call to fgets there will be nothing left for read to get because fgets already read all pending data into a buffer that is kept in the FILE * structure.

So since there's no way to check if an ISO C IO method will block, we have to use file descriptors only.

Since we know that STDIN_FILENO is just the number 0, we can use

fcntl (0, F_SETFL, O_NONBLOCK);

this will turn all reads on file descriptor 0 to non-blocking mode, if you want to use a different file descriptor so that you can leave 0 alone then just use dup to duplicate it.

This way, you can stay away from poll completely and implement ngetc as

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

or better yet, a macro

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

Thus you get a simple implementation for what you're looking for.

Edit: If you are still worried about the terminal buffering the input, you can always change the terminal's settings, see How to disable line buffering of input in xterm from program? for more information on how to do this.

Edit: The reason that one could not use fgetc instead of read is for the same reason that using fgets won't work. When one of the FILE * IO functions is run, it reads all the data from the associated file descriptor. But once that happens, poll will never return because it's waiting on a file descriptor that's always empty, and the same thing will happen with read. Thus, I suggest that you follow the advice of the documentation and never mix streams (IO using fgets, fgetc, etc.) and file descriptors (IO using read, write, etc.)

adamsmith

There are two problems in your code.

  1. According to manual of poll, assigning 0 to timeout will return immediately

    If the value of timeout is 0, poll() shall return immediately. If the value of timeout is -1, poll() shall block until a requested event occurs or until the call is interrupted.

  2. fgets does not do what you expect, it is from stdio library and will buffer reads. Suppose you entered 3 letters and press enter, after fgets, the third letter won't be available to poll.

So comment out the fgets line and assign -1 to timeout in poll, and run it again to see if that's what you want.

I did not get the expected behavior with the answer above, and I actually had to take into account this answer as well which set the TTY in non canonical mode.

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <termios.h>

int main(int argc, char *argv[]) 
{
    struct termios t;
    tcgetattr(0, &t);
    t.c_lflag &= ~ICANON;
    tcsetattr(0, TCSANOW, &t);

    fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);

    printf("Starting loop (press i or q)...\n");

    for (int i = 0; ; i++) {
        char c = 0;
        read (0, &c, 1);

        switch (c) {
        case 'i':
            printf("\niteration: %d\n", i);
            break;

        case 'q':
            printf("\n");
            exit(0);
        }
    }

    return 0; 
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!