实时调度机制(Real-Time Scheduler)是Windows Embedded Compact区别于其他所有Windows系统的最显著特性,同时也是开发嵌入式系统时需要着重考虑的部分。在工程领域对“实时”的理解,我非常欣赏下面的定义:“一个实时系统必须要满足明确的(受限的)响应时间约束或风险的严重后果,包括失效状态”- 出自于Phillip A. Laplante 《Real-Time System Design and Analysis》所以,一个实时系统中执行一个代码必须在规定的时间约束内有明确的结果,否则就可能会导致系统失效,实时并不一定意味着快速处理能力。而Windows Embedded Compact正符合上述对于实时系统的定义,因此为了进一步理解,我们首先来了解它的任务调度机制,WinCE的任务调度内核每隔1ms查询一次现有任务并依据下面两个原则来决定处理哪个任务:
a). 具有更高优先级的任务先被执行
b). 同样优先级的任务按100ms时间间隔(或Task Quantum定义的时间间隔)循环轮流执行
对于第一条规则,WinCE提供256个优先级等级(0-255),数字越小优先级越高,因此0级为最高优先级,关于优先级的应用本文不做重点描述,请参考下面文章:
Real-TimePriority System Levels (Windows Embedded CE 6.0)
对于第二条规则,当多个待处理任务拥有同样的优先级,将按照100ms时间间隔(可以根据Thread Quantum自定义)循环轮流执行。例如有N个同样优先级的任务,当第一个运行了上述定义时间片(WinCE称其为Quantum,如100ms),系统内核就会将其中断然后执行第二个任务,依此类推,直到N个任务都执行过一个Quantum时间后再重新回到第一个任务执行,如此循环。任一个线程最多只能运行一个Quantum的时间,除非另一个更高优先级的任务需要占用CPU,则按照第一条规则,这个线程会被更高优先级任务占用。
下面我们通过两个例子来验证上述两个规则,硬件平台使用ToradexColibri VF61(NXP/Freescale Vybrid Cortex-A5 )计算机模块搭配Iris 载板,软件使用Toradex提供的对应此平台的工业级的WinCE6OS和GPIO库。
a). 软硬件平台搭建请参考开发指南,如下图所示
b). 验证原理为在系统中启动两个任务,第一个任务驱动硬件某个GPIO输出为低电平,而另一个则将同一个GPIO输出为高电平;再将这两个任务的优先级分别设置为相同和不同时候,通过观测GPIO连接的示波器输出来判定上述的两个调度规则。关键代码如下:创建两个线程入口函数ThreadON和ThreadOFF,分别用来将选定的GPIO输出为高电平和低电平,而从函数内部代码可见会持续输出高电平或者低电平,因此我们通过示波器观察GPIO管脚的输出即可得出目前是哪个函数在运行。
--------------------------------------------------------------------------------------------------------------
#include
#include"gpio.h"
// ===define constant pins / gpios ===
// SODIMMpin 101
uIo io1 =COLIBRI_PIN(101);
HANDLE hGpio;
HANDLE hThreadON,hThreadOFF;
//defineThreadON
DWORD WINAPI ThreadON( LPVOID lpParam ){
//Set ThreadPriority
CeSetThreadPriority(GetCurrentThread(), 100);
Sleep(5); //Allowthe orther Thread to configure it's PRIO
//FNFINITELOCKING LOOP
while(1){
//Set GPIOlogic high
Gpio_SetLevel(hGpio, io1, ioHigh);
}
return 0;
}
//defineThreadOFF
DWORD WINAPI ThreadOFF( LPVOID lpParam ){
//Set ThreadPriority
CeSetThreadPriority(GetCurrentThread(),100);
//FNFINITELOCKING LOOP
while(1){
//Set GPIOlogic low
Gpio_SetLevel(hGpio, io1, ioLow);
}
return 0;
}
//=============================================================================
//Application Entry Point
//
// Thesimple error handling using ASSERT statements is only effective when
// theapplication is run as a debug version.
//=============================================================================
int wmain(int argc, _TCHAR* argv[])
{
BOOL success;
// === InitializeGPIO library. ===
// We don't useregistry-based configuration, thus wecan
// pass NULL goGpio_Init()
hGpio = Gpio_Init(NULL);
ASSERT(hGpio != 0);
success = Gpio_Open(hGpio);
ASSERT (success);
// Configure thepin to act as GPIO (as opposed to an Alternate function)
// Set it toOutput, High
Gpio_ConfigureAsGpio(hGpio, io1);
Gpio_SetDir (hGpio, io1, ioOutput);
Gpio_SetLevel (hGpio, io1, ioHigh);
CeSetThreadPriority(GetCurrentThread(),99);
//Create twoconcorrent Threads, one set GPIO to High and other to Low
hThreadON = CreateThread(NULL, 0, ThreadON,NULL, 0, NULL);
hThreadOFF = CreateThread(0, 0, ThreadOFF,NULL, 0, NULL);
//Time to finish the Program
Sleep(3000);
return(TRUE);
}
--------------------------------------------------------------------------------------------------------------
c). 首先我们来测试第二条规则,将两个任务的优先级设置为相同值(如上面代码为100),运行程序后示波器图形如下,可以看到每隔100ms GPIO输出循环交替变化,完全符合我们第二条规则。
d). 然后我们再来测试第一条规则,如下修改一个任务(ThreadON)的代码,将其优先级提高到99,另外在循环中增加一个5ms的暂停时间。
--------------------------------------------------------------------------------------------------------------
DWORD WINAPI ThreadON( LPVOID lpParam ){
//Set ThreadPriority
CeSetThreadPriority(GetCurrentThread(), 99);
Sleep(5); //Allowthe orther Thread to configure it's PRIO
//FNFINITELOCKING LOOP
while(1){
//Set GPIOlogic high
Gpio_SetLevel(hGpio, io1, ioHigh);
Sleep(5);
}
return 0;
}
--------------------------------------------------------------------------------------------------------------
e). 运行修改后的程序,示波器输出如下结果,每隔7ms左右有一个拉高的脉冲,表示每次当高优先级的任务(ThreadON)从5ms 暂停时间恢复后,都会中断低优先级任务的执行,这样完全符合第一条规则的描述。
当然,以上所有测试都是基于单核心系统,从Windows Embedded Compact 7 开始,WinCE内核提供了对多核心处理器的支持,同时也有一个新的属性“affinity”来定义哪一个核心来执行哪一个线程,所以如果在多核系统和WEC7上面运行上述例子,同时并未限定线程在同一个核心上面执行,则结果会不同因为两个线程会同时在不同核心上面运行。当然,其实正常应用情况下我们是不建议设置“affinity”参数的,因为这样就无法使得内核调度来自动安排线程在最先空闲的核心上面运行,达不到降低延迟提高系统性能的要求了。
实时系统目前在包括工业自动化,机器人和医疗领域等嵌入式设备上面有广泛的需求,因此了解WinCE的实时调度工作机制以及如何使用线程可以让我们的应用程序实时稳定的执行,让我们更高效可靠的利用WinCE搭建我们的实时系统!