实验4 优先级反转
1 实验目的
掌握在基于优先级的可抢占嵌入式实时操作系统的应用中,出现优先级反转现象的原理。
2 原理及程序结构
2.1 实验设计
2.1.1 优先级反转原理
在本实验中,要体现嵌入式实时内核的优先级抢占调度的策略,并显现由于共享资源的互斥访问而出现的优先级反转现象。
优先级反转发生在有多个任务需要使用共享资源的情况下,可能会出现高优先级任务被低优先级任务阻塞,并等待低优先级任务执行的现象。高优先级任务需要等待低优先级任务释放资源,而低优先级任务又正在等待中等优先级任务,这种现象就被称为优先级反转。两个任务都试图访问共享资源是出现优先级反转最通常的情况。为了保证一致性,这种访问应该是顺序进行的。如果高优先级任务首先访问共享资源,则会保持共享资源访问的合适的任务优先级顺序;但如果是低优先级任务首先获得共享资源的访 问,然后高优先级任务请求对共享资源的访问,则高优先级任务被阻塞,直到低优先级任务完成对共享资源的访问。
2.1.2 设计要点
1)设计了3个应用任务TA0~TA2,其优先级逐渐降低,任务TA0的优先级最高。
2)除任务TA1外,其它应用任务都要使用同一种资源,该资源必须被互斥使用。为此,创建一个二值信号量mutex来模拟该资源。虽然μC/OS-Ⅱ在创建信号量时可以选择采用防止优先级反转的策略,但在本实验中我们不使用这种策略。
3)应用任务的执行情况如图2-1所示:
任务2窑链
任务1 任务0
图2-1
注意:图中的栅格并不代表严格的时间刻度,而仅仅表现各个任务启动和执行的相对先后关系。
2.1.3 系统的运行流程
1) 系统初始化,之后进入main 函数;
培训台2) 在main 函数中,首先创建一个二值的信号量mutex ;
3) 在main 函数中创建TaskStart 任务,由TaskStart 任务创建所有的应用任务(Task0、
Task1、Task2)。优先级较高的任务Task0、Task1先延时若干个时钟节拍,以便低优先级任务Task2运行。
4) t1时刻,任务Task2运行并首先申请到信号量mutex ;
5) t2时刻,任务Task1延时到期,任务Task1的优先级高于任务Task2的优先级,因
此任务Task1立刻抢占Task2执行,任务Task2由执行态转为就绪态;
6) t3时刻,任务Task0延时到期,任务Task0的优先级高于任务Task1的优先级,所
以任务Task0立刻抢占执行,任务Task1由执行态转为就绪态,任务Task0申请二值信号量mutex 被阻赛;
碳化硅石墨坩埚7) t4时刻,任务Task1由就绪态转回为执行态;此时Task0在等待Task2保持的mutex ,
而Task2又因为优先级低于Task1被阻塞。如果Task1一直执行而Task2没有机会被调度的话,那么Task2将一直等到Task1执行完后才能执行,而Task0更要等到Task2释放它所占有的信号量资源后才能执行,这样就出现了优先级高的Task0任务等待优先级低的Task1任务的现象;
8)t5时刻,任务Task1挂起自己,而Task0又因为申请二值信号量mutex而处于阻塞状态,所以任务Task2由就绪态转为执行态,任务Task2释放信号量mutex;
9)t6时刻,Task0获得信号量并立刻抢占执行,任务Task2由执行态转为就绪态;
10)t7时刻,任务Task0将自己延时一段时间,而Task1仍然处于挂起状态,Task2是当前最高优先级的就绪任务,它又转为执行状态,任务Task2因申请二值信号量
mutex而阻塞;
11)t8时刻,任务Task1延时到期转为执行态,任务Task1又因等待一个事件而阻塞;
12)t9时刻,任务Task0延时到,释放二值信号量mutex,mutex被Task2得到后,内核自动切换任务;
13)t10时刻,在就绪队列中,Task0优先级最高,Task0执行,又因为任务Task0等待一事件而阻塞;
14)t11时刻,任务Task1延时到期,立刻抢占执行,又由于任务Task1等待一事件而阻塞;
15)t12时刻,任务Task2执行,保持信号量mutex;以后系统再次出现优先级反转现象;
16)系统如此周而复始地运行……
2.2 操作系统配置
修改uC_OS-II/OS_CFG.h:
#define OS_MAX_EVENTS 10 /*最多可以有10个事件*/
#define OS_MAX_FLAGS 5 /*最多可以有5个事件标志*/
#define OS_MAX_MEM_PART 5 /*最多可以划分5个内存块*/
#define OS_MAX_QS 2 /*最多可以使用2个队列*/
#define OS_MAX_TASKS 9 /*最多可以创建9个任务*/
#define OS_LOWEST_PRIO 24 /*任务优先级不可以大于24*/
#define OS_TASK_IDLE_STK_SIZE 1024 /*空闲任务堆栈大小*/
#define OS_TASK_STA T_EN 1 /*是否允许使用统计任务*/
#define OS_TASK_STA T_STK_SIZE 1024 /*统计任务堆栈大小*/
#define OS_FLAG_EN 1 /*是否允许使用事件标志功能*/
CCSVC#define OS_FLAG_WAIT_CLR_EN 1 /*是否允许等待清除事件标志*/
#define OS_FLAG_ACCEPT_EN 1 /*是否允许使用OSFlagAccept()*/
#define OS_FLAG_DEL_EN 1 /*是否允许使用OSFlagDel()*/
#define OS_FLAG_QUERY_EN 1 /*是否允许使用OSFlagQuery()*/
#define OS_MBOX_EN 0 /*是否允许使用邮箱功能*/
#define OS_MEM_EN 1 /*是否允许使用内存管理的功能*/
#define OS_MEM_QUERY_EN 1 /*是否允许使用OSMemQuery()*/
#define OS_MUTEX_EN 1 /*是否允许使用互斥信号量的功能*/ #define OS_MUTEX_ACCEPT_EN 1 /*是否允许使用OSMutexAccept()*/
#define OS_MUTEX_DEL_EN 1 /*是否允许使用OSMutexDel()*/
mhhpa
#define OS_MUTEX_QUERY_EN 1 /*是否允许使用OSMutexQuery()*/
#define OS_Q_EN 0 /*是否允许使用队列功能*/
#define OS_SEM_EN 1 /*是否允许使用信号量功能*/
#define OS_SEM_ACCEPT_EN 1 /*是否允许使用OSSemAccept()*/
#define OS_SEM_DEL_EN 1 /*是否允许使用OSSemDel() */
#define OS_SEM_QUERY_EN 1 /*是否允许使用OSSemQuery()*/
#define OS_TASK_CHANGE_PRIO_EN 1 /*是否允许使用OSTaskChangePrio()*/ #define OS_TASK_CREA TE_EN 1 /*是否允许使用OSTaskCreate()*/
#define OS_TASK_CREA TE_EXT_EN 1 /*是否允许使用OSTaskCreateExt()*/
#define OS_TASK_DEL_EN 1 /*是否允许使用OSTaskDel()*/
#define OS_TASK_SUSPEND_EN 1 /*是否允许使用OSTaskSuspend() and OSTaskResume()*/ #define OS_TASK_QUERY_EN 1 /*是否允许使用OSTaskQuery()*/
#define OS_TIME_DLY_HMSM_EN 1 /*是否允许使用OSTimeDlyHMSM()*/
#define OS_TIME_DLY_RESUME_EN 1 /*是否允许使用OSTimeDlyResume()*/
#define OS_TIME_GET_SET_EN 1 /*是否允许使用OSTimeGet() 和OSTimeSet()*/ #define OS_SCHED_LOCK_EN 1 /*是否允许使用OSSchedLock()和OSSchedUnlock()*/ #define OS_TICKS_PER_SEC 200 /*设置每秒之内的时钟节拍数目*/
2.3 源程序说明
首先,在main()函数中创建一个二值信号量:
mutex=OSSemCreate(1);
然后,在main()函数中创建TaskStart任务。
TaskStart任务
在TaskStart任务中创建并启动所有的应用任务Task0, Task1, Task2。
void TaskStart(void *pdata){
uint8 i;
uint8 TaskData[3];
for (i = 0; i < 3; i++){
TaskData[i] = i;
}
printf("uC/OS-II is Started\n==============================\n");
TargetInit();
OSTaskCreate(Task0, (void *)&TaskData[0], &Task0Stk[TASK_STK_SIZE - 1], 5);
OSTaskCreate(Task1, (void *)&TaskData[1], &Task1Stk[TASK_STK_SIZE - 1], 6);
OSTaskCreate(Task2, (void *)&TaskData[2], &Task2Stk[TASK_STK_SIZE - 1], 7);
for(;;){
OSTaskSuspend(0);
}
}
任务Task0的优先级最高,它需要使用信号量mutex:
void Task0(void *pdata){
液体收集系统uint8 err,id;
id = *(int *)pdata;
for(;;){
printf("Task %d is waitting a event.\n",id);
OSTimeDly(200);
printf("The event of Task %d come.\n",id);
printf("Task %d is try to get mutex.\n",id);
OSSemPend(mutex,0,&err);
switch(err){
case OS_NO_ERR:
printf("Task %d has got the mutex.\n",id);
printf("=========================================\n");
printf("\n");
break;
default:
printf("Task %d is suspended.\n",id);
printf("=========================================\n");
printf("\n");
break;
}
DelayTime(3000);
OSTaskSuspend(5);
}
}
任务Task1具有中等优先级,它不使用信号量:
void Task1(void *pdata){