2013-05-05 52 views
4

下面的代碼是一個非阻塞讀取terminal IO的示例,但是當我在控制檯上鍵入字符時,它不會立即將其打印出來。 Perpaps你會說我應該私下設置stty -icanon,所以canonical模式被禁用,確實有效,但我認爲即使我沒有禁用stty icanon,非阻塞讀終端是character-oriented,cannonical模式只是喚醒阻塞過程,但我的進程沒有阻塞,如果我們鍵入一個字符,那麼fd是可讀的,所以它應該立即打印出字符。如果規範模式未設置,非阻塞讀取失敗?

#include <unistd.h> 
#include <fcntl.h> 
#include <errno.h> 
#include <string.h> 
#include <stdlib.h> 

#define MSG_TRY "try again\n" 

int main(void) 
{ 
char buf[10]; 
int fd, n; 
fd = open("/dev/tty", O_RDONLY|O_NONBLOCK); 
if(fd<0) { 
    perror("open /dev/tty"); 
    exit(1); 
} 
tryagain: 
    n = read(fd, buf, 10); 
    if (n < 0) { 
     if (errno == EAGAIN) { 
      sleep(1); 
      write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY)); 
      goto tryagain; 
     }  
    perror("read /dev/tty"); 
    exit(1); 
    } 
write(STDOUT_FILENO, buf, n); 
close(fd); 
return 0; 
} 

回答

1

O作爲我明白它,使用O_NONBLOCK標誌,同時開,只是告訴開不等待調制解調器準備就緒,比如你試圖打開一個終端。

發現此源非常有幫助。 http://en.wikibooks.org/wiki/Serial_Programming/termios#Opening.2FClosing_a_Serial_Device

我今天正在用tty(RS232端口)設備做一些工作。 http://www.gnu.org/software/libc/manual/html_node/Terminal-Modes.html#Terminal-Modes

你怎麼知道終端是規範還是原始的?如果我正確理解你,你說你已經把終端設置爲非規範的(原始的),但是它後來改變了。它是否正確? 在那種情況下,你的代碼中你是否將設備設置爲非規範?

如果你想要原始模式,有一個叫做cfmakeraw()的函數。 並且不要忘記用tcsetattr()設置你的屬性。 例如 const struct termios yourtermios yourtermios.c_cc [VTIME] = 0; yourtermios.c_cc [VMIN] = 1; 或者你認爲合適的任何價值。

以下是關於規範與否的很好的信息來源。 http://www.gnu.org/software/libc/manual/html_node/Canonical-or-Not.html#Canonical-or-Not

0

如果你想要一個帶有單個未緩衝字符的函數,這裏是我自己編寫的一個函數。它將讀取單個字符(大部分時間是一個關鍵筆劃),而不用等待用戶按下回車鍵。

#include <unistd.h> 
#include <termios.h> 
#include <errno.h> 

int ugetc(char *c) 
{ 
    if(c == NULL) return EINVAL; 

    struct termios term; 

     //Get the current terminal settings so that only 
     //the settings we want to change are changed 
    if(tcgetattr(STDIN_FILENO, &term) == -1) return errno; 

     //Save the original terminal settings 
    unsigned int c_lflag = term.c_lflag; 
    unsigned int vmin = term.c_cc[VMIN]; 
    unsigned int vtime = term.c_cc[VTIME]; 

     //If line buffering is turned on then turn if off 
    if(term.c_lflag & ICANON) term.c_lflag ^= ICANON; 

     //Make sure that read() will wait for a single character 
    term.c_cc[VMIN] = 1; 
    term.c_cc[VTIME] = 0; 

     //Apply the changed settings 
    if(tcsetattr(STDIN_FILENO, TCSANOW, &term) == -1) return errno; 

     //Verify that changes were made properly see "man tcgetattr" for more details 
    if(tcgetattr(STDIN_FILENO, &term) == -1) return errno; 

     //Verify that changes were made properly see "man tcgetattr" for more details 
    if 
    (
     term.c_lflag & ICANON 
     || term.c_cc[VMIN] != 1 
     || term.c_cc[VTIME] != 0 
    ) 
     //The value returned can be change to any valid errno macro 
    return ECANCELED; 

     //Save the read() return value for later processing 
    int ret = read(STDIN_FILENO, c, 1); 

     //Restore the old settings saved from earlier 
    term.c_lflag = c_lflag; 
    term.c_cc[VMIN] = vmin; 
    term.c_cc[VTIME] = vtime; 

     //Apply the restored settings 
    if(tcsetattr(STDIN_FILENO, TCSANOW, &term) == -1) return errno; 

     //Verify the changes were successful see "man tcgetattr" for more details 
     //NOTE: If tcgetattr() fails to restore the original settings the terminal 
     //will stay unbuffered 
    if(tcgetattr(STDIN_FILENO, &term) == -1) return errno; 

     //Verify the changes were successful see "man tcgetattr" for more details 
    if 
    (
     term.c_lflag != term.c_lflag 
     || term.c_cc[VMIN] != vmin 
     || term.c_cc[VTIME] != vtime 
    ) 
    return ECANCELED; 

     //If a read error occured return errno 
     //NOTE: If an error occurs we want to restore the old 
     //settings anyway so we save the return value and check 
     //it after the old settings have been restored 

    if(ret == -1) return errno; 

    return 0; 
} 

該功能還沒有得到廣泛的測試,所以你應該自己測試一下。