事件监听器的实现——汇编与高级语言
Implementation of event listener - assembly vs high level language
程序集:
请考虑 Intel 8080. To handle an interrupt (for example from the keyboard), you simply have the keyboard pull the 'INT' pin high and place a 'vector' on the databus. The vector points to the location of the interrupt service routine (ISR) in the assembly program code. See this answer 了解更多详情。
高级语言:
考虑附加事件侦听器的概念。例如考虑 Javascript's keydown
event listener. An example:
document.getElementById("demo").addEventListener("keydown", myFunction);
function myFunction() {
document.getElementById("demo").style.backgroundColor = "red";
}
当 'demo' 元素处于焦点时按下某个键,将调用 myFunction
。
关系:
附加事件侦听器的概念与在程序集中具有 ISR 的概念有何关系?
我假设直接响应键盘中断的函数是 1) OS 内核的一部分和 2) 用 C 编写的。(这些假设是否正确?)
用户程序如何告诉这个 OS 中断处理程序在中断时提醒它? OS 函数在处理中断时是否保留一个可变大小的函数列表以供回调?那么 addEventListener
是否将用户回调函数附加到 OS 函数的列表中?
注意:我特别选择 Intel 8080 作为示例,因为与现代 CPU 相比,它没有辅助 OS 相关功能的奇特功能。
从按下一个键到 JavaScript keydown
事件侦听器中的代码成为 运行,这是一段漫长而复杂的旅程。假设一个 USB 键盘,通过硬件和软件层的路径是这样的:
- 键盘(硬件层)
- USB控制器(硬件层)
- USB控制器驱动(内核层)
- USB通用驱动(内核层)
- HID 通用驱动程序(内核层)
- HID键盘驱动(内核层)
- OSUI事件处理(kernel/user层)
- 浏览器UI事件循环(用户层)
- 浏览器UI事件处理器(用户层)
- JavaScript引擎事件处理器(用户层)
keydown
事件监听器(用户层)
其中的大部分内容并不像您假设的那样有效。特别是没有键盘中断。相反,有一个 USB 中断,但它并不像您想象的那样工作。当您按下 USB 键盘上的某个键时,不会将消息发送到计算机并生成中断。相反,键盘将按键添加到其内部队列并等待计算机对其进行轮询。
这是因为USB通信完全由USB主机(电脑)调度。除非响应主机的请求,否则 USB 设备不允许在总线上通信。由于 USB 事务被安排在一毫秒的帧中,通常操作系统只会每毫秒轮询一次 USB 键盘,要求它报告自上次轮询设备以来发生的任何事件。只有当键盘响应这个请求时(或者可能当帧中所有预定的传输都完成时),USB 控制器才会产生中断。
键盘的响应将以 HID (Human Interface Device) 报告的形式出现。 HID 堆栈将对其进行解码以查看报告了哪些键盘事件,并将它们转换为所有键盘类型通用的格式。这将由操作系统中的某种用户界面层进一步处理(例如,Windows 上的 "Win32" API 层,或 Linux 上的 X 服务器)然后放入浏览器的 UI 事件队列。
浏览器不会因为按键被按下而"interrupted"。相反,浏览器将有一个主要的 UI 界面事件循环,并且事件只会在程序中的单个定义点一次处理一个。这个循环所做的就是从 UI 事件队列中提取事件并将它们分派到浏览器中的适当代码。当队列为空时(通常几乎所有时间),循环只是等待一个事件。在等待期间,事件循环的线程未由 OS 调度,并且未 运行 任何 CPU.
一旦 UI 事件循环获得键盘事件,它就会传递到浏览器的键盘事件处理程序,然后将其传递给 JavaScript 引擎。然后引擎将执行指定为 keydown
事件侦听器的函数。引擎可能有自己的事件队列,因为 JavaScript 事件通常也一次只处理一个。
None 除了内核中的一小部分代码在调用实际调度它们的 C 代码之前充当中断和系统调用的着陆点外,其中的
None 将用汇编语言编写。
程序集:
请考虑 Intel 8080. To handle an interrupt (for example from the keyboard), you simply have the keyboard pull the 'INT' pin high and place a 'vector' on the databus. The vector points to the location of the interrupt service routine (ISR) in the assembly program code. See this answer 了解更多详情。
高级语言:
考虑附加事件侦听器的概念。例如考虑 Javascript's keydown
event listener. An example:
document.getElementById("demo").addEventListener("keydown", myFunction);
function myFunction() {
document.getElementById("demo").style.backgroundColor = "red";
}
当 'demo' 元素处于焦点时按下某个键,将调用 myFunction
。
关系:
附加事件侦听器的概念与在程序集中具有 ISR 的概念有何关系?
我假设直接响应键盘中断的函数是 1) OS 内核的一部分和 2) 用 C 编写的。(这些假设是否正确?)
用户程序如何告诉这个 OS 中断处理程序在中断时提醒它? OS 函数在处理中断时是否保留一个可变大小的函数列表以供回调?那么 addEventListener
是否将用户回调函数附加到 OS 函数的列表中?
注意:我特别选择 Intel 8080 作为示例,因为与现代 CPU 相比,它没有辅助 OS 相关功能的奇特功能。
从按下一个键到 JavaScript keydown
事件侦听器中的代码成为 运行,这是一段漫长而复杂的旅程。假设一个 USB 键盘,通过硬件和软件层的路径是这样的:
- 键盘(硬件层)
- USB控制器(硬件层)
- USB控制器驱动(内核层)
- USB通用驱动(内核层)
- HID 通用驱动程序(内核层)
- HID键盘驱动(内核层)
- OSUI事件处理(kernel/user层)
- 浏览器UI事件循环(用户层)
- 浏览器UI事件处理器(用户层)
- JavaScript引擎事件处理器(用户层)
keydown
事件监听器(用户层)
其中的大部分内容并不像您假设的那样有效。特别是没有键盘中断。相反,有一个 USB 中断,但它并不像您想象的那样工作。当您按下 USB 键盘上的某个键时,不会将消息发送到计算机并生成中断。相反,键盘将按键添加到其内部队列并等待计算机对其进行轮询。
这是因为USB通信完全由USB主机(电脑)调度。除非响应主机的请求,否则 USB 设备不允许在总线上通信。由于 USB 事务被安排在一毫秒的帧中,通常操作系统只会每毫秒轮询一次 USB 键盘,要求它报告自上次轮询设备以来发生的任何事件。只有当键盘响应这个请求时(或者可能当帧中所有预定的传输都完成时),USB 控制器才会产生中断。
键盘的响应将以 HID (Human Interface Device) 报告的形式出现。 HID 堆栈将对其进行解码以查看报告了哪些键盘事件,并将它们转换为所有键盘类型通用的格式。这将由操作系统中的某种用户界面层进一步处理(例如,Windows 上的 "Win32" API 层,或 Linux 上的 X 服务器)然后放入浏览器的 UI 事件队列。
浏览器不会因为按键被按下而"interrupted"。相反,浏览器将有一个主要的 UI 界面事件循环,并且事件只会在程序中的单个定义点一次处理一个。这个循环所做的就是从 UI 事件队列中提取事件并将它们分派到浏览器中的适当代码。当队列为空时(通常几乎所有时间),循环只是等待一个事件。在等待期间,事件循环的线程未由 OS 调度,并且未 运行 任何 CPU.
一旦 UI 事件循环获得键盘事件,它就会传递到浏览器的键盘事件处理程序,然后将其传递给 JavaScript 引擎。然后引擎将执行指定为 keydown
事件侦听器的函数。引擎可能有自己的事件队列,因为 JavaScript 事件通常也一次只处理一个。
None 除了内核中的一小部分代码在调用实际调度它们的 C 代码之前充当中断和系统调用的着陆点外,其中的
None 将用汇编语言编写。