使用无限循环时,x86中断处理程序被阻止

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用无限循环时,x86中断处理程序被阻止相关的知识,希望对你有一定的参考价值。

我正在尝试学习x86汇编和c语言。现在我已经完成了一个简单的计时器和键盘中断。第一个可以每隔几个刻度打印一行,第二个可以打印你在键盘上按下的内容。代码就像:

timer.c中:

print();
return;

keyboard.c中:

print();
return;

main.c中:

while(1);

这两个处理程序都在IDT中。因此,当发生中断时,IF标志被清除。 CPU将忽略其他中断。初始化了main函数中的所有配置后,我用无限循环完成了它,就像:while(1)。好的,这很好,我可以在屏幕上看到这两个处理程序的工作原理。 但是,当我想使它成为嵌套中断时,我做了以下事情:

timer.c中:

sti();      //set IF to 1 to enable interrupt 
print();
while(1); // wait here to test if the keyboard interrupt can  occur
return;

keyboard.c中:

print();
return;

main.c中:

while(1);

接下来发生的事情真的让我感兴趣:使用Bochs,我可以看到我的程序只是在timer.c中执行while循环。此时,我检查了标志,它显示IF标志已经设置,但是当我尝试输入某个东西来测试另一个interrut时,它失败了。似乎所有的中断都被阻止或忽略了。

问题是,这两个循环之间的区别是什么,一个只是在中断处理程序中,我也不知道如何解决这个问题。我在我的vmware和virtualbox中仔细检查了我的代码,但结果是一样的。

我知道这个问题肯定发生在我遇到它之前,但是,我找不到任何与此问题有关的内容。

答案

通过在中断处理程序中启用中断,您不会仅启用“其他”中断,而是启用所有中断。包括定时器中断。 (这太笼统了,payne在评论中指出,在x86上可能有不同的IRQ信号源,特别是PIC / APIC芯片可以配置为优先于其他...不会改变任何关于re的原理-inrant IRQ处理,但您可以微调哪些IRQ具有优先级,这可能意味着如果您的配置阻止它,最终根本不需要重新进入锁定)

你的代码看起来不像是可重入的处理程序,所以如果这就是你的处理程序代码的完成方式,那么你的计时器会按照配置继续启动新的计时器中断,并且因为你在那里有sti,它们会不断重新进入处理程序代码,填充缓慢的堆栈空间,直到它会溢出(或环绕,我不确定你是否处于实际或保护模式,以及如何设置堆栈)。只有最后一个当然是在运行,休息是休眠的(永远,如果最后一个没有返回)。

长时间的中断处理程序通常是错误的代码体系结构的标志(针对最短的+最快的中断处理程序,通常只是用数据填充一些队列,在中断处理程序之外以异步方式处理)。但是如果你确实需要一个,并且你不能禁用其他中断,你必须将处理程序编写为可重入的中断。

Re-entrant timer interrupt handler
{
    if (locked) return;   // already handling previous IRQ
        // which means the new IRQ is **IGNORED**! (price for slow handler)
    locked = true;        // lock to prevent further re-entry
    sti();                //set IF to 1 to enable interrupts

    // do the slow stuff here (but not infinite loop of course!)

    // "print();" is not an excellent idea either, for interrupt handler
    // while(1); // this would never ever end at all, so NO.

    // slow stuff finished, ready for next request

    locked = false;       // allow next timer IRQ to process the slow stuff again
    return;
}

附录:还有一句话,可能是“显而易见的”,但只是为了确保清楚。单CPU内核只能在同一时刻运行指令的单个“线程”,即它在main中执行无限循环,或者它正在执行其中一个中断处理程序。当新的IRQ信号进入CPU并启用中断时,当前CPU状态存储在堆栈中(无论是在主,或定时器或键盘,无关紧要),CPU将切换到所需的中断处理程序。因此,如果你发出的IRQ比你的处理程序完成并返回到前面的代码更快,你将慢慢地用存储的CPU状态阻塞堆栈内存,它将永远不会返回到底部的原始线程(main内的无限循环)。

另一答案

定时器中断(IRQ0)优先于键盘中断(IRQ1)。

因此,即使您在定时器中断处理程序中一般启用中断(使用sti指令),主中断控制器(PIC)也只会启用更高优先级的中断,直到您的定时器/ IRQ0服务程序完成。

有关其他解释,请参阅:https://groups.google.com/forum/#!topic/comp.lang.asm.x86/7coo0px-BU4

对,就是这样。中断控制器(在这种情况下是主要的中断控制器)向CPU发出定时器中断信号将不会允许任何相等或更低优先级的中断,直到CPU通过EOI指令告知定时器中断已完成(outportb(0x20, 0x20) for主中断控制器)。

您可以从技术上向PIC发出EOI指令,告诉它已完成IRQ0的处理,但您可能会为一些非常混乱的场景和竞争条件设置自己。

让中断处理程序只处理它们的特定中断并保存相关状态会更加清晰。然后,让您的主程序监视您需要的序列(例如计时器,然后是键盘)

以上是关于使用无限循环时,x86中断处理程序被阻止的主要内容,如果未能解决你的问题,请参考以下文章

如何修复这个 CouchDB 重写处理程序的无限循环?

无限循环 - 延迟 - 单独的线程

因发布 JavaScript 无限循环代码,13岁女学生被捕!

阻止 root.mainloop() 执行的无限循环 - Python

未配置时触发STM32 WWDG中断

在服务器应用程序中处理中断信号的最佳方法?