键盘,咱们做计算机这一行的自然不必多说,天天与它打交道。但熟归熟,清楚键盘背后的原理吗?键盘上都标有各键的名称,表明了各键所代表的意义,但是计算机是如何知道的?组合键是怎样实现的?按下一个代表字符的键,怎么变成平常使用的ASCII码的?
看完本文,相信你就能了解键盘的本质,知晓这些问题答案。

一、相关介绍

键盘编码器

键盘编码器(i8048),是键盘里的芯片,主要用来监控是否有键按下,弹起,然后向键盘控制器报告此键的相关信息。键盘编码器就像是键盘的嘴,让键盘能够说话,表达目前按键状态。Num Lock键和Caps Lock键的LED灯的开关也归它控制。

键盘扫描码

上述所说的信息就是键盘扫描码,一个键有按下就会有弹起,所以每个键会有两个状态,即每个键将会对应两个扫描码,键被按下时的编码叫做通码(makecode),弹起时的编码叫做断码(breakcode)。

大部分键的通码和断码都是 8 位 1 字节,但有些操作控制键如 ctrl、alt,附加键如Insert,小键盘区如 / ,方向键等是 2 字节甚至多个字节。有多个字节的扫描码都是以 0xe0 开头。只有Pause Break一个键是以 0xe1 开头。

断码与通码的关系:断码 = 通码 + 0x80。0x80 二进制表示为 1000 0000,所以对于断码和通码可以这样理解,它们由8位比特组成,最高位第7位表示按键状态,1表示按下,0表示弹起。

注:上述为第一套键盘扫描码的情况,现下使用的键盘基本使用的第二套键盘扫描码,但是为了兼容,最终还是要将第二套扫描码转化为第一套扫描码,这也是键盘控制器工作的一部分。

键盘控制器

键盘控制器(i8042),不在键盘内部,被集成在南桥芯片上。它主要是接收键盘编码器发来的扫描码(第二套),解码(转成第一套)后保存到自己的寄存器中,然后通过中断控制器发送中断请求。

i8042有4个寄存器,如下所示:

其中输入缓冲区和输出缓冲区共用0x60端口,状态控制器和控制寄存器共用一个0x64端口。

共用不会冲突吗?注意读写状态的不同,CPU使用int指令从8042读数据时 0x60 代表输出缓冲区,CPU使用out指令将数据写入8042时 0x60代表输入缓冲区,状态寄存器和控制寄存器同理。

注:输入输出要视对象决定,对键盘控制器来说是输出,那么对CPU来说则是输入,使用 in 指令。

每个寄存器都是8位的,保存扫描码时最多只能保存8位1字节的扫描码,每次键盘中断服务程序也只能处理 1 字节的扫描码。也就是说键盘中断的次数不是你按键、弹起的次数,而是按键、弹起对应的通码、断码(第一套)字节数。由此可以看出平时我们敲键盘时那是发生了无数次的中断呐。

那有的按键信息不是多个字节的扫描码吗? 的确是,但硬件环境如此,不能改变,只能在软件上下功夫,这就是接下来要说的键盘的中断程序,先看看键盘中断的流程,好有个清晰的路线。

二、键盘中断流程

其实上述的相关介绍已经涉及了部分键盘中断流程,在此从头至尾具体说说,先看流程图:

  1. 键盘编码器监控是否有键按下或弹起,若有键按下,向键盘控制器发送此键的通码;若有键弹起,则发送断码(基本发送第二套键盘扫描码)。

  2. 键盘控制器接收来自键盘编码器发来的扫描码,解码转化成第一套扫描码,保存到自己的输出缓冲区中,然后通过中断控制器向CPU发送键盘中断信号。

  3. 后面的流程基本和上文讲的中断流程一样了,在此简述:未关中断的情况下CPU响应,中断控制器再通过数据线发送中断向量号,CPU据向量号定位中断服务程序,期间检查特权级自动压栈,然后运行中断服务程序处理中断。

三、键盘中断服务程序

键盘中断在所有的可屏蔽中断中优先级仅次于时钟中断,也需要尽快的处理。在Linux 0.11里的整个键盘服务程序都是用汇编来写的,汇编语言直接操作底层的指令,没有编译器来增加额外的东西,所以运行起来比高级语言写的程序快,但也增加了编写程序的难度。

linux0.11版本的键盘中断服务程序的框架源码如下图所示:

这个框架程序主要做了以下事情:

保护现场——压栈

上文中写到压栈ss, esp, eflags, cs, eip, error_code (若有特权级变化且中断带有错误码) 来保存现场,那只是CPU自动执行的部分,完全保存原任务的信息还是在中断处理程序中进行的。

如上图所示,键盘中断服务程序里通用寄存器只保存了4个,eax, ebx, ecx, edx,若为了省事不追求效率完全可以无脑操作pushad压栈所有的通用寄存器,但人家是Linux系统嘛,虽然只是0.11版本,但也要追求精确,效率,只压栈中断程序需要用到,可能破坏的寄存器。

读取扫描码

