天天看點

基于嵌入式作業系統VxWorks的多任務并發程式設計(5)――中斷與任務

<b>基于嵌入式作業系統</b><b>VxWorks</b><b>的多任務并發程式設計(</b><b>5</b><b>)</b><b></b>

<b>――中斷與任務</b><b></b>

<b>作者:</b>宋寶華<b>  e-mail:</b>[email][email protected][/email]  出處:軟體報<b></b>

中斷處理是整個運作系統中優先級最高的代碼,可以搶占任何任務級代碼運作。中斷機制是多任務環境運作的基礎,是系統實時性的保證。幾乎所有的實時多任務作業系統都需要一個周期性系統時鐘中斷的支援,用以完成時間片排程和延時處理。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幾乎不能作為資訊的接收者,它不可以等待接收消息或信号量。

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 &lt;-&gt; 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)interruptHandler,i);

/* 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 &lt; 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 (j = 0; j &lt; 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 &lt; 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 */

硬體中斷發生時,代碼運作的上下文會發生切換,在進入中斷處理前,需要儲存目前運作的上下文。對于一些無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 * 通過核心退出

VxWorks提供兩個重要API:

(1)intLock():使中斷禁止

(2)intUnlock():開中斷

可以用intLock/intUnlock提供最進階别的互斥機制以保護臨界區域不被打斷,例如:

oldlevel = intLock();

/* 寫XXX寄存器 */

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);

總的來說,在實時系統中采取“禁止中斷”的方法會影響系統對外部中斷及時響應和處理的能力;而“禁止優先級搶占排程”方法阻止了高優先級的任務搶先運作,在實時系統中也是不适合的。是以,信号量無疑是解決互斥問題的最好方法。

 本文轉自 21cnbao 51CTO部落格,原文連結:http://blog.51cto.com/21cnbao/120322,如需轉載請自行聯系原作者

繼續閱讀