先来看一个简单的练习程序:
1 #include <stdio.h> 2 3 int main() 4 { 5 int c , n = 0 ; 6 while( (c = getchar() ) != 'Q' ) 7 printf("char %3d is %ccode %d\n" , n++ , c , c ); 8 return 0 ; 9 }
以上是程序编译后运行的效果啦,可以看到在输入hello之后敲击回车后才运行了该程序,也就是说,在终端中输入的字符实际上是被缓冲的。
另外,回车键本身被识别为换行了,也由该程序识别了,这也与终端的设置有关。
tty驱动程序包含很多对数据的操作:
- 输入:驱动程序如何处理来自终端的字符
- 输出:驱动程序如何处理流向终端的字符
- 控制:字符如何表示——位的个数,位的奇偶性,停止位等等
- 本地:驱动程序如何处理来自驱动程序内部的字符
编写终端驱动程序:关于系统调用函数:
改变终端驱动程序的设置就像改变磁盘文件连接的设置一样:
- 从驱动程序获得属性
- 修改所要修改的属性
- 将修改过的属性送回驱动程序
通常通过tcgetattr和tcsetattr提供对终端驱动程序的访问。这两个函数在termios结构体中交换设置。
1 /* stty.c 2 * show the settings of current tty 3 */ 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <termios.h> 7 8 struct flaginfo 9 { 10 int fl_value ; 11 char * fl_name ; 12 }; 13 14 struct flaginfo input_flags[] = 15 { 16 IGNBRK , "Ignore break condition" , 17 BRKINT , "Signal interrupt on break" , 18 IGNPAR , "Ignore chars with parity errors", 19 PARMRK , "Mark parity errors", 20 INPCK , "Enable input parity check", 21 ISTRIP , "Strip character", 22 INLCR , "Map NL to CR on input", 23 IGNCR , "Ignore CR", 24 ICRNL , "Map CR to NL on input", 25 IXON , "Enable start/stop control", 26 IXOFF , "Disable start/stop control", 27 0 , NULL 28 }; 29 30 struct flaginfo local_flags[] = 31 { 32 ISIG , "Enable signals", 33 ICANON , "Canonical input(erase and kill)", 34 ECHO , "Enable echo" , 35 ECHOE , "Echo REASE as BS-SPACE-BS" , 36 ECHOK , "Echo KILL by starting new line", 37 0 , NULL 38 }; 39 40 void show_baud(int thespeed); 41 void show_some_flags(struct termios * ttyp); 42 void show_flagset(int thevalue , struct flaginfo * thebitnames); 43 44 int main() 45 { 46 struct termios ttyinfo ; 47 // 从文件中获取当前驱动程序的设置 48 if(tcgetattr(0 , & ttyinfo ) == -1 ) 49 { 50 perror("cannot get para about stdin"); 51 exit(1); 52 } 53 54 show_baud( cfgetospeed( &ttyinfo ) ); 55 56 printf("The erase character is ascii %d , Ctrl-%c\n", 57 ttyinfo.c_cc[VERASE] , ttyinfo.c_cc[VERASE] - 1 + 'A') ; 58 printf("The line kill character is ascii %d , Ctrl-%c\n", 59 ttyinfo.c_cc[VKILL] , ttyinfo.c_cc[VKILL] - 1 + 'A') ; 60 61 show_some_flags(&ttyinfo); 62 return 0 ; 63 } 64 65 void show_baud(int thespeed) 66 { 67 printf("The baud rate is :"); 68 switch(thespeed) 69 { 70 case B300 : printf("300\n"); break ; 71 case B600 : printf("600\n"); break ; 72 case B1200 : printf("1200\n"); break ; 73 case B1800 : printf("1800\n"); break ; 74 case B2400 : printf("2400\n"); break ; 75 case B4800 : printf("4800\n"); break ; 76 case B9600 : printf("9600\n"); break ; 77 default : printf("Fast!\n");break ; 78 } 79 } 80 81 void show_some_flags(struct termios * ttyp) 82 { 83 show_flagset( ttyp -> c_iflag , input_flags ) ; 84 show_flagset( ttyp -> c_lflag , local_flags ) ; 85 } 86 87 void show_flagset(int thevalue , struct flaginfo * thebitnames) 88 { 89 int i ; 90 for(i = 0 ; thebitnames[i].fl_name ; i ++ ) 91 { 92 printf("%s is " , thebitnames[i].fl_name); 93 if(thevalue & thebitnames[i].fl_value) 94 { 95 printf("ON\n"); 96 } 97 else 98 { 99 printf("OFF\n"); 100 } 101 } 102 103 }
将结构体以及结构体数组的定义提到前面,编译就不会报错了。话说书中例程使用的应该是较老版本的编译器,所以用现在的编译器总是出警告和错误啥的,按照现C++的定义顺序来写就不会有大问题啦。