基于嵌入式操作系统VxWorks 的多任务并发程序设计(5)

基于嵌入式操作系统VxWorks 的多任务并发程序设计(5)
――中断与任务
作者:宋宝华 e-mail:
[email protected]
中断处理是整个运行系统中优先级最高的代码,可以抢占任何任务级代码运
行。中断机制是多任务环境运行的基础,是系统实时性的保证。几乎所有的实时
多任务操作系统都需要一个周期性系统时钟中断的支持,用以完成时间片调度和
延时处理。VxWorks 提供tickAnnounce(),由系统时钟中断调用,周期性地触发
内核。
为了快速响应中断,VxWorks 的中断服务程序(ISR)运行在特定的空间。
不同于一般的任务,中断服务程序没有任务上下文,不包含任务控制块,所有的
中断服务程序使用同一中断堆栈,它在系统启动时就已根据具体的配置参数进行
了分配和初始化。在ISR 中能使用的函数类型与在一般任务中能使用的有些不
同,主要体现在:
(1)ISR 中不能调用可能导致blocking的函数,例如:
(a)不能以semTake获取信号量,因如果该信号量不可利用,内核会试图让调
用者切换到blocking态;
(b)malloc和free可能导致blocking,因此也不能使用;
(c)应避免进行VxWorks I/O系统操作(除管道外);
(d)应避免在ISR 中进行浮点操作。
(2)在ISR 中应以logMsg打印消息,避免使用printf;
(3)理想的ISR 仅仅调用semGive 等函数,其它的事情交给semTake 这个
信号量的任务去做。一个ISR 通常作为通信或同步的发起者,它采用发送信号量
或向消息队列发送一个消息的方式触发相关任务至就绪态。ISR 几乎不能作为信
息的接收者,它不可以等待接收消息或信号量。
11.中断服务程序
VxWorks 中与中断相关的重要API函数或宏有:
(1)intConnect():中断连接,将中断向量与ISR 入口函数绑定
SYNOPSIS STATUS intConnect
(
VOIDFUNCPTR * vector,/* interrupt vector to attach to */
VOIDFUNCPTR routine, /* routine to be called */
int parameter /* parameter to be passed to routine */
);
intConnect 只是调用了下文将要介绍的intHandlerCreate()和intVecSet()函数。
(2)INUM_TO_IVEC(intNum):将中断号转化为中断向量的宏。与
INUM_TO_IVEC 对应的还有一个IVEC_TO_INUM(intVec),实现相反的过程。
INUM_TO_IVEC 和IVEC_TO_INUM 的具体定义与特定的BSP 有关,例如:
/* macros to convert interrupt vectors <-> interrupt numbers */
#define IVEC_TO_INUM(intVec) ((int) (intVec))
#define INUM_TO_IVEC(intNum) ((VOIDFUNCPTR *) (intNum))
结合1、2可知一般挂接一个中断服务程序的调用为:
intConnect(INUM_TO_IVEC(INTERRUPT_LEVEL),(VOIDFUNCPTR)interrup
tHandler,i);
例1:中断服务程序
/* includes */
#include "vxWorks.h"
#include "intLib.h"
#include "taskLib.h"
#include "sysLib.h"
#include "logLib.h"
/* function prototypes */
void interruptHandler(int);
void interruptCatcher(void);
/* globals */
#define INTERRUPT_NUM 2
#define INTERRUPT_LEVEL 65
#define ITER1 40
#define LONG_TIME 1000000
#define PRIORITY 100
#define ONE_SECOND 100
void interruptGenerator(void) /* task to generate the SIGINT signal */
{
int i, j, taskId, priority;
STATUS taskAlive;
if ((taskId = taskSpawn("interruptCatcher", PRIORITY, 0x100, 20000,
(FUNCPTR)
interruptCatcher, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) == ERROR)
logMsg("taskSpawn interruptCatcher failed/n", 0, 0, 0, 0, 0, 0);
for (i = 0; i < ITER1; i++)
{
taskDelay(ONE_SECOND); /* suspend interruptGenerator for one second */
/* check to see if interruptCatcher task is alive! */
if ((taskAlive = taskIdVerify(taskId)) == OK)
{
logMsg("++++++++++++++++++++++++++Interrupt generated/n", 0, 0,
0, 0, 0,
0);
/* generate hardware interrupt 2 */
if ((sysBusIntGen(INTERRUPT_NUM, INTERRUPT_LEVEL)) ==
ERROR)
logMsg("Interrupt not generated/n", 0, 0, 0, 0, 0, 0);
}
else
/* interruptCatcher is dead */
break;
}
logMsg("/n***************interruptGenerator
Exited***************/n/n/n/n", 0,
0, 0, 0, 0, 0);
}
void interruptCatcher(void) /* task to handle the interrupt */
{
int i, j;
STATUS connected;
/* connect the interrupt vector, INTERRUPT_LEVEL, to a specific interrupt
handler routine ,interruptHandler, and pass an argument, i */
if ((connected = intConnect(INUM_TO_IVEC(INTERRUPT_LEVEL),
(VOIDFUNCPTR)
interruptHandler, i)) == ERROR)
logMsg("intConnect failed/n", 0, 0, 0, 0, 0, 0);
for (i = 0; i < ITER1; i++)
{
for (j = 0; j < LONG_TIME; j++)
;
logMsg("Normal processing in interruptCatcher/n", 0, 0, 0, 0, 0, 0);
}
logMsg("/n+++++++++++++++interruptCatcher Exited+++++++++++++++/n",
0, 0, 0,
0, 0, 0);
}
void interruptHandler(int arg) /* signal handler code */
{
int i;
logMsg("-------------------------------interrupt caught/n", 0, 0, 0, 0, 0, 0);
for (i = 0; i < 5; i++)
logMsg("interrupt processing/n", 0, 0, 0, 0, 0, 0);
}
程序中的sysBusIntGen()调用将产生一个bus 中断,这个函数与特定的BSP
密切相关,其原型为:
STATUS sysBusIntGen
(
int intLevel, /* bus interrupt level to generate */
int vector /* interrupt vector to generate (0-255) */
);
为了在同一中断源的几种中断服务程序中进行切换,我们应使用如下方式:
vector = INUM_TO_IVEC(some_int_vec_num);
oldfunc = intVecGet (vector);
newfunc = intHandlerCreate (routine, parameter);
intVecSet (vector, newfunc);
...
intVecSet (vector, oldfunc); /* use original routine */
...
intVecSet (vector, newfunc); /* reconnect new routine */
其中,intHandlerCreate 函数的原型为:
FUNCPTR intHandlerCreate
(
FUNCPTR routine, /* routine to be called */
int parameter /* parameter to be passed to routine */
);
它被用于创建一个中断服务程序,在此之后,通过intVecSet()函数我们就可
以将intHandlerCreate()创建的结果与中断向量绑定,intVecSet()函数的原型为:
void intVecSet
(
FUNCPTR * vector, /* vector offset */
FUNCPTR function /* address to place in vector */
);
12.中断控制
12.1 中断执行过程
硬件中断发生时,代码运行的上下文会发生切换,在进入中断处理前,需要
保存当前运行的上下文。对于一些无RTOS 的单片机系统,这些工作由硬件和编
译器共同完成,向量表在编译完成后就填充完成,再写入存储器中,系统运行时
不能修改向量表来重新绑定中断入口函数。在VxWorks 系统中,除了需要保存
通常的寄存器环境外,还需要完成栈切换等;另外还要求中断入口运行时绑定、
平台移植性、中断嵌套等,所以VxWorks 本身也参与中断封装的管理。VxWorks
进行中断封装的伪代码如下:
* 00 e8 kk kk kk kk call _intEnt * 通知内核
* 05 50 pushl %eax * 保存寄存器
* 06 52 pushl %edx
* 07 51 pushl %ecx
* 08 68 pp pp pp pp pushl $_parameterBoi * push BOI param
* 13 e8 rr rr rr rr call _routineBoi * call BOI routine
* 18 68 pp pp pp pp pushl $_parameter * 传中断入口参数
* 23 e8 rr rr rr rr call _routine * 调用中断处理C 函数
* 28 68 pp pp pp pp pushl $_parameterEoi * push EOI param
* 33 e8 rr rr rr rr call _routineEoi * call EOI routine
* 38 83 c4 0c addl ?, %esp * pop param
* 41 59 popl %ecx * 恢复寄存器
* 42 5a popl %edx
* 43 58 popl %eax
* 44 e9 kk kk kk kk jmp _intExit * 通过内核退出
12.2中断使能/禁止
VxWorks 提供两个重要API:
(1)intLock():使中断禁止
(2)intUnlock():开中断
可以用intLock/intUnlock 提供最高级别的互斥机制以保护临界区域不被打
断,例如:
oldlevel = intLock();
/* 写XXX寄存器 */
XXX_REG_WRITE(pChan, XXX_UBRDIV, XXX_CNT0_115200 | XXX_C
NT1_VAL);
intUnlock(oldlevel);
用intLock()禁止中断后,当前执行的任务将一直继续,中断处理和任务调度
得不到执行,直到该任务主动调用intUnLock 解锁中断为止。对于intLock 和
unLock 的使用,我们要注意如下几点:
(1)不要在中断禁止期间调用VxWorks 系统函数,否则有可能意外使能中
断,违反临界代码的设计意图。另外,intLock 也不能屏蔽调度,如果在中断禁
止代码区使用系统调用,就可能出现任务调度,其他任务的运行可能会解锁中断;
(2)中断禁止对系统的实时性有很大的影响,在解决执行代码和中断处理
互斥问题才可使用,并且应使中断禁止时间尽可能的短。对于任务间的互斥问题,
可以使用taskLock()和taskUnLock()来解决;
(3)有些CPU中断是分级,我们可以用intLockLevelSet()和intLockLevelGet()
来操作中断闭锁的级别。缺省情况下,taskLock 禁止所有等级的中断。
至此,我们可以对“互斥”问题进行一个系统的总结,主要有如下几种方法:
(1)intLock禁止中断:解决任务和ISR 之间的互斥问题;
int lock = intLock();
//. . critical region that cannot be interrupted
intUnlock(lock);
(2)taskLock 禁止优先级抢占调度:当当前任务正在运行时,除了中断服
务程序外,高优先级的任务也不允许抢占CPU;
taskLock();
//. . critical region that cannot be interrupted .
taskUnlock();
(3)二进制信号量或互斥信号量。
semTake (semMutex, WAIT_FOREVER);
//. . critical region, only accessible by a single task at a time .
semGive (semMutex);
总的来说,在实时系统中采取“禁止中断”的方法会影响系统对外部中断及
时响应和处理的能力;而“禁止优先级抢占调度”方法阻止了高优先级的任务抢
先运行,在实时系统中也是不适合的。因此,信号量无疑是解决互斥问题的最好
方法。

你可能感兴趣的:(vector,任务调度,processing,任务,Signal,嵌入式操作系统)