专栏首页程序员小灰天天当键盘侠,你知道从按键到响应的底层原理吗?

天天当键盘侠,你知道从按键到响应的底层原理吗?

KEYBOARD

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

看完本文,相信你就能了解键盘的本质,知晓这些问题答案。

01

相关介绍

键盘编码器

键盘编码器(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 字节的扫描码。也就是说键盘中断的次数不是你按键、弹起的次数,而是按键、弹起对应的通码、断码(第一套)字节数。由此可以看出平时我们敲键盘时那是发生了无数次的中断呐。

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

02

键盘中断流程

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

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

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

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

03

键盘中断服务程序

键盘中断在所有的可屏蔽中断中优先级仅次于时钟中断,也需要尽快的处理。在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。

04

键处理程序

键的扫描码有通码和断码,有着不同的处理,主要的键处理程序我分为了以下几类(各点开头出现的名字都是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. 一直按着某个键时会一直触发键盘中断,若是普通的字符键,电脑屏幕可能会出现一直打印某个字符的现象。若是一些控制键,则中断程序可能会不停地将这个键设为按下状态。当然,键盘中断程序是否记录上次按键取决于具体实现,大多是不记录的,触发一次键盘中断就处理一个扫描码。

END

关于键盘控制输入的原理就是这样,这条线应该还是很清楚的。键盘输入是以键盘中断为核心的,如果还不是很清楚,可以回头看看键盘中断的流程图。

本文分享自微信公众号 - 程序员小灰(chengxuyuanxiaohui),作者:Rand

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-04-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • AI 放大招,可能终结勇士骑士的粉丝骂战

    网络上,有一种人好为人师、喜欢占据道德制高点、以口舌之争获胜为乐,他们就是键盘侠。今天,全网因为「勇士骑士粉丝对喷群」事件引发的各种互怼微信群,更是激发了他们的...

    HyperAI超神经
  • 为了挽回爱情,他竟举报了情敌......

    ? 一档聚焦维护网络秩序、营造风清气正网络空间的特别栏目,小助手将围绕网络社交场景下的不良行为和典型案例与大家展开探讨。 “什么是最难受理的举报问题?”   ...

    腾讯举报中心
  • 空降CTO是救世主?不,也可能是臭流氓

    但在疫情的影响下,不少企业都受到了不同程度的影响,为度过经济效益下降的暂时性困难期,很多企业迫于生存压力而选择减薪、降薪或人事调整来缓解运营压力。

    吃草的罗汉
  • 【挖祖坟】聊聊“密码朋克”运动与区块链发展路径

    区块链大本营
  • 源码系列:基于FPGA的电子琴设计(附源工程)

    大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。...

    FPGA技术江湖
  • 源码系列:基于FPGA的PS2通信电路设计(附源码)

    今天给大侠带来基于FPGA的PS2通信电路设计,附源码,获取源码,请在“FPGA技术江湖”公众号内回复“PS2源码”,可获取源码文件。话不多说,上货。

    FPGA技术江湖
  • 什么是第一性原理?

    大家都知道了伊龙马斯克(Elon Musk)把这个原理用得好,可以把火箭发射成本降低到原先的几十甚至上百分之一。

    王树义
  • 暴走的程序员

    今天一篇 "a sad day for rust" 霸榜了 hacker news。actix-web 的作者 fafhrd91 在无法忍受 "unsafe s...

    tyrchen
  • Epoch不仅过时,而且有害?Reddit机器学习板块展开讨论

    有一天,一个调参侠在训练一个深度学习模型,要用到的两个数据集损失函数不同,数据集的大小还不是固定的,每天都在增加。

    量子位
  • 和产品争论MySQL底层如何实现order by的,惨败!

    只听到产品又开始口若黄河:我需要要查询到city是“上海”的所有人的name,并且还要按name排序返回前1000人的name、age。

    JavaEdge
  • 代码传奇 | 身价10亿的程序员 雷军当年也为他打工——WPS之父 求伯君

    1988年的春天,深圳蔡屋围酒店501房间,一个24岁的小伙子正死死地盯着屏幕,双手不停地敲打键盘。

    不安分的猿人
  • 人过三十才想明白的那点事:送给奋斗路上的强者

    在当今社会结构下,人要出头,要在物质上成功,大家共同的切身感受就是官、商、明星及各个领域顶尖人才,才可以过上一劳永逸的生活,普通百姓就是庸碌一生为其效力的工蚁,...

    程序员互动联盟
  • 百度SEO排名信号,值得关注的6个方向有哪些?

    对于每一个SEO人员,我们都非常清楚,当你把所有的百度SEO排名信号,组合在一起的时候,实际上它就构成了搜索引擎的算法。

    蝙蝠侠IT
  • iOS 即时通讯 + 仿微信聊天框架 + 源码

    更新:2017年8月1日 实在是抱歉,git上的Demo这么久,有问题自己没有发现!肯定给大家造成过不方便,抱歉!git上Demo刚重新上传,要有需要的可以去...

    Mr.RisingSun
  • 必收!有问题不求人?4种方法max你的搜索技能

    随着大数据时代的到来,信息量较几年前是指数型增长,这个时代呈现知识大爆炸的趋势,不少时间管理、精力管理以及学习能力,还有思维方式的课程和大牛风起云涌,数不胜数。

    华章科技
  • 按键精灵——自动关闭广告弹窗

    其实学习按键精灵最最关键的,就是学以致用,投入到生产实践中,提高工作效率。这也是为什么很多人热衷于它的重要原因。

    Ed_Frey
  • 计算机性能之殇(三)-- 通用电子计算机的胎记:事件驱动

    Event-Driven(事件驱动)这个词这几年随着 Node.js® 的大热也成了一个热词,似乎已经成了“高性能”的代名词,殊不知事件驱动其实是通用计算机的胎...

    Fundebug
  • 聊聊我的第一篇10万+,同时反驳某些评论

    知乎后台显示我的回答《如何衡量一个人的 JavaScript 水平?》的阅读量已经超过了10万,具体截止2019年5月20号是115172。

    Fundebug
  • 【Augustzhang 张元龙】知根知底,方能游刃有余

    小编语:据江湖传闻,龙哥从初中就开始写代码,高中通过计算机竞赛免试上了大学,大学里则是ACM大神。2010年毕业加入腾讯,先后从事密保、验证码等后台研发工作,...

    TEG云端专业号

扫码关注云+社区

领取腾讯云代金券