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使用對于自己有效的共享資源時,共享資源被修改,造成錯誤。
是不是所有的共享資源都是需要進行資源同步?不一定,如一些共享資源的屬性是只讀,不能被寫,所有使用它的代碼段,只能讀取它,不能修改它,所以不需要資源同步;對于那些可讀可寫的共享資源,一定要進行資源同步。
如何分析一個共享資源,存在的安全隱患?
【只要是全局的資源(不是某個代碼段私有的,且不是只讀),就一定考慮:使用資源的過程被其他的代碼段打斷,資源被修改的情況,從這點出發再去做防范】
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函數會判斷,如果是,那就是在中斷中 正在調用任務級的切換,這是不允許的(如在中斷中創建任務等)。
】