linux关闭终端缓存,立即响应输入

我们平时在Linux下编写的终端程序,在输入内容时,要先打入内容再按回车。而许多系统程序,比如询问"y/n",并不要求你输入回车,你按一下“y”或者按一下“n”,程序立即就响应了。这是怎么做到的呢?

1.终端缓存

通过termios 结构体的c_lflag 成员,可以决定是否进行缓冲。有缓冲就代表着,在输入信息时,要按回车后字符才真正传给程序。而关闭缓冲,就代表,你按下一个键,就立刻传给程序了。

要注意,按下几个键再立即传给程序也是可以设置的,他是由termios 结构体的c_cc[VMIN] 成员来决定。当然,设为1,就代表按下一个键立刻响应。

2.关于如何查看和编辑终端属性

这也就是对termios 结构体的操作,可见我以前的博客《linux下编程查看终端设置》

3.保护现场

在你的程序中,修改 termios 结构体,通过系统调用实现了关闭缓存。但如果你不在程序结束前把设置改回来,那么当程序退出后,还是没有缓存的……比如,你想打个“sudo”,没等你按一下s,屏幕上就立刻显示“s:未找到命令”。

所以,在刚进入程序时,你要将原来终端的设置保存起来。在退出之前,再将其恢复。

4.示例程序
这是《Unix/Linux编程实践教程》中的一个示例,效果是提出了一个问题,问你按y还是按n,你一按下,就立刻响应,执行相应的操作,效果如下:

image

5.程序代码及注释如下:

#include
#include

#define QUESTION "Do you want another transaction";

main()
{
    //定义这个变量,用来表示用户按了y还是n
    //并作为程序返回值
    int response;

    //保存终端模式
    //这个函数是我们自己定义的
    tty_mode(0);

    //关闭缓存
    //这个函数也是我们自己定义的
    set_crmode();

    //get_response用来提出问题,获得用户输入
    //也就是处理用户间的输入
    response = get_response(QUESTION);

    //恢复终端模式
    tty_mode(1);

    return response;
}

int get_response(char * question)
{
    int input;
    //注意,将问题这个字符串存在宏中,
    //而非存在子函数里,这样
    //子函数是不是就通用多了?
    //放在别的程序里直接就能用!
    printf("%s (y/n)?";,question);
    while(1){
        switch( input = getchar() ){
        case'y':
            case'Y': return 0;
        case'n':
        case'N':
        case EOF: return 1;
        default:
            //其它输入时,现实提示信息
            //接着在while里面转
            printf("ncannot understand %c, ",input);
            printf("please type y or nn");
        }
    }
}

//用来关闭终端缓冲,包含了好几个步骤
//简单说,就是:读、改、更
set_crmode()
{
    struct termios ttystate;
    tcgetattr(0,&ttystate);
    //关闭缓冲
    //&= 是让ICANON和他的相反位与肯定是0
    ttystate.c_lflag &= ~ICANON;
    //一次读入一个字符,立即反应
    ttystate.c_cc[VMIN] = 1;
    //更新设置
    tcsetattr(0,TCSANOW,&ttystate);
}

//保护现场,通过how这个标志实现两种操作
//输入为0时,备份,放到original_mode里
//输入为1时,将老的配置(又缓存的)更新
int tty_mode(int how)
{
    //静态变量,一直存储着termios,直到程序退出
    static struct termios original_mode;
    if(how==0)
        tcgetattr(0,&original_mode);
    else
        return tcsetattr(0,TCSANOW,&original_mode);
}