嵌入式实时系统中的共享全局变量
C语言的程序员为了方便,经常会不顾这个编码规范。全局变量有一些显而易见的好处:全局可见,内存地址固定,读写效率高。比起优点来,全局变量的槽点更多:1)学过面向对象语言的同学会更加谨慎的使用全局变量,这破坏了函数的封装性能,降低了函数的可移植性。2)使代码可读性差,大型程序里面简直是灾难 3)生存期长,会占用较多的内存单元。
嵌入式实时系统中需要注意的全局变量
举个例子
u8 Seconds;
u8 Minutes;
u8 Hours;
void TimeOfDay(void *p_arg)
{
(void)p_arg;
OS_ERR err;
while (DEF_TRUE) { /* Task body, always written as an infinite loop. */
OSTimeDlyHMSM(0, 0, 1, 0,
OS_OPT_TIME_HMSM_STRICT,
&err);
Seconds++;
if(Seconds > 59)
{
Seconds = 0;
Minutes++;
if(Minutes > 59)
{
Minutes = 0;
Hours++;
}
if(Hours > 23)
{
Hours = 0;
}
}
}
}
假如在模块执行完Minutes = 0;这一行代码时,一个中断发生了,并使得一个具有比void TimeOfDay(void *p_arg)更高优先级的任务进入了就绪表,那么在中断结束返回后,TimeOfDay()就会被这个更高优先级的任务抢占而无法继续运行。一旦高优先级的任务想要从时钟模块中获取时间,那么由于中断前时钟模块的小时值没有更新,那么所读到的将会是一个与正确时间相差整整一个小时的错误值。
这里的时钟模块就是一个共享资源,在ucos中必须对此加以保护,保证对共享资源的独占访问。
参考资料:
转自博客园网友《ucos中需要注意的全局变量》
解决办法
1 增加临界段,在上述代码块两端增加临界段限制任务切换.
OS_ENTER_CRITICAL();
…
OS_EXIT_CRITICAL();
2.使用互斥信号量,保证两个任务互斥,计时任务运行时,获取时间被阻断。
OSMutexPost();
OSMutexPend();
3.使用消息邮箱
将Seconds ,Minutes,Hours 定义为地址连续的数据结构,通过消息邮箱发送给获取时间的任务。
INT8U OSMboxPost (OS_EVENT *pevent,
void *pmsg)
OSMboxPost 自带临界段,且指针传递耗时较短,提高了共享全局的安全性。
S_ENTER_CRITICAL();
if (pevent->OSEventGrp != 0u) { /* See if any task pending on mailbox */
/* Ready HPT waiting on event */
(void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_MBOX, OS_STAT_PEND_OK);
OS_EXIT_CRITICAL();
OS_Sched(); /* Find highest priority task ready to run */
return (OS_ERR_NONE);
}
if (pevent->OSEventPtr != (void *)0) { /* Make sure mailbox doesn't already have a msg */
OS_EXIT_CRITICAL();
return (OS_ERR_MBOX_FULL);
}
pevent->OSEventPtr = pmsg; /* Place message in mailbox */
OS_EXIT_CRITICAL();
return (OS_ERR_NONE);
只有当一轮计时完成,给获取时间的任务发送消息以后,才会获取时间的任务才会读取时间。
如果获取时间的任务频率较高and优先级较高,可以及时将时间读走,不会出现时间覆盖的情况。
如果不满足以上情况,邮箱指针指向的时间继续累加,计数过程中进入中断并执行任务切换,还可能出现所读到的是一个与正确时间相差整整一个小时的错误值。
4.使用消息队列
为保证数据的连续性,不遗漏数据,可以使用消息队列。
将不同时刻的数据存放在不同的内存区,循环存放,每次数据都放入消息队列中。
INT8U OSQPost (OS_EVENT *pevent,
void *pmsg)存入数据。
使用void *OSQPend (OS_EVENT *pevent,
INT32U timeout,
INT8U *perr)读取最先存入的消息。
INT8U OSQPostFront (OS_EVENT *pevent,
void *pmsg)存入数据。
使用void *OSQPend (OS_EVENT *pevent,
INT32U timeout,
INT8U *perr)读取最后存入的消息。
使用消息队列,可以防止指针指向的数据正在累加时,进入任务切换。