4

下面的代码是一个非阻塞读取terminalIO 的示例,但是当我在控制台上键入一个字符时,它不会立即将其打印出来。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;
}
4

2 回答 2

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

于 2013-07-07T20:38:42.957 回答
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;
}

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

于 2013-12-15T22:15:23.027 回答