Making stdin non-blocking

后端 未结 4 1104
情深已故
情深已故 2020-12-18 07:37

I have an exercise where I am required to print a file slowly (1 second intervals) until the file ends, unless the user types a character.

So far, the program output

相关标签:
4条回答
  • 2020-12-18 08:18

    The terminal is buffering lines. It doesn't send text to the program until you press the Enter key. There might be a way to disable terminal line buffering, but I imagine it is beyond the scope of your assignment.

    It stops when you press Enter. However, it doesn't quit immediately. That's something you'll want to fix. Get rid of that sleep(1).

    Now your program spams text! You gave select a timeout of one second, didn't you?

    // set the time value to 1 second
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    

    The reason the timeout doesn't stick is because select is modifying the timeout value. From the man page:

    On Linux, select() modifies timeout to reflect the amount of time not slept; most other implementations do not do this. (POSIX.1-2001 permits either behavior.) This causes problems both when Linux code which reads timeout is ported to other operating systems, and when code is ported to Linux that reuses a struct timeval for multiple select()s in a loop without reinitializing it. Consider timeout to be undefined after select() returns.

    You will need to initialize the timeval before every call to select, not just once at the beginning of the program.

    0 讨论(0)
  • 2020-12-18 08:19

    This is a working version, using tcgetattr/tcsetattr:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/select.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <termios.h>
    
    int main(void) {
        FILE* infile;
        char str[100];
        fd_set readset;
        struct timeval tv;
        struct termios ttystate, ttysave;
    
        // open a file
        if((infile = fopen("infile", "r")) == NULL)
        {
            (void)printf("Couldn't open the file\n");
            exit(1);
        }
        // file was opened successfully
    
        //get the terminal state
        tcgetattr(STDIN_FILENO, &ttystate);
        ttysave = ttystate;
        //turn off canonical mode and echo
        ttystate.c_lflag &= ~(ICANON | ECHO);
        //minimum of number input read.
        ttystate.c_cc[VMIN] = 1;
    
        //set the terminal attributes.
        tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
    
        // while we are not at the end of a file
        while(fgets (str, 100, infile))
        {
            // set the time value to 1 second
            tv.tv_sec = 1;
            tv.tv_usec = 0;
    
            FD_ZERO(&readset);
            FD_SET(fileno(stdin), &readset);
    
            select(fileno(stdin)+1, &readset, NULL, NULL, &tv);
            // the user typed a character so exit
            if(FD_ISSET(fileno(stdin), &readset))
            {
                fgetc (stdin); // discard character
                break;
            }
            // the user didn't type a character so print the next line
            else
            {
                puts(str);
                // not needed: sleep(1);
            }
        }
    
        // clean up
        fclose(infile);
    
        ttystate.c_lflag |= ICANON | ECHO;
        //set the terminal attributes.
        tcsetattr(STDIN_FILENO, TCSANOW, &ttysave);
        // report success
        return 0;
    }
    

    The sleep(1); isn't needed anymore.

    0 讨论(0)
  • 2020-12-18 08:20

    Part of your problem is that you are using sleep(1) which will cause that line to take a full second to execute. If the user types a character, they will have to wait up to a full second before your program responds. So even once you get the non-blocking portion working you will still have issues.

    The solution is to use nanosleep or usleep to pause the program for less than 1 second. My recommendation would be to sleep for 1/100 of a second *using one of those functions) and check for user key presses every time. On the 100th time, output the next portion of the file. That way the file still goes the right speed, but the user can stop it whenever they want and the program will respond to their command very quickly.

    0 讨论(0)
  • 2020-12-18 08:37

    You'd want to make your program multithreaded. Create a thread that prints out the file every 1 second interval, and your main thread would be getting input from stdin, then signal the other thread to stop printing whenever you get the input

    0 讨论(0)
提交回复
热议问题