inb $0x60, al 从键盘控制器的输出缓存区0x60端口读取扫描码。若不从输出缓冲区读取数据的话,键盘控制器是不会继续工作的,意思是无论你怎么按键,键盘控制器不会响应键盘操作,不会存下新的扫描码发送中断信号等。当然不读取扫描码后续的键盘中断程序也没法工作没有意义,在此只是说明一下。

判断是否为 0xe0 或 0xe1

如果扫描码是 0xe0 或者 0xe1,那说明这个键的扫描码是有多个字节的,需要先保存下来等待接下来的扫描码组合成完整的扫描码。

寻址、调用相应的键处理程序

拿到完整的扫描码之后就该去寻找相应的键处理程序了,源码中有个key_table,table, 说明它是一张表,或者说一个数组,这里面就按照扫描码大小存放了各个键的实际处理程序地址。

如何找到相应的键处理程序呢?其实跟数组用下表获取元素一样,只是汇编里面有一些听起来高大上的名词:据源码所示采用比例变址寻址的方式,即key_table(, %eax, 4),也就是说相应的键处理程序的地址是key_table + eax * 4。key_table,相当于数组首地址;eax里面存放的扫描码,扫描码可以看成数字索引号,相当于数组下标;地址32位,4字节,所以乘4。

回复现场——出栈

压栈保护现场的逆过程,在此不再赘述,需要注意执行到 iret 时的栈顶应是 eip。

四、键处理程序

键的扫描码有通码和断码,有着不同的处理,主要的键处理程序我分为了以下几类(各点开头出现的名字都是Linux0.11中实际键处理程序的函数名称):

  1. ctrl,alt,caps,shift,num等控制键处理程序,整个键盘中断程序维护了两个8位的变量mode和leds。它们的每一位(没用完)代表着一个键,1表示按下,0表示弹起。mode 代表的键有caps, alt, ctrl, shift。leds代表的键有NumLock, CapsLock, ScrollLock。所以操作控制键的键处理程序就是设置变量的相应位。

  2. do_self,处理普通键的程序,主要的功能就是将扫描码转换成ASCII码,然后放进键盘缓冲区中。键盘中断程序维护了一张扫描码到ASCII码,名为key_map的映射表,do_self依据这张表做转换。

  3. func, 处理功能键如Fxx键的程序

  4. cursor,设置光标位置,它是处理方向键,PgUp,Backsp等键的程序

  5. unctrl, unshift等,将mode和leds复位,如unctrl将mode中的ctrl位置0。

  6. none,除开特殊键的断码对应的键处理程序,什么都不做,直接返回。而特殊键的断码处理程序就是上述的5,复位就行。

由上,我们也能得知平时可能成为习惯但没具体关注的几个问题:

  1. 使用组合键时需要先按下控制键。键盘的中断程序为这些控制键设置了标识(mode/leds)。先按下控制键,程序为控制键设置好按下状态,再处理后到来的键时会检查这些标识,是否有控制键按下,以便做出不同的操作。

  2. 组合键按键时有顺序,但弹起无顺序要求。由上面的键处理程序可知,只有通码的键处理程序在做事,而断码的键处理程序除了特殊键需要复位之外其他键都是直接中断返回的。所以使用键盘控制输入时重要的是按键,而不是键弹起,所以只要按键对了,怎样弹起并不重要。

  3. 一直按着某个键时会一直触发键盘中断,若是普通的字符键,电脑屏幕可能会出现一直打印某个字符的现象。若是一些控制键,则中断程序可能会不停得将这个键设为按下状态。当然,键盘中断程序是否记录上次按键取决于具体实现,大多是不记录的,触发一次键盘中断就处理一个扫描码。

关于键盘控制输入的原理就是这样,这条线应该还是很清楚的。键盘输入是以键盘中断为核心的,如果还不是很清楚可以回头看看键盘中断的流程图。
好啦本文就到这里,如果有什么错误还请批评指正。如果有所帮助,还请多多关注支持。

喜欢本文的朋友还请点赞,关注公众号Rand_cs可获取更多关于系统的精品文章,还有关于计算机的各类电子书,总能找到你需要的,赶快关注吧

