《Linux程序设计》学习笔记05——终端

来源:互联网 发布:店老大软件 编辑:程序博客网 时间:2024/04/29 20:44


对终端进行读写

在编写程序时,我们往往需要从终端读入数据。一种情况是需要连续地读入用户键入的选择项,这往往出现在数据库程序中。程序员往往会使用getchar函数来读取数据,继而判断输入的数据是否有效,从而做出反应。其实如此做带有很大的风险,一个实例程序如下

#include <stdio.h>

 

char *menu[] = {

        "a - add new record",

        "d - delete record",

        "q - quit",

        NULL

};

 

int getchoice(char *choices[]){

        int chosen = 0;

        int selected;

        char **option;

 

        do {

                option = choices;

                while(*option){

                        printf("%s/n", *option);

                        option++;

                }

                selected = getchar();

                option = choices;

                while(*option){

                        if(option[0] == selected){

                                chosen = 1;

                                break;

                        }

                        option++;

                }

                if(!chosen){

                        printf("Incorrect choice, select again./n");

                }

        } while(!chosen);

 

        return selected;

}

 

int main()

{

        int choice = 0;

 

        do{

                choice = getchoice(menu);

                printf("You choose %c/n", choice);

        } while('q' != choice);

 

        exit(0);

}

实例程序中,用户需要键入“A/回车/Q/回车”才能做出选择。但这种处理有着很大的风险,读者可以自己测试一下。这也是初学者经常碰到的问题。

默认情况下,只有当用户按下回车键后,程序才能读到终端的输入。这种处理方式是规范模式或标准模式。在这种模式下,所有的输入都给予行进行处理,在一个输入行完成前,终端接口负责管理所有的用户键盘输入,包括退格键,应用程序读不到用户输入的任何字符。

与标准模式相对的另一种模式为非标准模式,这种模式下,应用程序对用户输入字符的处理拥有更大的控制权。

在上述程序中,Linux会暂存用户读入的内容,直到用户按下回车键,然后将用户选择的字符及紧随其后的回车符一起传送到程序。所以,每当你选择一个菜单时,程序就调用getchar函数处理该字符,而当程序在下一次循环再次调用getchar函数时,它会立刻返回一个回车符。一个解决方案是程序在每次读入数据前首先清空回车键之前的所有数据,典型代码如下:

        do{

                selected = getchar();

        } while('/n' != selected);

 

终端驱动程序和通用终端接口

有时,程序需要更加精细的终端控制能力,而不是仅通过简单的文件操作来完成对终端的一些控制。Linux提供了一组编程接口,这使得我们能够控制终端驱动程序的行为,从而允许我们对终端的输入和输出进行更好的控制。

有一组函数调用(GTI)用作控制终端,这组函数调用与用于读写数据的函数是分离的,这就使得读写数据的接口非常简洁,同时又保证用户可以对终端的行为进行更精细的控制。

 

termios结构

通过设置termios结构中的值和使用一组函数调用,我们就可以对终端接口进行控制。

提示:使用termios结构及相关的函数调用,需要包含termios.h头文件;同时需要包含curses函数库。

控制终端的操作模式有以下几种:输入模式、输出模式、控制模式、本地模式和特殊的控制字符。具体操作由tcgetattr函数和tcsetattr函数来完成。其中,本地模式是最常用,也是最重要的一种操作模式。

注意:程序要将终端设置恢复到程序开始运行之前的状态,这一点是非常重要的。首先保存这些值,然后在程序结束时恢复它们,这永远是程序的职责。

输入模式控制输入数据在被传递给程序之前的处理方式。通过设置termios结构中的c_iflag成员的标志对它们进行控制。

输出模式控制输出字符的处理方式,即由程序发送出去的字符在传递到串行口或屏幕之前是如何处理的。通过设置termios结构中c_oflag成员的标志对输出模式进行控制。

控制模式控制终端的硬件特性。通过设置termios结构中的c_cflag成员的标志对控制模式进行配置。控制模式主要用于串行线连接调制解调器的情况。

本地模式控制终端的各种特性。通过设置termios结构中的c_lflag成员的标志对本地模式进行配置。其中最常用的两个标志是ECHOICANON。前者抑制键入字符的回显,后者将终端在两个截然不同的接收字符处理模式之间进行切换。如果设置了ICANON标志,就启用标准输入行处理模式,否则就启动非标准模式。

当用户键入类似Ctrl-C这样的组合键时,终端会采取一些特殊的处理方式。termios结构中的c_cc数组成员将各种特殊的控制字符映射到对应的支持函数。每个字符的位置是由一个宏定义的,但不限制这些字符必须是控制字符。

注意:在两种不同的模式(标准模式和非标准模式)下,c_cc数组的下标值有一部分是重叠的。出于这个原因,一定要注意不要将两种模式各自的下标值混淆。

可以通过stty命令查询及修改终端模式。

通过termios结构我们还可以控制终端的传入和传出的速度(波特率)。

 

终端的输出

编写能够应付连接到UNIX系统上的各种不同类型终端的程序看上去是一件非常让人畏惧的事情。因为这样的程序必须针对各种类型的终端编写相应的代码。termifo软件包的出现解决了这一问题。在绝大多数现代的UNIX系统上,这个软件包和另一个软件包curses集成在一起。

注意:在Linux系统上,在使用termifo软件包时可能需要包含ncurses库;该库实现了curses软件包的所有功能。

termifo的功能标识由属性描述,它们被保存在一组编译好的terminfo文件中,而这些文件可以方便地在/usr/lib/terinfo/usr/share/terinfo目录下找到。例如,VT100终端的定义就放在文件/usr/share/terminfo/v/vt100中。你可以使用infocmp程序输出terminfo编译数据项的可读版本。

 

虚拟控制台

Linux的典型安装中将配置12个虚拟控制台。虚拟控制台通过字符设备文件/dev/ttyN使用,ttyTeletype的缩写,而N代表一个数字,从1开始。

通过whops命令,可以查看目前登录进系统的用户,以及目前在使用的虚拟控制台及其上运行的shell和程序。

Linux系统一般在前六个虚拟控制台上运行一个getty进程,这样用户即可用同一个屏幕、键盘和鼠标在六个不同的虚拟控制台上登录。可以通过组合键Ctrl+Alt+F<N>在这六个不同的虚拟控制台之间进行切换。

如果Linux系统使用的是图形登录界面或者使用startx切入图形界面,X视窗系统将使用第一个未使用的控制台,通常是/dev/tty7

伪终端由字符设备文件/dev/pty使用,其中ptypseudo tty的缩写。它与tty终端的区别在于伪终端没有对于的硬件设备。

运程登录的终端由字符设备文件/dev/pts/N使用。

原创粉丝点击