对于一个嵌入式多任务、多线程操作系统,所启动的应用进程至少拥有一个线程或多个线程,线程在进程中执行代码。一个进程能够“同时”运行多个线程,“同时”加上引号,因为实际上,在单处理CPU平台上,任何时刻,只有一个线程在执行。操作系统通过任务调度算法快速切换线程来模拟多线程并行,交替地停止一个线程,然后切换到另外一个上运行。支持任务优先级,高优先级线程比低优先级线程更先执行,也就是说低优先线程必须等到高优先级线程被阻塞挂起后才可能被调度。对于优先级别相等的线程使用轮换算法来调度。
无论是WinCE还是Linux操作系统,应用线程的运行总是涉及到两个基本的参数:一个是系统分配给线程的时间片,一个是系统调度的时间间隔。Linux和WinCE下这两个参数有所不同,如下表所示:
|
WinCE |
嵌入式Linux |
线程的运行时间片 | 100ms |
10ms |
系统调度间隔 | 1ms |
10ms |
这里需要注意的是,线程不一定需要将时间片完全用完,事实上,在嵌入式系统中,线程的运行处理时间通常都远小于所分的时间片,这时线程应当调用相关系统函数将自己挂起,系统将立即进行线程重调度。这时的重调度实际是加快了各个线程的轮片,提高了多线程并行运行的程度,客观上保证了嵌入式设备的实时响应能力。
如果线程运行处理的时间超过了系统分配的时间片,在到达时间片后系统将强制挂起该线程,进行任务调度,以保证其他线程的运行。所以在多任务的程序设计中,特别忌讳线程中出现长时间查询代码,如:
while( bFlag == FALSE )
{
// Polling Flag…
}
这种代码总是占完了时间片,大大地浪费CPU资源,使得整个进程的线程调度变得很慢,比如对于WinCE来说,就会出现100ms才调度一次,如果有10个这样的线程,意味着每个线程需要1s才能轮询一次,在嵌入式应用中,就很容易出现数据丢失等错误,从而无法实现正常的功能。对于Linux也是存在同样的问题。
在实际应用中,常用的挂起线程的方法是调用延时函数。对于WinCE,调用Sleep( ms )函数;对Linux,可以调用select( )函数来挂起当前线程。由于系统的调度间隔不一样,对相同的挂起时间,系统会有不同的处理。比如,设置挂起时间为2ms,在WinCE中,将在挂起2ms之后的1ms后,被系统重新调度;在Linux中,由于调度间隔大于挂起的时间,所以系统将在线程挂起10ms之后才被重新调度,也就是说在Linux下,无论程序中设置的挂起时间2ms还是9ms,该线程实际被挂起的时间总是10ms。
线程的调度策略分为3个:SCHED_OTHER,SCHED_FIFO,SCHED_RR。
SCHED_OTHER是非实时分时调度策略,线程优先级为0;
试验结果(linux2.6 Montavista 5.0):每个线程都不能强占其它线程,但是线程都受到时间片的限制,并不是线程不主动退出(包括被阻塞),就会一直占用。
但是在sun公司的 《多线程编程手册》中,其说这种情况 线程会一直占用。
SCHED_FIFO是实时先进先出调度策略,即一当占用CPU,除非自己阻塞或结束或有更高优先级线程,否则会一直运行,线程优先级为1-99;
线程会按不同的优先级来分为不同的队列,同优先级的线程是按FIFO来调度的。
SCHED_RR是实时分时调度策略,其不会一直占用CPU,运行一个时间片后会让出CPU给自己同优先级的线程;其实SCHED_RR与SCHED_FIFO基本是相似的,只是前者会受到时间片的限制,相同优先级的线程,用时间片来调度。而FIFO的话,正在运行的线程,是不会被其他同优先级线程强占的,除非自己主动退出或被阻塞。所以在采用FIFO策略时,千万别使用一直占用的线程(不主动退出,也没有挂起的条件),否则其他同优先级线程永远不会运行了。这种情况最好使用RR策略。
指出:SCHED_OTHER是不支持优先级使用的,而SCHED_FIFO和SCHED_RR支持优先级的使用,他们分别为1和99,数值越大优先级越高。实时调度策略会抢占非实时调度策略,即只要有SCHED_FIFO或SCHED_RR这两个实时调度策略的线程,像SCHED_OTHER的非实时调度策略的线程就不会得到运行,除非所有的实时调度策略的线程阻塞或结束。
Linux线程优先级设置
首先,可以通过以下两个函数来获得线程可以设置的最高和最低优先级,函数中的策略即上述三种策略的宏定义:
int sched_get_priority_max(int policy); int sched_get_priority_min(int policy); |
SCHED_OTHER是不支持优先级使用的,而SCHED_FIFO和SCHED_RR支持优先级的使用,他们分别为1和99,数值越大优先级越高。
设置和获取优先级通过以下两个函数:
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param); |
例如以下代码创建了一个优先级为10的线程:
struct sched_param { int __sched_priority; //所要设定的线程优先级 }; |
例:创建优先级为10的线程
pthread_attr_t attr; struct sched_param param; pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_RR); param.sched_priority = 10; pthread_attr_setschedparam(&attr, ¶m); pthread_create(xxx , &attr , xxx , xxx); pthread_attr_destroy(&attr); |