簡體   English   中英

使stdin無阻塞

[英]Making stdin non-blocking

我有一個練習,我需要緩慢打印文件(間隔1秒),直到文件結束,除非用戶鍵入一個字符。

到目前為止,程序以一秒的間隔輸出文件很好,但是當我輸入一個字符時,沒有任何反應。 我的猜測是我以某種方式使用選擇錯誤。

這是我最終提交的最終計划。

#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    FILE* infile;
    char str[100];
    fd_set readset;
    struct timeval tv;

    // open a file
    if((infile = fopen("infile", "r")) == NULL)
    {
        (void)printf("Couldn't open the file\n");
        exit(1);
    }
    // file was opened successfully
    else
    {       
        // while we are not at the end of a file
        while(fgets(str, 100, infile) != NULL)
        {
            FD_ZERO(&readset);
            FD_SET(fileno(stdin), &readset);
            // set the time value to 1 second
            tv.tv_sec = 1;
            tv.tv_usec = 0;
            select(fileno(infile)+1, &readset, NULL, NULL, &tv);
            // the user typed a character so exit
            if(FD_ISSET(fileno(stdin), &readset))
            {
                fclose(infile);
                exit(0);
            }
            // the user didn't type a character so print the next line
            else
            {
                fgets(str, 100, stdin);
                puts(str);
            }
        }

        // clean up
        fclose(infile);
    }

    // report success
    return 0;
}

謝謝您的幫助!

這是一個工作版本,使用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;
}

sleep(1); 不再需要了。

終端是緩沖線路。 在按Enter鍵之前,它不會向程序發送文本。 可能有一種方法可以禁用終端線緩沖,但我認為它超出了您的任務范圍。

Enter鍵時停止。 但是,它不會立即退出。 這是你想要解決的問題。 擺脫那種sleep(1)

現在你的程序垃圾文本! 你給了select超時一秒,不是嗎?

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

超時不堅持的原因是因為select正在修改超時值。 手冊頁

在Linux上,select()修改超時以反映未睡眠的時間; 大多數其他實現不會這樣做。 (POSIX.1-2001允許任何一種行為。)當讀取超時的Linux代碼移植到其他操作系統時,以及當代碼移植到Linux時,在循環中為多個select()s重用struct timeval時,這會導致問題重新初始化它。 在select()返回后,請考慮超時未定義。

您需要在每次調用選擇之前初始化timeval ,而不僅僅是在程序開頭的一次。

你想讓你的程序多線程。 創建一個每1秒間隔打印出文件的線程,主線程將從stdin獲取輸入,然后在獲得輸入時發出另一個線程的信號以停止打印

你的部分問題是你正在使用sleep(1) ,這會導致該行需要一整秒才能執行。 如果用戶鍵入一個字符,他們將需要等待一整秒才能在程序響應之前。 因此,即使您使非阻塞部分工作,您仍然會遇到問題。

解決方案是使用nanosleepusleep暫停程序不到1秒。 我的建議是使用其中一種功能睡眠1/100秒*並每次檢查用戶按鍵。 在第100次,輸出文件的下一部分。 這樣文件仍然以正確的速度運行,但是用戶可以隨時停止它,程序將很快響應它們的命令。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM