虚终端,也叫虚屏,它的英文名字为virtual terminal,缩写为VT或vt。在Unix系统用户中,虚终端有着广泛的应用。它解决了主控台单一交互界面的限制,使用户可在保护当前界面的情况下启用另一界面去做另一工作。我们可利用Unix系统提供的系统调用来编制虚终端程序,并可把它加入到我们的应用程序中去。本人就在此虚终端程序的基础上编写了图形软件,它能够保存和恢复先前的界面。着实方便,现把虚终端的实现方法介绍给大家,以共勉。 由于本人用的系统为AT&T USL Unix SVR 4.2,在此就以此系统为基础给予介绍。 在USLUnix系统中,虚终端使用的设备文件是/dev/vt??,其中?代表数字0至9,如/dev/vt01,/dev/vt02等等。在使用的设备名称这一点上,与Sco Unix不同。我们可利用ioct1函数操纵虚终端设备文件,实现与Unix系统内核的交互,得到我们所需要的服务。使用ioct1的具体形式如下: int ioct1(int filedes,int request,…/*参数*/) 其中filedes为打开的虚终端设备文件描述符,request为请求的服务,随后的参数可为各种数据类型,视具体情况而定。与虚终端有关的用于ioct1调用中request值主要有以下几个: VT_OPENQRY 查找一个可利用的虚终端。用于ioct1系统调用的参数是一个指向长整数的指针。该长整数的值将被置为第一个可利用的无进程在其上打开的虚终端的序号。如果没有可利用的虚终端,则该长整数的值被置为-1。 VT_GETMODE 获取活动虚终端当前所在的状态。用于ioct1系统调用的参数是一个指向vt_mode结构的指针。结构vt-mode定义如下: struct vt-mode { char mode;/* VT mode */ char waitv;/* if set,hang on writes when not active */ short relsig;/*signal to use for release request */ short acqsig; /*signal to use for display acquired */ short frsig; /*not use set to 0 */ } 在结构vt_mode中,mode字段的值可为VT_AUTO和VT_PROCESS,它们的宏定义如下: #define VT_AUTO 0x00 /*自动切换*/ #define VT_PROCESS 0x01 /*进程控制切换*/ VT_SETMODE 设置虚终端模式(自动或进程控制)。用于ioctl系统调用的参数是一个″vt_mode″结构的指针(结构定义见VT_GETMODE)。 VT_GETSTATE 获取全部虚终端的状态信息。用于ioctl的系统调用的参数是一个指向vt_stat结构的指针,该结构定义如下: struct vt_stat { ushort v_active;/*活动的vt*/ ushort v_signal;/*要发送的信号(用于VT_SENDSIG)*/ ushort v_state; /*vt位掩码(VT_SENDSIG和VT_GETS TATE)*/ } 该调用在v_active字段返回活动虚终端号,在v_state字段中可获得全部虚终端状态的位掩码(v_state的位x是虚终端x的状态,该位为1说明对应的虚终端是打开状态,否则为可利用状态)。 VT_SENDSIG 指定要发送给虚终端(在v_state中)的位屏蔽的信号(在v_signa1中)。用于ioct1的系统调用的参数是一个指向vt_stat结构的指针(结构定义见前面VT_GETSTATE)。 VT_RELDISP 用于告诉虚终端管理器进程是否已经释放了显示。一个零值指示拒绝释放显示。 VT_ACTIVATE 使在参数中指定的虚终端号为活动虚终端。″VT″管理器将产生一个切换,如同在键盘上按组合热键一样。如果指定的虚终端不处于打开状态或者不存在,调用将失败,并置errno的值为ENXI0。 VT_WAITACTIVE 等待虚终端被激活,不需要参数。 有了上面的介绍,现在我们就可以实现虚终端了: 1?查找是否有可利用的虚终端,如没有,则结束。 2?打开可利用虚终端设备文件,以便进行。 3?利用ioct1的TCSETSW功能设置虚终端参数,其作用同Unix的stty命令一样。 4?利用ioct1的VT_ACTIVATE功能激活虚终端,并利用VT_WAITACTIVE功能等待其可用。 5?用putenv函数设置环境变量。 至此,一个虚终端程序就实现了(当然,是利用Unix系统提供的功能,但它与Unix提供的newvt命令的实现并无区别)。在这个虚终端里,我们可方便地实现我们自己想要的功能,而不必担心会破坏原来的环境。 下面是本人编制的实现虚终端的程序,仅供参考。
程序清单如下: #include ″stdio.h″ #include ″signal.h″ #include ″time.h″ #include ″sys/types.h″ #include ″sys/fcnt1.h″ #include ″sys/vt.h″ #include ″sys/termio.h″ int vtlog (char *errmsg,char *file,int line) { FILE *fp; char logfile[]=″/tmp/vtlog″; char buf[100]; time_t ii; memset(buf,0,100); ii=time((time_t*)0); strcpy(buf,ctime(&ii)+4); buf[15]=0; if((fp=fopen(logfile,″a″))==NULL) /*打开日志文件*/ return -1; if((file==NULL)&&(line==0)) fprintf(fp,″%s %s\n″,buf,errmsg); else fprintf(fp,″%s File %s line %d:%s\n″,buf,file,line, errmsg); fclose(fp) return 0; } void (*istat)(int),(*qstat)(int);/*用于保存原来的信号地址*/ /***********************************/ int Init_VT() { int fd,fd0,fd1 fd2; long vt_avail; pid_t childpid; char termname[20]; char buf[128〗; struct termios ParTerm; struct termio termiopm; char prompt[20]; time_t tbuf; if((fd=open)″/dev/vt00″,O_RDWR,010010766 70))==-1){ vtlog(″不能打开文件/dev/vt00!″,_FILE_,_LINE_); exit(-1); } if(ioct1(fd,VT_OPENQRY,&vt_avail)==-1){ vtlog(″无可利用的虚终端!″,_FILE_,_LINE_); exit(-1); } close(fd); close(2); close(1); close(0); if((childpid=fork())< 0){ /*fork子进程*/ vtlog("fork错误!\n″,_FILE_,_LINE_); exit(-1); } else if(childpid>0) exit(0); /*父进程退出*/ if(setpgrp()< 0) { vtlog(″设置进程组标识错误!″,_FILE_,_LINE_); exit(-1); } sprintf(termname,″/dev/vt%02d″,vt_avail); if((fd0=open(termname,O_RDWR,01014020330))==-1){ vtlog(″不能打开新的虚终端!″,_FILE_,_LINE_); ;exit(-1); } fd1=dup(fd0); fd2=dup(fd0); if(ioct1(fd0,TCGETS,&ParTerm)==-1){ vtlog(″不能取term 参数!″,_FILE_,_LINE_); exit(-1); } ParTerm.c_1f1ag |=ICANON; ParTerm.c_1f1ag |=ECHO; ParTerm.c_1f1ag |=ECHOE; ParTerm.c_cc[2]=CTRL('h'); ParTerm.c_cc[3]=CTRL('x'); ParTerm.c_cc[4]=4; ParTerm.c_cc[5]=5; ParTerm.c_iflag &=~ISTRIP; ParTerm.c_cflag 1=CS8; ParTerm.c_cflag &=~PARENB; if(ioct1(fd0,TCSETSW,&ParTerm)==-1){ vtlog(″不能设置term参数!″,_FILE_,_LINE_); exit(-1); } if(ioct1(fd1,TCGETA,&termiopm)==-1){ vtlog(″不能取 term参数!″,_FILE_,_LINE_); } istat=signal(SIGINT,SIG_DFL); qstat=signal(SIGQUIT,SIG_DFL); if(ioctl(fd0,VT_ACTIVATE,vt_avail)==-1){ vtlog(″激活虚终端失败!″,_FILE_,_LINE_); exit(-1); } if(ioctl(fdo,VT_WAITACTIVE,NULL)==-1){ vtlog(″等待激活虚终端失败!″,_FILE_,_LINE_); exit(-1); } sprintf(prompt,″PS1=VT %1d>″,vt_avail); putenv(prompt); } /*______________主函数,可根据具体情况编制________*/ int main() { Init_VT(); system(″sh″); /*可根据自己的需要灵活设计,如; 用gets函数读取输入,对输入的命令加以分析,然后用exec等函数去执行,即为一个shell程序了*/ ;return 0; } /* 主函数结束 */ □
| | |