重学操作系统----15 | 中断和中断向量:Java/js 等语言为什么可以捕获到键盘输入?

简介: 当你拿到一个问题时,需要冷静下来思考和探索解决方案。你可以查资料、看视频或者咨询专家,但是在这之前,你先要进行一定的思考和梳理,有的问题可以直接找到答案,有的问题却需要继续深挖寻找其背后的理论支撑。

探索过程:如何设计响应键盘的整个链路?

当你拿到一个问题时,需要冷静下来思考和探索解决方案。你可以查资料、看视频或者咨询专家,但是在这之前,你先要进行一定的思考和梳理,有的问题可以直接找到答案,有的问题却需要继续深挖寻找其背后的理论支撑。

问题 1:我们的目标是什么?


我们的目标是在 Java/JS 中实现按键响应程序。这种实现有点像 Switch-Case 语句——根据不同的按键执行不同的程序,比如按下回车键可以换行,按下左右键可以移动光标。

问题 2:按键怎么抽象?


键盘上一般不超过 100 个键。因此我们可以考虑用一个 Byte 的数据来描述用户按下了什么键。按键有两个操作,一个是按下、一个是释放,这是两个不同的操作。对于一个 8 位的字节,可以考虑用最高位的 1 来描述按下还是释放的状态,然后后面的 7 位(0~127)描述具体按了哪个键。这样我们只要确定了用户按键/释放的顺序,对我们的系统来说,就不会有歧义。

问题 3:如何处理按键?使用操作系统处理还是让每个程序自己实现?


处理按键是一个通用程序,可以考虑由操作系统先进行一部分处理,比如:


用户按下了回车键,先由操作系统进行统一的封装,再把按键的编码转换为字符串Enter方便各种程序使用。


处理组合键这种操作,由操作系统先一步进行计算比较好。因为底层只知道按键、释放,组合键必须结合时间因素判断。


你可以把下面这种情况看作是一个Ctrl + C组合键,这种行为可以由操作系统进行统一处理,如下所示:

按下 Ctrl
按下 C
释放 Ctrl
释放 C

问题 4:程序用什么模型响应按键?


当一个 Java 或者 JS 写的应用程序想要响应按键时,应该考虑消息模型。因为如果程序不停地扫描按键,会给整个系统带来很大的负担。比如程序写一个while循环去扫描有没有按键,开销会很大。 如果程序在操作系统端注册一个响应按键的函数,每次只有真的触发按键时才执行这个函数,这样就能减少开销了。

问题 5:处理用户按键,需不需要打断正在执行的程序?


从用户体验上讲,按键应该是一个高优先级的操作,比如用户按 Ctrl+C 或者 Esc 的时候,可能是因为用户想要打断当前执行的程序。即便是用户只想要输入,也应该尽可能地集中资源给到用户,因为我们不希望用户感觉到延迟。


如果需要考虑到程序随时会被中断,去响应其他更高优先级的情况,那么从程序执行的底层就应该支持这个行为,而且最好从硬件层面去支持,这样速度最快。 这就引出了本课时的主角——中断。具体如何处理,见下面我们关于中断部分的分析。

问题 6:操作系统如何知道用户按了哪个键?


这里有一个和问题 5 类似的问题。操作系统是不断主动触发读取键盘按键,还是每次键盘按键到来的时候都触发一段属于操作系统的程序呢?


显然,后者更节省效率。


那么谁能随时随地中断操作系统的程序? 谁有这个权限?是管理员账号吗? 当然不是,拥有这么高权限的应该是机器本身。


我们思考下这个模型,用户每次按键,触发一个 CPU 的能力,这个能力会中断正在执行的程序,去处理按键。那 CPU 内部是不是应该有处理按键的程序呢?这肯定不行,因为我们希望 CPU 就是用来做计算的,如果 CPU 内部有自带的程序,会把问题复杂化。这在软件设计中,叫作耦合。CPU 的工作就是专注高效的执行指令。


