打印
[资料分享与下载]

uC/OS II程序设计点滴记录【经验、技巧、错误等】

[复制链接]
1842|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
舒斯特尔|  楼主 | 2015-8-19 11:02 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
1:任务有切换,但切换到某个任务,总是出现hardfault错误?【现象:给出错的任务换一个大小一样但名字不一样的堆栈就可以,使用原来名字的堆栈就是出错】
   解决:1:查看hardfault寄存器,找到出错的原因-->提示是fault上报导致
         2:查看其它的fault寄存器,发现是用法fault-->具体为异常返回时试图非法加载EXC_RETURN到PC...
         3: 又查看出错任务的堆栈(使用PSP,通过PSP查看,hardfault使用msp),找出出错时(进入hardfault前)的PC与LR,再跳到该PC处,还是不能发现不了问题。
         4:直接在应用层调试,查看任务的堆栈,发现任务的堆栈256个字节都使用了,推测是堆栈溢出导致的问题,加大堆栈到512个字节,上面的现象解决,错误排除。
         【其实还可以在内存中对某个地址加上观察点来试一下,我没有做】
   但是为什么给出错的任务换一个大小一样但名字不一样的堆栈就可以,使用原来名字的堆栈就是出错?原因是这两个堆栈分配的空间是不一样的,而且程序中还有很多的数据分配在RAM中,两个堆栈有可能都溢出了,但是没出错的堆栈有可能是他溢出后,它的堆栈没有被其他的代码修改,或者堆栈溢出没有影响带其他的变量或者数据等,不出错其实是一个巧合。
        所以在uC/OS编程中,如果出现上述分析的用法fault错误,非法加载EXC_RETURN到PC,很可能就是堆栈溢出后,被其他程序修改导致,加大堆栈试一下。
2:一个任务的堆栈大小怎么估算?
   在uC/OS II中,创建任务,至少要考虑到被切换,任务切换至少使用17个寄存器(68个字节),这时再加上任务的局部变量、参数传递、函数调用等还要使用堆栈,所以至少要大于 68多 的字节。【68字节的堆栈只能符合空函数之类很简单的函数,因为在任务函数中,函数内各个私有变量以及函数调用时参数的传递基本都是使用CPU寄存器或堆栈配合来实现,这样栈肯定要大于68,如果这时栈太小,那么程序运行到需要一些开销很大的数值等时,很可能因为溢出造成hardfault错误】
   方法1:可以先分配大的堆栈,再使用堆栈检验功能,带任务运行一段时间,估计堆栈使用最多的时候已经过了,再通过堆栈检验函数查看具体的堆栈使用了多少,再可以修改代码或者动态分配内存在创建任务。
   方法2:来自网上自己还没有验证:
这个不是这样滴,微扣死吐 有个高级选择,CreateTaskPrxx 里面可以选择一个类似于debug模式,然后里面有个类似于stackDepth的东西,Run起来就可以知道这个Task大概用了多少ram了。当然了,前提是必须把Task的所有路径运行完毕。
PS:一般人我不告诉他的,看你是原子锅的粉丝就额外给你的建议。


3:任务划分
  在uC/OS II工程中,可能会包含多种外设,可能会有很多种功能,比如键盘,显示等等, 其实任务划分时最好将各功能 任务化, 比如显示就单独成立一个显示任务,键盘就单独成立一个键盘扫描任务,任务之间通信通过各种通信机制进行; 不能在各个任务之间,将各种的功能太过交叉化,比如显示功能,该功能模块可能会有多个显示函数接口,那么如果不单独将显示做出一个独立的任务,那么在很多任务中就要交叉使用这个显示函数接口,如果某一个显示函数接口在函数可重入性方面做的不好,就会引发程序错误;那如果将显示独立做成一个任务【也就是将函数都变成该任务的私有函数】,那其他的任务想要显示时,可以通过邮箱或消息队列与显示任务通信,这样程序就会安全很多。
  目前,各功能单独成立为一个任务,比如显示功能成为显示任务,文件系统通过一个任务来管理,这样文件系统任务要显示时,就向显示任务的消息队列里面发送显示消息,而不是直接在本任务中调用显示函数。


4:资源同步
采样任务将AD的采样结果转换并存储到数组data[]中,显示任务从data[]中读取数据并显示。
两个任务需要访问同一个资源:data[],那么时就可以先定义一个互斥信号量,任务一个任务需先获取该互斥信号量再进行操作,最后释放信号量。
用简单的二值信号来解决资源访问冲突,因为没有优先级的反转,容易锁住(为什么?或者不是这样
,待求证),比如低优先级的任务在获取了二值信号量还没有释放时就被高优先级的任务抢占

什么是共享资源?共享资源就是被两个或以上的并发程序单元(如:ISR与任务、任务与任务)访问的资源,共享资源一定是全局资源,但是全局资源不一定是共享资源,如字体数组,是全局的数值,但只被单个任务使用(显示任务),就不是共享资源;
什么是资源同步?访问共享资源的代码段位临界区(关键段落),各个临界区访问共享资源时,一定要保住互斥访问,要做到这点,就需要使用相关的措施,这些措施就是资源同步。
为什么要使用资源同步?因为可读可写的共享资源的访问一定要在互斥条件下进行,只有这样才能保证共享资源的可靠性与完整性;如当前的A临界区要用到共享资源,且这时的共享资源对A有效,那如果不适用资源同步,就很有可能在A使用对于自己有效的共享资源时,共享资源被修改,造成错误。
是不是所有的共享资源都是需要进行资源同步?不一定,如一些共享资源的属性是只读,不能被写,所有使用它的代码段,只能读取它,不能修改它,所以不需要资源同步;对于那些可读可写的共享资源,一定要进行资源同步。

