终端连接控制(stty的编写)
一、背景
文件与目录在之前已经学习过了。文件中包含着数据,这些数据可以被读出、写入,也可以用以操作。但文件不仅仅是计算机唯一的数据来源,计算机的数据还可以来自于许多的外部设备,比如扫描仪、照相机、鼠标等输入设备,还有扬声器、打印机等输出设备。总之数据来源多种多样,并不仅局限于文件。那么这些外部设备的连接是怎么样的呢,其与进程协同工作时的方式(属性)如何来修改呢。这就是本文所要探究的。
二、外部设备与磁盘文件的联系
文件我们都已经很熟悉了,文件通过目录中的文件名来定位,文件名有一个i-节点与之对应。而i-节点中有文件的属性且包含着指针列表(此指针列表指向文件的数据内容)。而实际上,外部设备在unix系统中也是以文件的形式存储的。我们可以从文件中读数据也可以写数据。同样,我们也可以向设备文件写数据或从中读出数据。
外部设备的文件一般存储于/dev中。
这里显示的是我的一些设备文件。但是并不是说所有的设备文件必须要存储于/dev下,并不是这样的,只是一种约定成俗的习惯,当然也可以在别的目录下创建设备文件。
三、终端文件
要想看自己的终端文件名,只需输入tty即可。我的(root)终端文件是这样的。
而在另一个终端的用户的终端文件是这样的。
如此,如我前面所述,如果我可以使用root用户往/dev/pts/1文件中写入数据,那么应该就可以在用户pf的屏幕上显示出我所写入的数据。这样,我写了一段代码用于此功能,实际上这个即是命令write的功能。
如下所示:
这就很清楚的演示了设备文件(终端文件)的操作方法与磁盘文件十分类似,许多的系统调用也都是通用的。
四、外部设备与磁盘文件的不同
第二节讲了它们是十分相似的,都是以文件形式来进行操作。并且系统调用往往也是通用的。但它们却不可能是完全相同的。不同之处主要体现在连接属性上。对于文件来说,它们与进程进行数据的传输是需要缓冲的,缓冲用于文件数据的传输上好处很多,最突出的是缓冲可以容错,而且缓冲是以块为单位传输的,比起字节为单位传输,效率要高很多。但这是文件的连接属性之一,可以开启也可以关闭。但对于终端来说(以终端为例),当然是不需要缓冲,数据需要尽快的从进程传输到终端上来。并且终端也有其许多的连接属性是文件连接属性所没有的,比如回显、奇偶位、暂停位。
五、终端连接的属性
文件连接的属性这里就不啰嗦了,我主要想讨论一下终端连接的属性。
命令stty用于显示或修改终端的连接属性。
显示全部的终端连接属性用-a参数。如下所示:
这里有三种类型的属性,一种是开关型,一种是数字型、一种是字符控制型。类型是这么分的,在修改对应类型的属性时也是不同的。具体如何修改?对于开关型的属性,只需输入stty (-)属性名,括号中的减号可加可不加,若不加,则是打开此属性开关,若加,则是关闭此属性开关。对于数字型与字符控制型,只需输入stty 属性名 修改值,当然,需要修改什么属性就输入什么属性名,而修改值根据自己的需求来定。
六、stty的编程
stty的编程并不复杂,主要会使用两个系统调用:tcgetattr、tcsetattr。前一个用来将设备文件的信息传出来,后一个用来将修改过后的信息传回去。并使用掩码来进行测试、置位与清除位。
这里我先编写了一个简单的echo的开关文件。结果如下所示:
代码如下:
#include<stdio.h>
#include<termios.h>
#include<stdlib.h>
int main()
{
struct termios info;
if(tcgetattr(0,&info)==-1){
perror("tcgetattr");
exit(1);
}
else{
if(info.c_lflag&ECHO)
info.c_lflag&=~ECHO;
else
info.c_lflag|=ECHO;
if(tcsetattr(0,TCSANOW,&info)==-1)
perror("tcsetattr");
}
}
自己编写的stty结果如下:
我这里还是拿echo来做测试的,因为结果很明显。
实现stty的代码如下:
#include<stdio.h>
#include<termios.h>
#include<stdlib.h>
#include<string.h>
struct termios execute_1(struct termios buff,char *av);
struct termios execute_2(struct termios buff,char *av_1,char *av_2);
int main(int argc,char *argv[])
{
struct termios info;
struct termios buf;
if(argc!=2 && argc!=3){
printf("Usage error\n");
exit(1);
}
if(tcgetattr(0,&info)==-1){
perror("tcgetattr");
exit(1);
}
if(argc==2){
buf=execute_1(info,argv[1]);
tcsetattr(0,TCSANOW,&buf);
}
if(argc==3){
buf=execute_2(info,argv[1],argv[2]);
tcsetattr(0,TCSANOW,&buf);
}
}
struct termios execute_1(struct termios buff,char *av)
{
if(strcmp(av,"ignbrk")==0)
buff.c_iflag|=IGNBRK;
if(strcmp(av,"-ignbrk")==0)
buff.c_iflag&=~IGNBRK;
if(strcmp(av,"brkint")==0)
buff.c_iflag|=BRKINT;
if(strcmp(av,"-brkint")==0)
buff.c_iflag&=~BRKINT;
if(strcmp(av,"ignpar")==0)
buff.c_iflag|=IGNPAR;
if(strcmp(av,"-ignpar")==0)
buff.c_iflag&=~IGNPAR;
if(strcmp(av,"parmrk")==0)
buff.c_iflag|=PARMRK;
if(strcmp(av,"-parmrk")==0)
buff.c_iflag&=~PARMRK;
if(strcmp(av,"inpck")==0)
buff.c_iflag|=INPCK;
if(strcmp(av,"-inpck")==0)
buff.c_iflag&=~INPCK;
if(strcmp(av,"istrip")==0)
buff.c_iflag|=ISTRIP;
if(strcmp(av,"-istrip")==0)
buff.c_iflag&=~ISTRIP;
if(strcmp(av,"inlcr")==0)
buff.c_iflag|=INLCR;
if(strcmp(av,"-inlcr")==0)
buff.c_iflag&=~INLCR;
if(strcmp(av,"igncr")==0)
buff.c_iflag|=IGNCR;
if(strcmp(av,"-igncr")==0)
buff.c_iflag&=~IGNCR;
if(strcmp(av,"icrnl")==0)
buff.c_iflag|=ICRNL;
if(strcmp(av,"-icrnl")==0)
buff.c_iflag&=~ICRNL;
if(strcmp(av,"iuclc")==0)
buff.c_iflag|=IUCLC;
if(strcmp(av,"-iuclc")==0)
buff.c_iflag&=~IUCLC;
if(strcmp(av,"ixon")==0)
buff.c_iflag|=IXON;
if(strcmp(av,"-ixon")==0)
buff.c_iflag&=~IXON;
if(strcmp(av,"ixany")==0)
buff.c_iflag|=IXANY;
if(strcmp(av,"-ixany")==0)
buff.c_iflag&=~IXANY;
if(strcmp(av,"ixoff")==0)
buff.c_iflag|=IXOFF;
if(strcmp(av,"-ixoff")==0)
buff.c_iflag&=~IXOFF;
if(strcmp(av,"imaxbel")==0)
buff.c_iflag|=IMAXBEL;
if(strcmp(av,"-imaxbel")==0)
buff.c_iflag&=~IMAXBEL;
if(strcmp(av,"opost")==0)
buff.c_oflag|=OPOST;
if(strcmp(av,"-opost")==0)
buff.c_oflag&=~OPOST;
if(strcmp(av,"onlcr")==0)
buff.c_oflag|=ONLCR;
if(strcmp(av,"-onlcr")==0)
buff.c_oflag&=~ONLCR;
if(strcmp(av,"olcuc")==0)
buff.c_oflag|=OLCUC;
if(strcmp(av,"-olcuc")==0)
buff.c_oflag&=~OLCUC;
if(strcmp(av,"ocrnl")==0)
buff.c_oflag|=OCRNL;
if(strcmp(av,"-ocrnl")==0)
buff.c_oflag&=~OCRNL;
if(strcmp(av,"onlret")==0)
buff.c_oflag|=ONLRET;
if(strcmp(av,"-onlret")==0)
buff.c_oflag&=~ONLRET;
if(strcmp(av,"ofill")==0)
buff.c_oflag|=OFILL;
if(strcmp(av,"-ofill")==0)
buff.c_oflag&=~OFILL;
if(strcmp(av,"ofdel")==0)
buff.c_oflag|=OFDEL;
if(strcmp(av,"-ofdel")==0)
buff.c_oflag&=~OFDEL;
if(strcmp(av,"nldly")==0)
buff.c_oflag|=NLDLY;
if(strcmp(av,"-nldly")==0)
buff.c_oflag&=~NLDLY;
if(strcmp(av,"crdly")==0)
buff.c_oflag|=CRDLY;
if(strcmp(av,"-crdly")==0)
buff.c_oflag&=~CRDLY;
if(strcmp(av,"tabdly")==0)
buff.c_oflag|=TABDLY;
if(strcmp(av,"-tabdly")==0)
buff.c_oflag&=~TABDLY;
if(strcmp(av,"bsdly")==0)
buff.c_oflag|=BSDLY;
if(strcmp(av,"-bsdly")==0)
buff.c_oflag&=~BSDLY;
if(strcmp(av,"ffdly")==0)
buff.c_oflag|=FFDLY;
if(strcmp(av,"-ffdly")==0)
buff.c_oflag&=~FFDLY;
if(strcmp(av,"vtdly")==0)
buff.c_oflag|=VTDLY;
if(strcmp(av,"-vtdly")==0)
buff.c_oflag&=~VTDLY;
if(strcmp(av,"csize")==0)
buff.c_cflag|=CSIZE;
if(strcmp(av,"-csize")==0)
buff.c_cflag&=~CSIZE;
if(strcmp(av,"cstopb")==0)
buff.c_cflag|=CSTOPB;
if(strcmp(av,"-cstopb")==0)
buff.c_cflag&=~CSTOPB;
if(strcmp(av,"cread")==0)
buff.c_cflag|=CREAD;
if(strcmp(av,"-cread")==0)
buff.c_cflag&=~CREAD;
if(strcmp(av,"parenb")==0)
buff.c_cflag|=PARENB;
if(strcmp(av,"-parenb")==0)
buff.c_cflag&=~PARENB;
if(strcmp(av,"parodd")==0)
buff.c_cflag|=PARODD;
if(strcmp(av,"-parodd")==0)
buff.c_cflag&=~PARODD;
if(strcmp(av,"hupcl")==0)
buff.c_cflag|=HUPCL;
if(strcmp(av,"-hupcl")==0)
buff.c_cflag&=~HUPCL;
if(strcmp(av,"clocal")==0)
buff.c_cflag|=CLOCAL;
if(strcmp(av,"-clocal")==0)
buff.c_cflag&=~CLOCAL;
if(strcmp(av,"crtscts")==0)
buff.c_cflag|=CRTSCTS;
if(strcmp(av,"-crtscts")==0)
buff.c_cflag&=~CRTSCTS;
if(strcmp(av,"isig")==0)
buff.c_lflag|=ISIG;
if(strcmp(av,"-isig")==0)
buff.c_lflag&=~ISIG;
if(strcmp(av,"icanon")==0)
buff.c_lflag|=ICANON;
if(strcmp(av,"-icanon")==0)
buff.c_lflag&=~ICANON;
if(strcmp(av,"echoe")==0)
buff.c_lflag|=ECHOE;
if(strcmp(av,"-echoe")==0)
buff.c_lflag&=~ECHOE;
if(strcmp(av,"echok")==0)
buff.c_lflag|=ECHOK;
if(strcmp(av,"-echok")==0)
buff.c_lflag&=~ECHOK;
if(strcmp(av,"echoke")==0)
buff.c_lflag|=ECHOKE;
if(strcmp(av,"-echoke")==0)
buff.c_lflag&=~ECHOKE;
if(strcmp(av,"echoctl")==0)
buff.c_lflag|=ECHOCTL;
if(strcmp(av,"-echoctl")==0)
buff.c_lflag&=~ECHOCTL;
if(strcmp(av,"echo")==0)
buff.c_lflag|=ECHO;
if(strcmp(av,"-echo")==0)
buff.c_lflag&=~ECHO;
if(strcmp(av,"echonl")==0)
buff.c_lflag|=ECHONL;
if(strcmp(av,"-echonl")==0)
buff.c_lflag&=~ECHONL;
if(strcmp(av,"iexten")==0)
buff.c_lflag|=IEXTEN;
if(strcmp(av,"-iexten")==0)
buff.c_lflag&=~IEXTEN;
if(strcmp(av,"noflsh")==0)
buff.c_lflag|=NOFLSH;
if(strcmp(av,"-noflsh")==0)
buff.c_lflag&=~NOFLSH;
if(strcmp(av,"tostop")==0)
buff.c_lflag|=TOSTOP;
if(strcmp(av,"-tostop")==0)
buff.c_lflag&=~TOSTOP;
if(strcmp(av,"pendin")==0)
buff.c_lflag|=PENDIN;
if(strcmp(av,"-pendin")==0)
buff.c_lflag&=~PENDIN;
if(strcmp(av,"flusho")==0)
buff.c_lflag|=FLUSHO;
if(strcmp(av,"-flusho")==0)
buff.c_lflag&=~FLUSHO;
return buff;
}
struct termios execute_2(struct termios buff,char *av_1,char *av_2)
{
if(strcmp(av_1,"intr")==0)
buff.c_cc[VINTR]=*av_2;
if(strcmp(av_1,"quit")==0)
buff.c_cc[VQUIT]=*av_2;
if(strcmp(av_1,"erase")==0)
buff.c_cc[VERASE]=*av_2;
if(strcmp(av_1,"kill")==0)
buff.c_cc[VKILL]=*av_2;
if(strcmp(av_1,"eof")==0)
buff.c_cc[VEOF]=*av_2;
if(strcmp(av_1,"min")==0)
buff.c_cc[VMIN]=*av_2;
if(strcmp(av_1,"eol")==0)
buff.c_cc[VEOL]=*av_2;
return buff;
}
七、总结
这是我第一次从系统的角度来观察设备文件,对于设备文件的学习当然是对比更为熟悉的磁盘文件。从异、同两个角度来观察,相同之处在于两者都是以文件的形式在计算机进行存储,表面来看十分相似,且两者的系统调用也基本通用。不同之处在于连接属性决然不一样。这是理所当然的事情,具体问题具体分析,各个不同的设备文件的连接属性也都不同,对终端来说,属性有回显、奇偶位、暂停位等等,对CD刻录机来说,属性有刻录速度、颜色深度等等。我这里只大概的分析了一下终端的连接属性。往后会更多的分析其它的设备文件连接属性。