因此,每次按键,必须有一个机制通知 CPU。我们可以考虑用总线去通知 CPU,也就是主板在通知 CPU。

20210224220409408.png

那么 CPU 接收到通知后,如何通知操作系统呢?CPU 只能中断正在执行的程序,然后切换到另一个需要执行的程序。说白了就是改变 PC 指针,CPU 只有这一种办法切换执行的程序。这里请你思考,是不是只有这一种方法:CPU 中断当前执行的程序,然后去执行另一个程序,才能改变 PC 指针?

2021022422042228.png

接下来我们进一步思考,CPU 怎么知道 PC 指针应该设置为多少呢?是不是 CPU 知道操作系统响应按键的程序位置呢?


答案当然是不知道。


因此,我们只能控制 CPU 跳转到一个固定的位置。比如说 CPU 一收到主板的信息(某个按键被触发),CPU 就马上中断当前执行的程序,将 PC 指针设置为 0。也就是 PC 指针下一步会从内存地址 0 中读取下一条指令。当然这只是我们的一个思路,具体还需要进一步考虑。而操作系统要做的就是在这之前往内存地址 0 中写一条指令,比如说让 PC 指针跳转到自己处理按键程序的位置。


讲到这里,我们总结一下,CPU 要做的就是一看到中断,就改变 PC 指针(相当于中断正在执行的程序),而 PC 改变成多少,可以根据不同的类型来判断,比如按键就到 0。操作系统就要向这些具体的位置写入指令,当中断发生时,接管程序的控制权,也就是让 PC 指针指向操作系统处理按键的程序。

问题 7:主板如何知道键盘被按下?


经过一层一层地深挖“如何设计响应键盘的整个链路?”这个问题,目前操作系统已经能接管按键,接下来,我们还需要思考主板如何知道有按键,并且通知 CPU。


你可以把键盘按键看作按下了某个开关,我们需要一个芯片将按键信息转换成具体按键的值。比如用户按下 A 键,A 键在第几行、第几列,可以看作一个电学信号。接着我们需要芯片把这个电学信号转化为具体的一个数字(一个 Byte)。转化完成后,主板就可以接收到这个数字(按键码),然后将数字写入自己的一个寄存器中,并通知 CPU。


为了方便 CPU 计算,CPU 接收到主板通知后,按键码会被存到一个寄存器里,这样方便处理按键的程序执行。

思路的整理:中断的设计


整体设计分成了 3 层,第一层是硬件设计、第二层是操作系统设计、第三层是程序语言的设计。


按键码的收集,是键盘芯片和主板的能力。主板知道有新的按键后,通知 CPU,CPU 要中断当前执行的程序,将 PC 指针跳转到一个固定的位置,我们称为一次中断(interrupt)。


考虑到系统中会出现各种各样的事件,我们需要根据中断类型来判断PC 指针跳转的位置,中断类型不同,PC 指针跳转的位置也可能会不同。比如按键程序、打印机就绪程序、系统异常等都需要中断,例如系统调用,也需要中断正在执行的程序,切换到内核态执行内核程序。


因此我们需要把不同的中断类型进行分类,这个类型叫作中断识别码。比如按键,我们可以考虑用编号 16,数字 16 就是按键中断类型的识别码。不同类型的中断发生时,CPU 需要知道 PC 指针该跳转到哪个地址,这个地址,称为中断向量(Interupt Vector)


你可以考虑这样的实现:当编号 16 的中断发生时,32 位机器的 PC 指针直接跳转到内存地址 16*4 的内存位置。如果设计最多有 255 个中断,编号就是从 0~255,刚好需要 1K 的内存地址存储中断向量——这个 1K 的空间,称为中断向量表


32位刚好是4个字节,也就是说255个4字节就可以存储整个向量表.


