洗澡的时候不知怎么突然又想到“中断”。 以前上学的时候就听的就不是很明白,许多书里也讲的似是而非,在后来编程的时候又遇到很多名词像 addListener,bind,槽,回调,事件,消息等等似乎都跟“中断”这个词有关。所以也上网看过许多帖子的解释,过程虽然很清晰明了,但是总觉得还是哪里隔着一层膜,让人那么的不舒服。 经常性的解释就是:
loop {
check A //假设check实在检查某事件是否发生
do B
check C
do D
}
在 B 执行时,如果 C 发生了,C 不能得到及时相应,或者 A 一直不发生却一直检查 A 效率很低。 用中断机制时,
loop {
do B
do D
}
A,C 发生了会主动发出信号,CPU 再去处理。之前注册的两个回调 do A {} do B {}
实际上,在底层,CPU 执行完每条指令时都会去检查一个中断标志位,当发现某一位置 1(可能)时,知道有中断发生(不考虑多中断),CPU 会保存现场(context),转而执行我们提前注册过的程序,即 ISR(Interrupt Service Routine),实际就是一种回调函数。ISR 会调用具体硬件驱动程序的中断例程,这里的中断例程又是一种回调(linux 会用 request_irq 注册),它会去读硬件寄存器取得事件的具体信息。
之后的就不太熟悉了,猜测的,希望有高人教我 Linux 可能会是层层回调机制,最上层的应用程序注册的回调最终会被调用。 windows 可能用的事件机制(消息机制,一回事),事件从底层层层上传,windows 窗口程序有一个线程专门循环 GetMessage,检查一个事件队列。
不管怎么样,检查某个事件是否发生本质都是轮询+check,它没在代码里看到,只是发生在别的地方了,比如 CPU 层级;或者换了一种方式。经常举得例子里实际是用听力代替了视力去轮询(觉得这里有什么可以挖掘,还没想到),一定范围内,听力比视力的监察范围更广。
所谓的实时性,就是提高到了指令周期级别。每秒十亿次,但是在无限细分下去,仍然有无法响应的盲点,因为每一周期就是一个轮询。并行性也就是利用计算机极快的速度与人的反应的差别。并不是一直在运行,但是看上去似乎是的。
光速 30 万公里每秒,人的肉眼捕捉不到 24 帧以下的空隙,当有那么快的速度时,人就可以做到分身术了
cpu 的中断引脚是异步的,只管收到设备的信号就有中断了。
"检查某个事件是否发生本质都是 轮询+check"这句的一个反例是时钟中断,可编程时钟 (PIT) 就绝对不会是靠轮询来实现的。
相反,cpu 为了更好的利用主线,处处都留着异步的设计的痕迹。轮询 (或者同步操作) 在 cpu 一级,则属于能避免就避免的
#18 楼 @ninehills 只要没屏蔽都会压栈的,之前还会进入 trap gate 切换 cpu 的权限。操作系统可以选择忽略它们,但这由不得 cpu。
#21 楼 @ninehills 我写过操作系统,对 cpu 不能没入门的。前面的问题您没有回答,cflags 的中断标志位是做什么用的? 『中断发生时也就是设置一下中断标志位』,是真的这样子么?
#26 楼 @ninehills 不要拿 51 当好东西,落后二十年了。
英特尔的手册在那里可以免费下载,APIC 之类做的事情很多很多,这部分语义很微妙,但仔细看是可以找到主线的。如果连这种概念都含含混混的不能给个准,英特尔的工程师也就只能停留在 51 这层次上了。
#30 楼 @ninehills #29 楼 @jiffies
真理、本质这种词还是躲远些好。否则越来越糊涂。
你看到了中断发生时,51 设置中断标志位。但是中断标志位是做什么的你没有看。设置 IF 是为了防止中断时产生其它中断而造成同步问题。也就是说,在中断的处理例程中,不允许并发。51 这样自动设置,是个很简陋的设计。现代的操作系统和 CPU,是希望中断标志位设置的时间越短越好。x86 发生中断时会做很多检查,不过这检查更多是权限的检查,而把中断作为一个事实接受下来,会穿过 IDT,从 ring3 跳到 ring0,切换用户栈到内核栈,把一坨寄存器的值和中断号压进去,操作系统才知道是发生了哪个中断。很多事情需要做,中断的发生就是这么一个开关。信号在总线上按照同一个时钟周期流着,经过了一个与门,就打开了。
如果说检查,一个开关算什么检查,除非电流可以往回跑。除非水库开闸淹死了人,那责任是水的,而不是开闸的。
#49 楼 @ninehills 例如 add 带内存操作数时,CPU 读入 add 指令,在执行中发现内存地址不在 cache 中,就中断了这个指令产生一个 page fault,跑去载内存后再回来重新执行 add,如果你是操作系统,就可以注册一个 page fault 的 handler,然后就可以选择不重新继续执行这个 add 之类的。。
先不提流水线,很多 CPU 指令的执行就包括 fetch, decode, ALU, access memory, update register, update pc 这几步,从定义看就是执行被打断了 ...
从化学反应的角度看,原子是不可分的,但是核反应就不一样了 ...
bug fix: 呃.. 化学反应里原子也是可分的
#52 楼 @ninehills 总之“一个最基本的机器指令能被中断"。有没有打破原子性,取决于你对“原子性”这个概念的定义,“能被中断”和“原子操作”不是同一个概念
在我看来,CPU 每次执行一条指令后,都要 check 中断状态是很 SB 的,CPU 原子指令与中断并不会冲突,因为 中断第一件事情,就是保存状态,研究中断是很 10 前的事情,现在完全没有去了解了;不过 ·@fleuria 的认知明显更正确
花点钱买个 demo 板 + jtag, 跟一下都明白了。
对我接触的几种 cpu 而言:
arm7tdmi, msp430, cortexa9, cortex-m0
中断就是这个意思:
当产生中断时,cpu 会看中断向量表,并保存目前的环境,然后跳转,执行中断向量所指的指令。执行完,回到刚才运行的语句,运行下一句。
多核没接触过。
#26 楼 @ninehills 现代的 x86 以及 x86_64 CPU 早已不是单纯的冯诺依曼结构了,事实上,核心部分是相当纯粹的哈佛结构。不然你也不会看到 L1 Cache 是区分数据和指令的。
http://en.wikipedia.org/wiki/Athlon_64#Sledgehammer_.28130.C2.A0nm_SOI.29
#66 楼 @jinleileiking 恩,确实不该黑 51。如果能把 51 学好,也断不至于说出那些偏颇的话出来。
但以后用 51 赚钱会越来越难。直到前两年我还对手持设备用上多核处理器的可行性感到十分怀疑,可是现在的手机都四核了。未来是现代体系结构的,它们需要面对更加复杂的问题。考虑这么多问题应接不暇的时候,谈本质其实是想偷懒。
谁要能从 8086 的门电路一路往上解释到 IOCP 就牛叉了。就像我面试老喜欢问人一个问题:“百度一下,你觉得都发生了什么?”,感谢这个问题让我开始回忆单片机。
#65 楼 @jinleileiking 我不能同意,电路的设计也是编程,它被设计完成这么一个循环流程操作。我觉得不要把计算机想的太神奇,它诞生还没有一个人的寿命长,基本思想是朴素的。谢谢参与讨论
看过....感觉这个应该是一个硬件级的问题,其实大家讨论的时候主要都是一个底层上的"主动轮询"使 CPU 感知中断,还是中断触发信号存在迫使 CPU 感知...这个没研究过硬件..不懂~不过感激大牛们的讨论
抱歉挖一下坟,我的看法和楼主一致,下面是我的总结: 中断其实是硬件轮询,只是恰好利用上 cpu 的机制而产生一种比轮询效率高的错觉,因为 cpu 是本身是轮询的,即隔一段时间进行分片调度切换上下文,这时候正好可以取检查有没有事故发生了。中断其实把所有种类的轮询都统一交给 cpu 处理了 (类似线程池这样的概念),这样就不用不停的分心去检查很多个 io 信号,造成 cpu 空置浪费,让本来多次的轮询合并变为一次轮询(而这个轮询本身是必须的,相当于没给钱有人免费给你装了一个电话)