【问题标题】:Implementation of event listener - assembly vs high level language事件监听器的实现——汇编 vs 高级语言
【发布时间】:2018-12-16 16:57:48
【问题描述】:

大会:

考虑Intel 8080。要处理中断(例如来自键盘),您只需让键盘将“INT”引脚拉高并在数据总线上放置一个“向量”。该向量指向汇编程序代码中中断服务程序 (ISR) 的位置。详情请见this answer

高级语言:

考虑附加事件侦听器的概念。例如考虑Javascript's keydown event listener。一个example

document.getElementById("demo").addEventListener("keydown", myFunction);

function myFunction() {
    document.getElementById("demo").style.backgroundColor = "red";
}

当'demo'元素处于焦点时按下一个键,myFunction 被调用。

关系:

附加事件侦听器的概念与在程序集中包含 ISR 的概念有何关系?

我假设直接响应键盘中断的函数是 1) 操作系统内核的一部分和 2) 用 C 编写的。(这些假设正确吗?)

用户程序如何告诉这个操作系统中断处理程序在中断时提醒它?操作系统函数在处理中断时是否保留一个可变大小的函数列表以进行回调?那么addEventListener 是否将用户回调函数附加到 OS 函数的列表中?

注意:我之所以选择 Intel 8080 作为示例,是因为与现代 CPU 相比,它不具备辅助操作系统相关功能的花哨功能。

【问题讨论】:

  • 是的,用户空间中的异步回调函数或信号处理程序非常类似于在裸机上运行的程序的中断处理程序。 (例如,现代硬件上的内核,或任何独立程序。)Javascript 有点特别,因为作为一种托管语言,VM 知道何时可以安全地异步运行某些东西。
  • 但是不,事件回调不直接挂钩到实际的内核中断处理程序;内核中有几层软件。 (对不起,我没有时间写一些值得发布的东西作为答案。)
  • “附加事件侦听器的概念与在程序集中包含 ISR 的概念有何关系?” ...从操作系统内核的角度来看,事件处理程序/侦听器通常是非中断的“商务逻辑代码”,这是在服务中断后在主线程中完成的事情。您不必以任何快速的方式处理事件处理程序,它们可以等待相当长的几微秒并且不会发生任何可怕的事情,而且操作系统通常只会将主事件委托给用户应用程序,事件循环在其中运行,处理事件,并跟踪自己的侦听器 + 调用它们(例如 android)。
  • 与 8080 的关系更像是键盘中断处理键盘,将击键放入缓冲区(中断部分),然后运行代码最终会读取缓冲区并对其进行操作(on-事件处理程序)。
  • 8080 不希望在中断确认周期期间看到总线上的 vector,而是希望看到它会执行的 opcode。因为它只能获取一个字节,所以可能的操作码有点有限,通常使用RSTx,但你也可以放一个NOP(不是那么有用)或任何其他单字节命令(如@987654331 @ 并在主循环中等待设置进位标志)。

标签: events assembly operating-system interrupt


【解决方案1】:

从按下一个键到运行 JavaScript keydown 事件侦听器中的代码,这是一个漫长而复杂的过程。假设一个 USB 键盘,通过硬件和软件层的路径是这样的:

  • 键盘(硬件层)
  • USB 控制器(硬件层)
  • USB 控制器驱动程序(内核层)
  • USB 通用驱动程序(内核层)
  • HID 通用驱动程序(内核层)
  • HID 键盘驱动程序(内核层)
  • OS UI 事件处理(内核/用户层)
  • 浏览器 UI 事件循环(用户层)
  • 浏览器 UI 事件处理程序(用户层)
  • JavaScript 引擎事件处理程序(用户层)
  • keydown 事件监听器(用户层)

其中大部分都不像你想象的那样工作。特别是没有键盘中断。取而代之的是一个 USB 中断,但它并不像您想象的那样工作。当您按下 USB 键盘上的某个键时,它不会导致将消息发送到计算机并产生中断。相反,键盘将按键添加到其内部队列中并等待计算机对其进行轮询。

这是因为 USB 通信完全由 USB 主机(计算机)调度。除非响应主机的请求,否则不允许 USB 设备在总线上通话。由于 USB 事务被安排在一毫秒的帧中,通常操作系统只会每毫秒轮询一次 USB 键盘,要求它报告自上次轮询设备以来发生的任何事件。仅当键盘响应此请求时(或者可能当帧中的所有计划传输都完成时),USB 控制器才会生成中断。

来自键盘的响应将以HID (Human Interface Device) 报告的形式出现。 HID 堆栈将对其进行解码以查看已报告的键盘事件,并将它们转换为所有键盘类型通用的格式。这将由操作系统中的某种用户界面层(例如 Windows 上的“Win32”API 层或 Linux 上的 X 服务器)进一步处理,然后放入浏览器的 UI 事件队列中。

浏览器不会因为按键被“中断”。相反,浏览器将有一个主 UI 界面事件循环,并且事件只会在程序中的单个定义点一次处理一个。这个循环所做的只是从 UI 事件队列中提取事件并将它们分派到浏览器中的适当代码。当队列为空时(通常几乎是所有时间),循环只是等待一个事件。在等待期间,事件循环的线程没有被操作系统调度,也没有在任何 CPU 上运行。

一旦 UI 事件循环获得一个键盘事件,它就会被传递到浏览器的键盘事件处理程序,然后再将它传递给 JavaScript 引擎。然后引擎将执行分配为keydown 事件侦听器的函数。引擎可能有自己的事件队列,因为 JavaScript 事件通常也一次只处理一个。

除了在调用实际调度它们的 C 代码之前充当中断和系统调用的着陆点的内核中的少量代码外,所有这些都不会用汇编语言编写。

【讨论】:

  • 一些 用户/内核接口的工作更像是 I/O 中断,例如 POSIX 异步 I/O 将发送信号而不是解除阻塞线程睡眠在epoll 或等待read()O_NONBLOCK fd 上进行轮询。这不是一个方便的 API,它很少用于任何事情,但它证明了这样的设计是可能的(许多人会争辩说它是一个 bad 内核用户的设计)。 What is the status of POSIX asynchronous I/O (AIO)? 但是+1,没有人将 AIO 用于键盘 I/O,如果这可能的话。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多