因此 CPU 接收到中断后,CPU 根据中断类型操作 PC 指针,找到中断向量。操作系统必须在这之前,修改中断向量,插入一条指令。比如操作系统在这里写一条Jump指令,将 PC 指针再次跳转到自己处理对应中断类型的程序。

20210224220649229.png

操作系统接管之后,以按键程序为例,操作系统会进行一些处理,包括下面的几件事情:


将按键放入一个队列,保存下来。这是因为,操作系统不能保证及时处理所有的按键,比如当按键过快时,需要先存储下来,再分时慢慢处理。


计算组合键。可以利用按下、释放之间的时间关系。


经过一定计算将按键抽象成消息(事件结构或对象)。


提供 API 给应用程序,让应用程序可以监听操作系统处理后的消息。


分发按键消息给监听按键的程序。


所以程序在语言层面,比如像 Java/Node.js 这种拥有虚拟机的语言,只需要对接操作系统 API 就可以了。

中断的类型


按照中断的触发方分成同步中断异步中断


根据中断是否强制触发分成可屏蔽中断不可屏蔽中断


中断可以由 CPU 指令直接触发,这种主动触发的中断,叫作同步中断。同步中断有几种情况。


  1. 系统调用,需要从用户态切换内核态,这种情况需要程序触发一个中断,叫作陷阱(Trap),中断触发后需要继续执行系统调用。
  2. 还有一种同步中断情况是错误(Fault),通常是因为检测到某种错误,需要触发一个中断,中断响应结束后,会重新执行触发错误的地方,比如缺页中断 pagefault。
  3. 程序的异常,这种情况和 Trap 类似,用于实现程序抛出的异常。


另一部分中断不是由 CPU 直接触发,是因为需要响应外部的通知,比如响应键盘、鼠标等设备而触发的中断。这种中断我们称为异步中断。


CPU 通常都支持设置一个中断屏蔽位(一个寄存器),设置为 1 之后 CPU 暂时就不再响应中断。对于键盘鼠标输入,比如陷阱、错误、异常等情况,会被临时屏蔽。但是对于一些特别重要的中断,比如 CPU 故障导致的掉电中断,还是会正常触发。可以被屏蔽的中断我们称为可屏蔽中断,多数中断都是可屏蔽中断。


这也可以解释为什么有的程序不能被ctrl + c终结.

问题1: Java/Js 等语言为什么可以捕获到键盘输入?


为了捕获到键盘输入,硬件层面需要把按键抽象成中断,中断 CPU 执行。CPU 根据中断类型找到对应的中断向量。操作系统预置了中断向量,因此发生中断后操作系统接管了程序。操作系统实现了基本解析按键的算法,将按键抽象成键盘事件,并且提供了队列存储多个按键,还提供了监听按键的 API。因此应用程序,比如 Java/Node.js 虚拟机,就可以通过调用操作系统的 API 使用键盘事件。

问题2:操作系统可以处理键盘按键可以理解,那么我们开机的时候也可以使用键盘,但是那时候操作系统还没有载入内存,这个怎么解释?


主板的一块 ROM 上往往还有一个简化版的操作系统,叫 BIOS(Basic Input/Ouput System)。在 OS 还没有接管计算机前,先由 BIOS 管理机器,并协助加载 OS 到内存。早期的 OS 还会利用 BIOS 的能力,现代的 OS 接管后,就会替换掉 BIOS 的中断向量。


