• 对终端的读写进行设置(2)


    2.与终端进行对话



    Linux是多用户系统,终端会有很多个。但是/dev/tty始终指向当前终端


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



        1.控制终端的主要功能如下:



        (1)行编辑:是否允许使用退格键编辑。
        (2)缓存:是立即读取字符,还是等待一段可配置的延迟之后再读取它们
        (3)回显:允许控制字符的回显:例如读取密码
        (4)换行/回车:CR/LF:定义如何再输入输出时映射回车/换行符。比如打印\n字符时应该如何处理。
        (5)线速:这一个功能很少用在PC控制台,但是对调制解调器或者通过串行线连接的终端就很重要



        2.termios结构体



            这个结构体可以影响终端的几种模式
            (1).输入模式
            (2).输出模式
            (3).控制模式
            (4).本地模式
            (5).特殊控制模式
        struct termios{
            tcflag_t c_iflag;
            tcflag_t c_oflag;
            tcflag_t c_cflag;
            tcflag_t c_lflag
            cc_t c_cc[NCCS]
        };
        这个结构体的成员控制这上面的几种模式,一一对应


        3.功能


     (1),获取终端的属性


        int tcgetattr(int fd, struct termios* termios_p)
        这个函数会把对应的终端属性写入到termios_p指向的结构体中
        


     

        (2).配置终端的属性



        int tcsetattr(int fd, int action, const struct termios* termios_p);
        action 控制修改方式,有三种取值
        a.TCSANOW 立刻修改
        b.TCSADRAIN 等当前输出完成后再对值进行修改
        c.TCSAFLUSH 等当前输出完成后再对值进行修改,但是丢弃还没有从read调用返回的当前可用的任何输入。
        
        五种控制模式的取值可以查看手册,修改的时候选择对应的属性。下面简单说一下五种模式的功能或者呈现的效果
        a)输入模式:修改输入的各种模式,比如输入错误,产生一个中断信号,或者忽略某些符号。一般不需要修改,默认的最好
        b)输出模式:管理输出的模式,比如不输出某些字符,一般不需要修改。
        c).控制模式:控制硬件的终端。比如修改调制解调器
        d).本地模式:控制终端的各种特性,比如控制回显,接受各种动作的组合,启用标准输入处理,启用信号等
        e).特殊控制字符:主要是指输入的字符组合,比如Ctrl+C.它的控制有两种模式,一个是标准模式,另一个是非标准模式,它会根据本地模式,选择不同的模式。
        主要书向系统发送信号。
        



        (3)终端的速度。



        由于termios结构体没有关于速度的成员,因此专门设计了一组函数来控制终端的速度
        speed_t cfgetispeed(const struct  termios*)
        speed_t cfgetospeed(const struct termios*)
        int cfsetispeed(struct termios*, speed_t speed)
        int cfsetospeed(struct termios*, speed_t speed)
        速度都是有限制的,需要查看相关文档来设计
        



        (3)其他函数。


    这些函数都是直接对文件描述符进行操作,而不是用termios结构体
        int tcdrain(int fd) 让调用程序一直等待,直到所有排队的输出都已经发送完毕
        int tcflow(int fd, int flowtype)用于暂停或者重新输入
        int tcflush(int fd)清空输入,输出

    下面这个程序再输入密码的时候隐藏了显示,我们看不见输入了什么,输入结束后,我们重新恢复了回显,并输出了输入的密码在屏幕上

    1. #include
    2. #include
    3. #include
    4. #define PASSEDLEN 8//设置密码的固定长度
    5. int main()
    6. {
    7. struct termios initialrsettings, newrsettings;
    8. char password[PASSEDLEN + 1];
    9. if( tcgetattr(fileno(stdin),&initialrsettings) == -1)
    10. {
    11. fprintf(stderr,"获取失败\n");
    12. return 0;
    13. }
    14. newrsettings = initialrsettings;
    15. //设置终端回显,用到第五个元素,输入密码时不显示输入
    16. newrsettings.c_lflag &= ~ECHO;
    17. if(tcsetattr(fileno(stdin),TCSAFLUSH,&newrsettings) == -1)
    18. {
    19. fprintf(stderr,"设置错误\n");
    20. return 0;
    21. }
    22. printf("请输入密码>>");
    23. fgets(password,PASSEDLEN,stdin);
    24. if(tcsetattr(fileno(stdin),TCSAFLUSH,&initialrsettings) == -1)
    25. {
    26. fprintf(stderr,"设置错误\n");
    27. return 0;
    28. }
    29. printf("%s\n",password);
    30. return 0;
    31. }

    我们对上篇文章的程序修改,从终端入手解决读取缓存的问题

    下面这个程序,我们设置每次只要读取到输入字符,就立刻返回程序 ,而不是等待回车换行符号才读取缓存

    1. #include
    2. #include
    3. #include
    4. #include
    5. char *meun[]={"a = add new record","b - delete record","q - quit",NULL};
    6. int getchoice(char *greet, char* choices[], FILE* in, FILE* out);
    7. int main()
    8. {
    9. int choice = 0;
    10. FILE* input,*output;
    11. struct termios initialSettings, newSettings;
    12. if(!isatty(fileno(stdout)))//判断输出是否被重定向
    13. fprintf(stderr,"这不是一个终端\n");
    14. else
    15. fprintf(stderr,"输出重定向到了终端\n");
    16. input = fopen("/dev/tty","r");//读取终端
    17. output = fopen("/dev/tty","w");//写入终端
    18. if(!input || !output)
    19. {
    20. perror("打开tty文件失败\n");
    21. return 0;
    22. }
    23. //先读取当前的终端状态
    24. if(tcgetattr(fileno(input),&initialSettings) < 0)
    25. {
    26. fprintf(stderr,"获取失败\n");
    27. return 0;
    28. }
    29. newSettings = initialSettings;
    30. newSettings.c_lflag &= ~ICANON;//启用标准输入,只有启用了标准输入才能对特殊字符处理,比如换行符
    31. newSettings.c_lflag &= ~ECHO;//关闭回显
    32. newSettings.c_cc[VMIN] = 1;
    33. newSettings.c_cc[VTIME] = 0;
    34. newSettings.c_lflag &= ~ISIG;//屏蔽信号,即使是使用ctrl+c也不会终止程序
    35. //设置新的终端状态
    36. if(tcsetattr(fileno(input),TCSANOW,&newSettings) < 0)
    37. {
    38. fprintf(stderr,"修改状态失败\n");
    39. return 0;
    40. }
    41. //进行本地设置
    42. do{
    43. choice = getchoice("请输入一个选择",meun,input,output);
    44. printf("您的选择是:%c\n",choice);
    45. }while(choice != 'q');
    46. //恢复为原来的默认状态
    47. if(tcsetattr(fileno(input),TCSANOW,&initialSettings) < 0)
    48. {
    49. fprintf(stderr,"修改状态失败\n");
    50. return 0;
    51. }
    52. return 0;
    53. }
    54. int getchoice(char* greet, char* choice[], FILE* input, FILE* output)
    55. {
    56. int chosen = 0;
    57. int selected;
    58. char** option;
    59. do
    60. {
    61. fprintf(output,"choice:%s\n",greet);
    62. option = choice;
    63. while(*option)
    64. {
    65. fprintf(output,"%s\n",*option);
    66. option++;
    67. }
    68. do{
    69. selected = fgetc(input);
    70. }while(selected == '\n' || selected == '\r');
    71. option = choice;
    72. while(*option)
    73. {
    74. if(selected == *option[0])
    75. {
    76. chosen = 1;
    77. break;
    78. }
    79. option++;
    80. }
    81. if(!chosen)
    82. fprintf(stdout,"选择错误,请重新选择");
    83. }
    84. while(!chosen);
    85. return selected;
    86. }

    这个程序还屏蔽了某些信息的输入,这与信号函数不一样,这个是从输入进行屏蔽的,系统根本不会接受到我们输入信号 ,比如CTRL+C,输入后被认为是错误输入。

  • 相关阅读:
    2022世界杯La‘eeb肖像,python海龟实现啦
    ValueError: check_hostname requires server_hostname
    MySQL之MHA高可用配置及故障切换
    打造魔法般的节日装扮---智能氛围灯新方案,创意更好玩
    ESP8266-Arduino编程实例-DHT11传感器数据储存到SD卡
    安卓学习:广播
    数据结构复盘——第一章:绪论
    Leetcode原题电话号码的字母组合的两种解法【BFS-DFS】
    QDataStream
    基因组 DNA 分离丨Worthington核糖核酸酶A
  • 原文地址:https://blog.csdn.net/weixin_42581560/article/details/127936538