原文链接地址http://newkedison.blog.163.com/blog/static/369822582007917119442/
什么是中断
---------------------------------------------
中断是CPU处理外部突发事件的一个重要技术。它能使CPU在运行过程中对外部事件发出的中断请求及时地进行处理,处理完成后又立即返回断点,继续进行CPU原来的工作。引起中断的原因或者说发出中断请求的来源叫做中断源。 根据中断源的不同,可以把中断分为硬件中断和软件中断两大类,而硬件中断又可以分为外部中断和内部中断两类。
CPU为了处理并发的中断请求,规定了中断的优先权。中断优先权由高到低的顺序是:
- 除法错、溢出中断、软件中断
- 不可屏蔽中断
- 可屏蔽中断
- 单步中断。
---------------------------------------------
我个人理解就是象函数调用一样,CPU运行运行着,突然调用了一个函数,这个函数处理完之后,由返回下面的程序继续执行,至于为什么会突然调用了一个程序,那就是中断的原因了。而调用的这个函数,就就叫中断函数。
然后来看几个C语言控制中断函数的函数
void interrupt (*getvect(int interruptno))();
这个函数的结构我可是研究了半天才研究出来的
1、这是个函数,名字叫getvect,输入参数是interruptno,输出参数是一个中断函数指针
2、interruptno是个中断地址(比如时钟中断地址是0x1c)
3、每个中断都会调用一个中断函数(不然叫什么中断)
4、这个函数的作用就是把指定的中断调用的函数的指针作为返回值返回,这样就可以保存这个函数,以便以后恢复。
void setvect(int interruptno, void interrupt (*isr) (...);
理解了上面的那个函数,这个就简单了
1、函数名setvect,输入参数,一个中断地址,一个中断函数指针,输出参数,无
2、这个函数的功能是把指定中断的中断函数设置为第二个输入参数指定的函数,也就是更改这个中断调用的中断函数。
3、函数的用处有两个,一个是把原来默认的中断函数改成我们自己写的函数,另一个是恢复默认的中断函数(为什么能恢复呢?因为每次更改中断函数的时候,都必须先用那个getvect把原来的中断函数保存下来,只要把保存下来的函数恢复回去就行)
搞定了这两个函数,中断函数就不再神秘了,下面看个实例
目标是调用时间中断,每秒输出一个数,10秒后结束
/*
下面这段英文不大明白是什么意思
This is an interrupt service routine. You can NOT compile this
program with Test Stack Overflow turned on and get an executable
file which will operate correctly.
*/
/*
这个程序每隔1秒钟输出一个整数,10秒钟后结束程序。
按escape键提前退出程序 。
*/
#include <stdio.h>
#include <dos.h>
#include <conio.h>
/* ESC的按键码 */
#define VK_ESC 0x11b
#define TIMER 0x1c // 时钟中断的中断号,也就是上面说的中断地址
/*
中断处理函数在C和C++中的表示略有不同。
如果定义了_cplusplus则表示在C++环境下,否则是在C环境下。
可能是因为C++的函数必须加三个点,所以要这么干,至于为什么要加三个点就不大清楚了
*/
#ifdef __cplusplus
#define __CPPARGS ...
#else
#define __CPPARGS
#endif
int TimerCounter=0; // 计时变量,每秒钟增加18。解释一下,其实是平均每秒发生18.2次时钟中断,所以累计到18次也就大概是1秒了
// 指向原来时钟中断处理过程入口的中断处理函数指针(句柄)
void interrupt ( *oldhandler)(__CPPARGS);
/*
1、这是个变量,名字叫oldhandler,类型是一个指向中断函数的指针
2、只要知道这是个函数指针,就好理解了,后面括号里面的是参数,如果是c,就留空,如果是C++,就写三个点,目前还不知道原因。
3、这个变量的作用就是用来保存原来的中断函数地址,以便后来的恢复
*/
// 新的时钟中断处理函数
void interrupt newhandler(__CPPARGS)
{
// increase the global counter
TimerCounter++; //累计看多少次进入了这个中断函数
/*
call the old routine 调用原来的中断函数,因为原来的中断函数也是要完成一些事情的,
如果因为我们改变了中断函数的位置而导致原来的中断函数无法执行,那系统可能就出问题了,
就这个程序来说,就是会导致计时错误。
*/
oldhandler();
}
// 设置新的时钟中断处理过程
void SetTimer(void interrupt (*IntProc)(__CPPARGS)) //输入参数就是一个中断函数的指针
{
oldhandler=getvect(TIMER); //获得原来时钟中断所调用的中断函数,赋值给oldhandler,这样原来的中断函数地址就保存下来了
disable(); // 设置新的时钟中断处理过程时,禁止所有中断
setvect(TIMER,IntProc); //设置时间中断的中断函数为我们自己设置的函数
enable(); // 开启中断
}
// 恢复原有的时钟中断处理过程
void KillTimer()
{
disable();
/*
把保存在oldhandler里面的原来中断函数的地址恢复,这个步骤绝对不能少,不然等我们程序运行完了,
上面我们自己定义的函数就不见了,那时钟中断就找不到地方可以执行中断函数了,会出什么问题我不敢试,但是肯定会出问题
*/
setvect(TIMER,oldhandler);
enable();
}
void main(void)
{
int key,time=0;
SetTimer(newhandler); // 修改时钟中断
for (;;)
{
if (bioskey(1)) // 这个表示按了键盘上的某个键,因为与本文无关,具体用法请自行百度之
{
key=bioskey(0); // 取得按键码
if (key==VK_ESC) // 按escape键提前退出程序
{
printf("User cancel!\n");
break;
}
}
if (TimerCounter>18) // 1秒钟处理一次
{
// 恢复计时变量
TimerCounter=0;
time++;
printf("%d\n",time);
if (time==10) // 10秒钟后结束程序
{
printf("Program terminated normally!\n";
break;
}
}
}
KillTimer(); // 恢复时钟中断
}
程序结束。
通过这个实例,可以很清楚的表示使用中断函数所必须的几个步骤
1、用一个变量保存原来的中断函数
2、设置一个新的中断函数,这个中断函数要调用默认的中断函数
3、更改中断的中断函数,改为我们自己写的中断函数
4、程序结束后,记得恢复中断函数为默认的中断函数
我记得以前还看过说,我们自己定义的中断函数不要有太多太复杂的操作,我觉得也是应该这样的,不然出了问题就麻烦大了。