目录
相关文章
|
1天前
|
存储 人工智能 JavaScript
Harmony OS开发-ArkTS语言速成二
本文介绍了ArkTS基础语法,包括三种基本数据类型(string、number、boolean)和变量的使用。重点讲解了let、const和var的区别,涵盖作用域、变量提升、重新赋值及初始化等方面。期待与你共同进步!
61 47
Harmony OS开发-ArkTS语言速成二
|
2月前
|
监控 Java API
如何使用Java语言快速开发一套智慧工地系统
使用Java开发智慧工地系统,采用Spring Cloud微服务架构和前后端分离设计,结合MySQL、MongoDB数据库及RESTful API,集成人脸识别、视频监控、设备与环境监测等功能模块,运用Spark/Flink处理大数据,ECharts/AntV G2实现数据可视化,确保系统安全与性能,采用敏捷开发模式,提供详尽文档与用户培训,支持云部署与容器化管理,快速构建高效、灵活的智慧工地解决方案。
|
3月前
|
设计模式 算法 安全
实时操作系统(RTOS)深度解析及Java实现初探
【10月更文挑战第22天】实时操作系统(RTOS,Real-Time Operating System)是一种能够在严格的时间限制内响应外部事件并处理任务的操作系统。它以其高效、高速、可靠的特点,广泛应用于工业自动化、航空航天、医疗设备、交通控制等领域。本文将深入浅出地介绍RTOS的相关概念、底层原理、作用与功能,并探讨在Java中实现实时系统的方法。
106 1
|
3月前
|
缓存 JavaScript 前端开发
Java 如何确保 JS 不被缓存
【10月更文挑战第19天】在 Java 中,可以通过设置 HTTP 响应头来确保 JavaScript 文件不被浏览器缓存。方法包括:1. 使用 Servlet 设置响应头,通过 `doGet` 方法设置 `Expires`、`Cache-Control` 和 `Pragma` 头;2. 在 Spring Boot 中配置拦截器,通过 `NoCacheInterceptor` 类和 `WebConfig` 配置类实现相同功能。这两种方法都能确保每次请求都能获取到最新的 JavaScript 内容。
|
3天前
|
开发框架 JavaScript 前端开发
Harmony OS开发-ArkT语言速成一
本文介绍ArkTS语言,它是鸿蒙生态的应用开发语言,基于TypeScript,具有静态类型检查、声明式UI、组件化架构、响应式编程等特性,支持跨平台开发和高效性能优化。ArkTS通过强化静态检查和分析,提升代码健壮性和运行性能,适用于Web、移动端和桌面端应用开发。关注我,带你轻松掌握HarmonyOS开发。
28 5
Harmony OS开发-ArkT语言速成一
|
15天前
|
JavaScript 前端开发
捕获键盘输入和输入组合键的js库hotkeys
捕获键盘输入和输入组合键的js库hotkeys
|
2月前
|
JSON 前端开发 JavaScript
聊聊 Go 语言中的 JSON 序列化与 js 前端交互类型失真问题
在Web开发中,后端与前端的数据交换常使用JSON格式,但JavaScript的数字类型仅能安全处理-2^53到2^53间的整数,超出此范围会导致精度丢失。本文通过Go语言的`encoding/json`包,介绍如何通过将大整数以字符串形式序列化和反序列化,有效解决这一问题,确保前后端数据交换的准确性。
56 4
|
2月前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
58 4
|
2月前
|
开发框架 JavaScript 前端开发
HarmonyOS UI开发:掌握ArkUI(包括Java UI和JS UI)进行界面开发
【10月更文挑战第22天】随着科技发展,操作系统呈现多元化趋势。华为推出的HarmonyOS以其全场景、多设备特性备受关注。本文介绍HarmonyOS的UI开发框架ArkUI,探讨Java UI和JS UI两种开发方式。Java UI适合复杂界面开发,性能较高;JS UI适合快速开发简单界面,跨平台性好。掌握ArkUI可高效打造符合用户需求的界面。
126 8
|
3月前
|
Java 程序员 编译器
在Java编程中,保留字(如class、int、for等)是具有特定语法意义的预定义词汇,被语言本身占用,不能用作变量名、方法名或类名。
在Java编程中,保留字(如class、int、for等)是具有特定语法意义的预定义词汇,被语言本身占用,不能用作变量名、方法名或类名。本文通过示例详细解析了保留字的定义、作用及与自定义标识符的区别,帮助开发者避免因误用保留字而导致的编译错误,确保代码的正确性和可读性。
65 3