如果没有设置规范模式,非阻塞读取失败?

时间:2013-05-05 08:26:47

标签: c stdin nonblocking canonical-link

下面的代码是terminal IO的非阻塞读取的示例,但是当我在控制台上键入字符时,它不会立即打印出来。 Perpaps你会说我应该私下设置stty -icanon,所以禁用规范模式,这确实有效,但我想即使我没有禁用stty icanon,终端的非阻塞读取是character-orientedcannonical模式只是唤醒阻塞过程,但我的进程没有阻塞,如果我们输入一个字符,那么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;
}

2 个答案:

答案 0 :(得分:1)

O我已经明白了,在打开时使用O_NONBLOCK标志,只是告诉open不要等待调制解调器准备就绪,如果你试图打开一个终端。

发现此来源非常有帮助。 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

答案 1 :(得分: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;
}

该功能尚未经过广泛测试,因此您应自行测试。

相关问题