【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
在我个人看来,中断是cpu最重要的特色。从某种意义上来说,没有中断就没有嵌入式操作系统。一旦你明白了中断的真正含义,你对操作系统的了解就算真正入门了。什么是中断呢?我们可以看看单片机下面是怎么做的。
#include <REG51.h> sbit LED = P1 ^ 6; unsigned int led_enable = 0; void Delay(unsigned int a) { unsigned int i; while(a) { a --; for(i = 0; i < 1200; i++); } } void led_switch(void) interrupt 0 using 1 { if(0 == led_enable) { led_enable = 1; } else { led_enable = 0; } EX0 = 1; } void main(void) { EA = 1; EX0 = 1; while(1){ if(led_enable) { LED = 0; Delay(100); LED = 1; Delay(100); } } }
上面的代码是一段真实的51单片机代码。它完成的功能很简单,就是对led灯进行点亮处理。怎么解释呢?在单片机上电后,我们发现一开始led二极管没有发生闪烁。在我们单击按键之后,led开始出现间隙性闪烁的现象,之后再一次单击按键,又可以发现led的闪烁现象消失了。为什么会出现这种现象?主要是因为我们单击按键的时候,在单片机的引脚处产生了中断。查看到中断的单片机此时就会跳转到中断向量表里面查找中断处理函数。这里的按键中断处理函数就是led_switch。处理完led_switch之后,单片机又会回到原来的main函数继续执行,所以整个中断的过程就像没有发生过一样。因为在led_switch中我们对led_enable进行了处理,所以就出现了我们在前面说过的各种现象。
说到这,也许有的朋友会说,cpu的这种中断属性怎么才能在pc上面仿真出来呢?其实很简单。linux系统本身就有一个优秀的特性,那就是信号。只要我们设定相应的信号和处理函数,那么linux系统就会在系统调度返回之前调用相应的信号函数来处理。整个信号处理的过程和中断是一模一样的。因为在处理中断的时候,我们需要对cpu的现场进行保存和恢复处理,而信号的处理也是一样。在信号处理前,系统肯定是处于内核态,那么linux系统肯定已经为我们做好了现场的保护工作,处理完信号之后,系统本身又会恢复到原来的用户态,继续执行下面的代码。所以linux自身也会默认对原来的场景进行恢复处理,就好象中断返回一样。
#include <stdio.h> #include <time.h> #include <sys/time.h> #include <stdlib.h> #include <signal.h> static int count = 0; static struct itimerval oldtv; void set_timer() { struct itimerval itv; itv.it_interval.tv_sec = 1; itv.it_interval.tv_usec = 0; itv.it_value.tv_sec = 1; itv.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itv, &oldtv); } void signal_handler(int m) { count ++; printf("%d\n", count); } int main() { signal(SIGALRM, signal_handler); set_timer(); while(count < 10000); exit(0); return 1; }
大家可以把这样一段代码在Linux编译一下,然后使用gdb调试即可。查看整个signal_handler在被断点断住的时候,本身线程是不是只有一个?函数堆栈是不是在一个线程里面。如果不出意外,整个信号的处理过程应该是这样的,
(gdb) bt #0 signal_handler(m=14) at code.c: 23 #1 <signal handler caller> #2 main() at code.c:32
到了这里,相应大家应该还有一个疑问,既然可以利用linux的signal对cpu的中断进行仿真,那么能不能利用windows的signal对中断进行仿真呢。因为Windows下面的没有SIGALRM这个信号,所以我们可以重新编写一段代码,然后利用visual studio 6.0进行编译,看看对应的情况。
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <tchar.h> #include <Windows.h> void SignalHandler(int signal) { printf("Application over..\n"); } int main() { typedef void (*SignalHandlerPointer)(int); SignalHandlerPointer previousHandler; previousHandler = signal(SIGINT, SignalHandler); while(1) { Sleep(0); } exit(1); }
下面,我们首先编译这一段代码。接着在程序run之后,我们可以在SignalHandler之处设置一个断点。一切就绪完毕,再按下ctrl+c之后,系统就会在SignalHandler之处断住。此时单击【Debug】-> 【Threads】,就可以看到这个情况。
相信看到这里,大家应该看明白了。其实在Windows下面,信号是专门有一个线程来完成的,和原来的main函数不是同一个线程。既然线程都不是一样的,而中断本身是必须在一个thread中完成的。我们怎么能利用windows来仿真cpu的中断处理流程呢。