相关帖子

沙发
舒斯特尔|  楼主 | 2015-8-19 11:03 | 只看该作者
如何分析一个共享资源,存在的安全隐患?
   【只要是全局的资源(不是某个代码段私有的,且不是只读),就一定考虑:使用资源的过程被其他的代码段打断,资源被修改的情况,从这点出发再去做防范】
    1:由于系统存在各种突发事件(如中断、时间片轮转),可读可写的共享资源在没有使用资源同步措施情况下一定存在不可靠性与不完整性。
    2:从访问共享资源出错的调度去分析:在使用共享资源的地方(要有一种意识:使用资源的地方即使只用一句话,这个使用的过程也是需要CPU多步走,即使用共享资源,就存在使用过程被打断的情况),假设出错(可能原因是中断修改资源、中断触发高优先任务运行修改资源、时间片轮转后其他任务修改),这时再去分析,具体的代码会怎么样,应该做如何的修改。
资源同步的措施有哪些? 1:关中断 2:关调度 3:使用互斥信号量 4:使用计数信号量

关中断方法:应使关中断的时间尽可能的短(可以联想到linux中处理中断时的方法:上下文法,让需要实时性很高的代码在关闭中断下处理(上文),对于一些耗时的操作,可以放在中断外面去作为一个线程去运行(下文)),有这个思路,我们也可以借鉴,如在一个临界区关了中断,要访问共享资源,我们可以先只读取数据到一个临时的地方(所谓对数据拍照),然后立马开中断,对数据的处理(较耗时)放在中断外面进行。
例如:RTC,RTC中断服务程序中设置全局数组中的时分秒,我们在任务中读取这个全局数组时就可以先关中断,再拍照,再开中断,再处理数据(如显示等等),这样系统对中断的实时性响应就很好。
【并发程序包含ISR时,只能通过关中断措施来访问共享资源,关中断直接影响系统的实时性,因此只能用于对简单共享资源的短暂访问,故关中断常用于对全局变量或小规模全局数据结构的访问,且需要使用拍照的方法】
关调度方法:当临界区代码不包含ISR时(即全部是任务级代码),可以通过关调度的方法,访问共享资源;关调度的方**影响与共享资源无关的任务的运行。【直接关调度的方法优点不多,缺点不少,尽可能不要使用】
使用互斥信号量:(ISR中不包含临界区代码的情况)互斥信号量也是二值的,专门用于资源同步的信号量,与用于行为同步的二值信号量(二值[计数]信号量也可用作资源同步)不同,互斥信号量还可以进行优先级翻转[临时调高优先级]。使用互斥信号量访问共享资源,对中断和任务调度都没有限制,系统可以照常响应各种异步事件,且其他与共享资源无关的高优先级任务也可以运行。【使用互斥信号量进行资源访问对系统的实时性影响最小】
           1:选取互斥信号量:OSMutexPend(sem,0,&err)
           2: 访问共享资源
           3: 释放互斥信号量:OSMutexPost(sem)
使用计数信号量:与用于行为同步的计数信号量不一样,用于资源同步的计数信号量的初始值为共享资源的实体总数【如内存:同类型的内存分配了好几块,那么此时计数信号量的值就是这个总数,计数信号量减1,表示这类型的内存块就有一块被占用,直到用完,其他的任务再要使用就需要等待,这对于有多个实体的共享资源比较好,其实这里还要管理具体的那个任务占用了具体的那个实体资源】

5:UCOSII的中断服务函数是不是一定都要先调用OSIntEnter(), 退出时调用OSIntExit()?
    不一定,一般没有调用任何操作系统的服务函数(如发送信号量之类函数),就不需要操作系统来干预。
   调用OSIntEnter()目的是进行中断嵌套计数,调用OSIntExit()的目的是在该中断退出后进行任务切换。
   首先说OSIntExit():如果中断服务例程并没有调用任务的OS函数,那么中断退出后,对系统中对各任务的就绪情况完全没有影响,这时调研 OSIntExit()就是一种浪费,就不用调用,让中断退出后,直接回到被中断的任务处。
   再说 OSIntEnter ():OSIntEnter ()只是对中断嵌套计数变量加1,如果调用了OSIntEnter ()就必须调用在中断退出时调用OSIntExit()对中断嵌套计数变量减1(或直接操作该变量),这样成对调用最后的结果是抵消,那既然如果没有必要使用OSIntExit,那就不用使用OSIntEnter
   【OSIntNesting系统引入这个计数变量的目的:
1:在OSIntExit中,通过该变量判断是否所以中断都响应了,如果是系统就要在中断退出后,保证系统优先级最高的任务运行(即有可能进行任务切换)。【即为了保证所有嵌套的中断都响应后,在退出中断时,有可能切换任务】
2:通过该变量判断目前环境是否处于中断中,如OS_Sched函数会判断,如果是,那就是在中断中 正在调用任务级的切换,这是不允许的(如在中断中创建任务等)。

使用特权

评论回复
板凳
FSL_TICS_Jeremy| | 2015-8-19 13:28 | 只看该作者
谢谢楼主分享!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

25

主题

277

帖子

1

粉丝