RTOS 診斷和錯(cuò)誤檢查
錯(cuò)誤處理不太可能成為任何用于嵌入式系統(tǒng)應(yīng)用的操作系統(tǒng)的主要功能。這是資源限制的必然結(jié)果——所有嵌入式系統(tǒng)都有某種限制。這也是合乎邏輯的,因?yàn)橹挥杏邢迶?shù)量的嵌入式系統(tǒng)有機(jī)會(huì)像桌面系統(tǒng)一樣運(yùn)行——即為用戶(hù)提供在發(fā)生某些異常事件時(shí)決定下一步做什么的機(jī)會(huì)。
在 Nucleus SE 中,錯(cuò)誤檢查大致有三種類(lèi)型:
·
對(duì)所選配置進(jìn)行“健全性檢查”的設(shè)施——只是為了確保所選選項(xiàng)是一致的
·
·
可選包含代碼來(lái)檢查運(yùn)行時(shí)行為
·
·
特定的 API 函數(shù)有助于設(shè)計(jì)更強(qiáng)大的代碼
·
本文將介紹這些內(nèi)容以及一些關(guān)于用戶(hù)實(shí)現(xiàn)的診斷的想法。
配置檢查
Nucleus SE 的設(shè)計(jì)非常便于用戶(hù)配置,因此可以根據(jù)需要進(jìn)行定制,以充分利用可用資源。這種可配置性是一個(gè)挑戰(zhàn),因?yàn)檫x項(xiàng)的數(shù)量以及它們之間的相互依賴(lài)性非常大。正如之前許多文章中所述,Nucleus SE 的大多數(shù)用戶(hù)配置都是通過(guò) 在文件nuse_config.h中設(shè)置#define常量來(lái)執(zhí)行的。
為了幫助識(shí)別配置錯(cuò)誤,包含了一個(gè)文件 - nuse_config_check.h (即通過(guò)#include 到nuse_config.c中),該文件對(duì)#define符號(hào)執(zhí)行了許多一致性檢查 。以下是該文件的摘錄:
/*** Tasks and task control ***/
#if NUSE_TASK_NUMBER < 1 || NUSE_TASK_NUMBER > 16
#error NUSE: invalid number of tasks – must be 1-16
#endif
#if NUSE_TASK_RELINQUISH && (NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER)
#error NUSE: NUSE_Task_Relinquish() selected – not valid with
priority scheduler
#endif
#if NUSE_TASK_RESUME && !NUSE_SUSPEND_ENABLE
#error NUSE: NUSE_Task_Resume() selected – task suspend not
enabled
#endif
#if NUSE_TASK_SUSPEND && !NUSE_SUSPEND_ENABLE
#error NUSE: NUSE_Task_Suspend() selected – task suspend not
enabled
#endif
#if NUSE_INITIAL_TASK_STATE_SUPPORT && !NUSE_SUSPEND_ENABLE
#error NUSE: Initial task state enabled – task suspend not
enabled
#endif
/*** Partition pools ***/
#if NUSE_PARTITION_POOL_NUMBER > 16
#error NUSE: invalid number of partition pools – must be 0-16
#endif
#if NUSE_PARTITION_POOL_NUMBER == 0
#if NUSE_PARTITION_ALLOCATE
#error NUSE: NUSE_Partition_Allocate() enabled – no
partition pools configured
#endif
#if NUSE_PARTITION_DEALLOCATE
#error NUSE: NUSE_Partition_Deallocate() enabled – no
partition pools configured
#endif
#if NUSE_PARTITION_POOL_INFORMATION
#error NUSE: NUSE_Partition_Pool_Information() enabled –
no partition pools configured
#endif
#endif
執(zhí)行的檢查包括以下內(nèi)容:
·
驗(yàn)證已配置至少一個(gè)(但不超過(guò)十六個(gè))任務(wù)
·
·
確認(rèn)所選的 API 函數(shù)與所選的調(diào)度程序或其他選項(xiàng)不一致
·
·
驗(yàn)證是否已指定不超過(guò) 16 個(gè)其他內(nèi)核對(duì)象的實(shí)例
·
·
確認(rèn)沒(méi)有為根本沒(méi)有實(shí)例化的對(duì)象選擇 API 函數(shù)
·
·
確保在未啟用這些功能時(shí)不選擇信號(hào)和系統(tǒng)時(shí)間的 API 函數(shù)
·
·
驗(yàn)證所選調(diào)度程序類(lèi)型和相關(guān)選項(xiàng)
·
在所有情況下,檢測(cè)到錯(cuò)誤都會(huì)導(dǎo)致編譯#error 語(yǔ)句。這通常會(huì)導(dǎo)致編譯以指定的消息終止。
此文件不會(huì)使創(chuàng)建不合邏輯的配置變得不可能,但會(huì)使這種配置不太可能發(fā)生。
API 參數(shù)檢查
與 Nucleus RTOS 一樣,Nucleus SE 具有可選功能,可包含代碼以在運(yùn)行時(shí)驗(yàn)證 API 函數(shù)調(diào)用參數(shù)。通常,這只會(huì)在初始調(diào)試和測(cè)試期間使用,因?yàn)樯a(chǎn)代碼中的內(nèi)存和運(yùn)行時(shí)開(kāi)銷(xiāo)是不可取的。
通過(guò)將nuse_config.h 中的NUSE_API_PARAMETER_CHECKING設(shè)置 為T(mén)RUE來(lái)啟用參數(shù)檢查。這樣就可以編譯所需的附加代碼。以下是 API 函數(shù)參數(shù)檢查的示例:
STATUS NUSE_Mailbox_Send(NUSE_MAILBOX mailbox, ADDR *message,
U8 suspend)
{
STATUS return_value;
#if NUSE_API_PARAMETER_CHECKING
if (mailbox >= NUSE_MAILBOX_NUMBER)
{
return NUSE_INVALID_MAILBOX;
}
if (message == NULL)
{
return NUSE_INVALID_POINTER;
}
#if NUSE_BLOCKING_ENABLE
if ((suspend != NUSE_NO_SUSPEND) &&
(suspend != NUSE_SUSPEND))
{
return NUSE_INVALID_SUSPEND;
}
#else
if (suspend != NUSE_NO_SUSPEND)
{
return NUSE_INVALID_SUSPEND;
}
#endif
#endif
此參數(shù)檢查可能會(huì)導(dǎo)致 API 函數(shù)調(diào)用返回錯(cuò)誤代碼。這些都是形式為NUSE_INVALID_xxx的負(fù)值 (例如NUSE_INVALID_POINTER)——完整的定義集包含在nuse_codes.h中。
可以包含額外的應(yīng)用程序代碼(可能是有條件編譯的)來(lái)處理這些錯(cuò)誤值,但最好使用現(xiàn)代嵌入式調(diào)試器的數(shù)據(jù)監(jiān)控功能來(lái)檢測(cè)它們。
參數(shù)檢查會(huì)引入內(nèi)存開(kāi)銷(xiāo)(額外代碼)和運(yùn)行時(shí)性能,因此其使用有些干擾。由于 Nucleus SE 的完整源代碼可供開(kāi)發(fā)人員使用,因此如果需要絕對(duì)的精度,可以“手動(dòng)”對(duì)生產(chǎn)代碼進(jìn)行檢查和調(diào)試。
任務(wù)堆棧檢查
只要未使用“運(yùn)行至完成”調(diào)度程序,Nucleus SE 就會(huì)提供任務(wù)堆棧檢查功能,該功能與 Nucleus RTOS 中提供的類(lèi)似,并提供剩余堆??臻g的指示。此 API 調(diào)用 - NUSE_Task_Check_Stack() - 在上一篇文章中進(jìn)行了詳細(xì)描述。本文后面的“用戶(hù)診斷”部分將討論有關(guān)堆棧錯(cuò)誤檢查的一些想法。
版本信息
Nucleus RTOS 和 Nucleus SE 有一個(gè) API 函數(shù),它簡(jiǎn)單地返回有關(guān)內(nèi)核的版本/發(fā)布信息。
Nucleus RTOS API 調(diào)用
服務(wù)調(diào)用原型:
字符*NU_Release_Information(VOID);
參數(shù):
沒(méi)有任何
返回:
指向以 NULL 結(jié)尾的版本字符串的指針
Nucleus SE API 調(diào)用
此 API 調(diào)用支持 Nucleus RTOS API 的關(guān)鍵功能。
服務(wù)調(diào)用原型:
char *NUSE_Release_Information(void);
參數(shù):
沒(méi)有任何
返回:
指向以 NULL 結(jié)尾的版本字符串的指針
Nucleus SE 實(shí)施發(fā)布信息
這個(gè)API調(diào)用的實(shí)現(xiàn)幾乎很簡(jiǎn)單。返回一個(gè)指向在nuse_globals.c中聲明和初始化的字符串常量NUSE_Release_Info的指針。
該字符串采用 Nucleus SE – Xyymmdd的形式,其中:
X 是發(fā)布狀態(tài):A = alpha;B = beta;R = 已發(fā)布
yy 是發(fā)布年份
mm 是發(fā)布月份
dd 是發(fā)布日期
與 Nucleus RTOS 的兼容性
Nucleus RTOS 包含一個(gè)可選功能,用于維護(hù)歷史記錄。內(nèi)核記錄各種系統(tǒng)活動(dòng)的詳細(xì)信息。提供 API 函數(shù)以使應(yīng)用程序能夠:
·
啟用/禁用歷史記錄保存
·
·
記錄歷史
·
·
檢索歷史記錄條目
·
Nucleus SE 不支持此功能。
Nucleus RTOS 還包括一些錯(cuò)誤管理宏,它們執(zhí)行斷言并提供調(diào)用用戶(hù)定義的致命錯(cuò)誤函數(shù)的方法。這些宏有條件地包含在 OS 構(gòu)建中。Nucleus SE 不支持此類(lèi)功能。
用戶(hù)診斷
到目前為止,我們?cè)诒疚闹醒芯苛?Nucleus SE 本身提供的診斷和錯(cuò)誤檢查功能?,F(xiàn)在是一個(gè)很好的機(jī)會(huì)來(lái)考慮如何使用內(nèi)核提供的功能和/或應(yīng)用我們對(duì)其內(nèi)部結(jié)構(gòu)和實(shí)現(xiàn)的了解來(lái)實(shí)現(xiàn)用戶(hù)定義或面向應(yīng)用程序的診斷。
特定應(yīng)用診斷
幾乎任何應(yīng)用程序都可以添加額外的代碼來(lái)在運(yùn)行時(shí)檢查其自身的完整性。使用多任務(wù)內(nèi)核,讓特定任務(wù)執(zhí)行這項(xiàng)工作非常方便和直接。顯然,特定于應(yīng)用程序的診斷不在本系列文章的討論范圍內(nèi),但我們可以考慮一些廣泛的想法。
內(nèi)存檢查
內(nèi)存的正確運(yùn)行顯然對(duì)任何基于處理器的系統(tǒng)的完整性都至關(guān)重要。同樣明顯的是,災(zāi)難性故障會(huì)阻止任何軟件(更不用說(shuō)診斷程序)運(yùn)行。但在某些情況下,一定程度的故障會(huì)發(fā)生,這是一個(gè)主要問(wèn)題,但不會(huì)完全阻止代碼執(zhí)行。測(cè)試內(nèi)存是一個(gè)相當(dāng)復(fù)雜的主題,遠(yuǎn)遠(yuǎn)超出了本文的范圍,所以我只能給出一些一般性的想法。
在 RAM 中可能發(fā)生的兩個(gè)最常見(jiàn)故障是:“卡住位”——某個(gè)位的值為零或一,無(wú)法更改;以及“串?dāng)_”——相鄰位相互干擾??梢酝ㄟ^(guò)依次將適當(dāng)?shù)臏y(cè)試模式寫(xiě)入和讀回每個(gè) RAM 位置來(lái)測(cè)試這些故障。有些測(cè)試實(shí)際上只能在啟動(dòng)時(shí)執(zhí)行,甚至在建立堆棧之前;例如,“移動(dòng)位”測(cè)試,其中內(nèi)存中的每個(gè)位都設(shè)置為 1,并檢查其他每個(gè)位以確保其為零。其他逐字節(jié)模式測(cè)試可以即時(shí)執(zhí)行,只要您確保在 RAM 位置損壞時(shí)不會(huì)發(fā)生上下文切換。使用 Nucleus SE 臨界區(qū)分隔宏NUSE_CS_Enter() 和NUSE_CS_Exit()在nuse_types.h中定義,簡(jiǎn)單且可移植。
各種類(lèi)型的 ROM 也有可能偶爾出現(xiàn)故障,但軟件能夠進(jìn)行的檢查有限。在構(gòu)建代碼時(shí)生成的校驗(yàn)和會(huì)很有用。可以在啟動(dòng)時(shí)檢查,也可以在運(yùn)行時(shí)檢查。
內(nèi)存尋址邏輯故障會(huì)影響 ROM 和 RAM??梢栽O(shè)計(jì)專(zhuān)門(mén)針對(duì)此問(wèn)題的測(cè)試,但最有可能在上述其他測(cè)試中出現(xiàn)。
外圍設(shè)備檢查
除了 CPU 之外,外圍電路也有可能出現(xiàn)故障。當(dāng)然,這種情況在不同系統(tǒng)之間存在很大差異,但設(shè)備通常都有一些方法可以通過(guò)診斷軟件來(lái)驗(yàn)證其完整性。例如,通信線路可能具有環(huán)回模式,任何寫(xiě)入該線路的數(shù)據(jù)都會(huì)立即返回。
看門(mén)狗服務(wù)
嵌入式系統(tǒng)設(shè)計(jì)人員通常會(huì)使用“看門(mén)狗”電路。這是一種外圍設(shè)備,它要么中斷 CPU 并期望得到及時(shí)響應(yīng),要么(更確切地說(shuō))需要軟件發(fā)起定期訪問(wèn)。無(wú)論是哪種情況,看門(mén)狗“咬人”的常見(jiàn)結(jié)果是系統(tǒng)重置。
在多任務(wù)環(huán)境中有效使用看門(mén)狗是一項(xiàng)挑戰(zhàn)。僅通過(guò)一項(xiàng)任務(wù)對(duì)其進(jìn)行維護(hù)只能確認(rèn)該特定任務(wù)正在運(yùn)行。解決此問(wèn)題的一種方法是實(shí)施“監(jiān)控任務(wù)”——本文后面將介紹此示例。
堆棧溢出檢查
除非您選擇“運(yùn)行至完成”調(diào)度程序,否則 Nucleus SE 應(yīng)用程序?qū)槊總€(gè)任務(wù)包含一個(gè)堆棧。這些堆棧的完整性至關(guān)重要,但 RAM 可能有限,因此獲得最佳大小至關(guān)重要。靜態(tài)預(yù)測(cè)每個(gè)任務(wù)的堆棧要求是可能的,但非常困難——它需要有足夠的容量來(lái)滿(mǎn)足最嵌套的函數(shù)調(diào)用和最耗費(fèi)堆棧的中斷服務(wù)例程的需求。一種更簡(jiǎn)單的方法是執(zhí)行詳盡的運(yùn)行時(shí)測(cè)試。
堆棧驗(yàn)證大致有兩種方法。如果使用復(fù)雜的嵌入式軟件調(diào)試器,則可以監(jiān)視堆棧邊界并檢測(cè)任何違規(guī)行為。Nucleus SE 堆棧的位置和大小可通過(guò) ROM 中的全局?jǐn)?shù)據(jù)結(jié)構(gòu)輕松訪問(wèn):NUSE_Task_Stack_Base[] 和NUSE_Task_Stack_Size[]。
另一種方法是執(zhí)行運(yùn)行時(shí)測(cè)試。通常的方法是在每個(gè)堆棧的末尾添加“保護(hù)字”——通常這些字是每個(gè)堆棧數(shù)據(jù)區(qū)域中的第一個(gè)位置。這些字被初始化為可識(shí)別的非零值。然后,診斷例程/任務(wù)檢查它們是否已更改并采取適當(dāng)?shù)拇胧?。覆蓋保護(hù)字并不意味著堆棧實(shí)際上已溢出,而是表明它即將溢出;因此,軟件可能會(huì)繼續(xù)執(zhí)行足夠長(zhǎng)的時(shí)間以采取糾正措施或警告用戶(hù)。
主管任務(wù)
盡管 Nucleus SE 不會(huì)將這 16 個(gè)可能的任務(wù)中的任何一個(gè)保留給自己使用,但用戶(hù)可以選擇將其中一個(gè)任務(wù)專(zhuān)門(mén)用于診斷。這可能是一個(gè)低優(yōu)先級(jí)任務(wù),它只利用任何“空閑”的 CPU 時(shí)間;也可能是一個(gè)高優(yōu)先級(jí)任務(wù),它偶爾會(huì)運(yùn)行一小段時(shí)間,從而確保始終定期執(zhí)行診斷。
以下是示例的運(yùn)行方式:
主管任務(wù)的信號(hào)標(biāo)志用于監(jiān)視系統(tǒng)中六個(gè)關(guān)鍵任務(wù)的運(yùn)行。每個(gè)任務(wù)都使用一個(gè)特定的標(biāo)志(位 0 到位 5),并且需要定期設(shè)置它。主管任務(wù)清除所有標(biāo)志,然后暫停一段時(shí)間。當(dāng)它恢復(fù)時(shí),它希望所有六個(gè)任務(wù)都通過(guò)設(shè)置適當(dāng)?shù)臉?biāo)志來(lái)“簽到”;它尋求與值b00111111 (來(lái)自nuse_binary.h)的精確匹配。如果滿(mǎn)足這一點(diǎn),它將清除標(biāo)志并再次暫停。如果不滿(mǎn)足,它將調(diào)用關(guān)鍵錯(cuò)誤處理例程,例如,該例程可能執(zhí)行系統(tǒng)重置。
另一種實(shí)現(xiàn)方式是使用事件標(biāo)志組。如果信號(hào)未在應(yīng)用程序中的其他地方使用(因?yàn)樗腥蝿?wù)都會(huì)產(chǎn)生 RAM 開(kāi)銷(xiāo)),則這種方法是有意義的,如果事件標(biāo)志用于其他目的,則這種方法更有意義。
跟蹤和分析
盡管許多現(xiàn)代嵌入式軟件調(diào)試器都具有很高的可定制性,并且可以“感知 RTOS”,但調(diào)試多線程應(yīng)用程序仍然具有挑戰(zhàn)性。一種廣泛使用的方法是執(zhí)行后分析,其中對(duì) (RTOS) 代碼進(jìn)行檢測(cè),以便可以回顧性地分析其操作的詳細(xì)審計(jì)。通常,實(shí)現(xiàn)這樣的功能涉及兩個(gè)組件:
1.
向 RTOS 添加附加代碼(即“儀表化”)以記錄活動(dòng)。通常,這將包含在預(yù)處理器指令中以方便條件編譯。當(dāng)發(fā)生重大事件(如 API 調(diào)用或上下文切換)時(shí),此代碼會(huì)存儲(chǔ)幾個(gè)字節(jié)的信息。這些信息包括以下內(nèi)容:
2.
– 當(dāng)前地址(PC)。
– 當(dāng)前任務(wù) ID(索引)。
– 所涉及的任何其他對(duì)象的索引。
– 指示已執(zhí)行操作的代碼。
3.
專(zhuān)門(mén)用于將配置文件信息緩沖區(qū)清空到外部存儲(chǔ)器(通常是主機(jī))的任務(wù)。
4.
這些捕獲數(shù)據(jù)的分析也需要一些工作,但這可能就像使用 Excel 電子表格一樣簡(jiǎn)單。