键盘中断,键盘驱动,基于Linux0.11的更多相关文章

  1. 0x06_自制操作系统My-OS,IDT,GDT,PIC初始化,实现键盘中断

    把class03改成class04 IDT,GDT,PIC 我来介绍什么是IDT和GDT,PIC,怎么实现键盘中断 GDT全局描述表在16位CPU用不到,到了32位CPU要用. 16位CPU实模式用基 ...

  2. 对Linux0.11 中 进程0 和 进程1分析

    1. 背景 进程的创建过程无疑是最重要的操作系统处理过程之一,很多书和教材上说的最多的还是一些原理的部分,忽略了很多细节.比如,子进程复制父进程所拥有的资源,或者子进程和父进程共享相同的物理页面,拥有 ...

  3. linux0.11改进之四 基于内核栈的进程切换

    这是学习哈工大李治军在mooc课操作系统时做的实验记录.原实验报告在实验楼上.现转移到这里.备以后整理之用. 完整的实验代码见:实验楼代码 一.tss方式的进程切换 Linux0.11中默认使用的是硬 ...

  4. [自制简单操作系统] 2、鼠标及键盘中断处理事件[PIC\GDT\IDT\FIFO]

    1.大致介绍: >_<" 大致执行顺序是:ipl10.nas->asmhead.nas->bootpack.c PS: 这里bootpack.c要调用graphic. ...

  5. 操作系统开发系列—12.g.在内核中设置键盘中断

    8259A虽然已经设置完成,但是我们还没有真正开始使用它呢. 所有的中断都会触发一个函数spurious_irq(),这个函数的定义如下: PUBLIC void spurious_irq(int i ...

  6. 怎样抓获或忽略像control-C这样的键盘中断?

    基本步骤是调用signal():#include <signal.h>singal(SIGINT, SIG_IGN); 就可以忽略中断信号, 或者:extern void func(int ...

  7. python 键盘中断子线程及graceful exiting方案

    最近需要实现一个服务程序的graceful exiting,保证在退出前关闭所有已创建的子线程 python借助KeyboardInterrupted异常响应键盘中断,因此首先尝试在子线程中try-c ...

  8. Linux0.11内核剖析--内核体系结构

    一个完整可用的操作系统主要由 4 部分组成:硬件.操作系统内核.操作系统服务和用户应用程序,如下图所示: 用户应用程序是指那些字处理程序. Internet 浏览器程序或用户自行编制的各种应用程序: ...

  9. linux0.11学习笔记(1)

    公布软件包包括内容: bootimage.Z - 具有美国键盘代码的压缩启动映像文件: rootimage.Z - 以1200kB 压缩的根文件系统映像文件: linux-0.11.tar.Z- 内核 ...

  10. Linux0.11启动过程

    从开机加电,到执行main函数之前的过程 好吧,这里应该是有执行3个汇编的文件,但是我不太了解.囧 从main函数,到启动OK(即可以响应用户操作了) 这个步骤做了3件事情: 创建进程0,使之具备在主 ...

随机推荐

  1. LLM开源小工具(基于代码库快速学习/纯shell调用LLM灵活管理系统)

    随着AI的各种信息的发展,LLM各种模型不断涌现,作为一名IT人员不得不向前走,不断探索学习发现新知识. 随着学习,也了解到一些对于模型的调用,从而解决一些问题,或者对已有工具或应用的重写.如下是两个 ...

  2. 【数学】主成分分析(PCA)的详细深度推导过程

    Based on Deep Learning (2017, MIT) book. 本文基于Deep Learning (2017, MIT),推导过程补全了所涉及的知识及书中推导过程中跳跃和省略的部分 ...

  3. 从0到1使用Webpack5 + React + TS构建标准化应用

    简介: 本篇文章主要讲解如何从一个空目录开始,建立起一个基于webpack + react + typescript的标准化前端应用. 作者 | 刘皇逊(恪语)来源 | 阿里开发者公众号 前言 本篇文 ...

  4. 选轻量应用服务器or云服务器ECS?一图帮你彻底区分

    简介:轻量应用服务器适合轻量级且访问量低的应用场景,更适合个人开发者.对新手小白更友好:而云服务器ECS可覆盖全业务场景(如大数据分析,深度学习等),要求用户有一定的开发技术能力. 本文首发于公众号& ...

  5. Spring Boot 微服务性能下降九成!使用 Arthas 定位根因

    简介: 接收到公司业务部门的开发反馈,应用在升级公司内部框架后,UAT(预生产)环境接口性能压测不达标. 背景 接收到公司业务部门的开发反馈,应用在升级公司内部框架后,UAT(预生产)环境接口性能压测 ...

  6. Quick BI:降低使用门槛,大东鞋业8000家门店的数据导航

    简介: 通过引入MaxCompute和Quick BI,大东解决了以往数据查询即刻导致数据库闪崩的状况,还搭建起完善的报表体系,稳定应对高频.高并发的数据分析. 大东鞋业一季大约有500款的新品.大区 ...

  7. 18.基于Consul的服务发现和ConsulManager管理

    192.168.10.14 prometheus.consul 192.168.10.100 各类服务 一.基于Consul的服务发现 Consul 是由 HashiCorp 开发的一个支持多数据中心 ...

  8. Ubuntu RDP服务

    这里先简单了解一下rdp和vnc的区别 VNC 就像我们使用向日葵一下远程操作别的电脑一下,只能有一人在操作 RDP 是无感式操作,在别人没知觉的情况下控制新的桌面 这是我个人的理解,有不对的地方望各 ...

  9. 算法~PBKDF2-SHA让密码更安全

    摘要:在当今的数字世界中,密码安全是至关重要的.为了保护用户密码免受未经授权的访问和破解,Password-Based Key Derivation Function 2 (PBKDF2)算法成为了一 ...

  10. go实现发送邮件验证码

    目录 开启SMTP服务: 发邮件测试 业务实现 开启SMTP服务: QQ邮箱参考下面连接: QQ邮箱如何开通SMTP服务 https://jingyan.baidu.com/article/00a07 ...