黃 河 任 見(jiàn) 佘 慶,2 錢 晨 楊 帆
1(上海華元?jiǎng)?chuàng)信軟件有限公司 上海 200062) 2(華東師范大學(xué)軟件工程學(xué)院 上海 200062)
在嵌入式實(shí)時(shí)系統(tǒng)[1-2]運(yùn)行環(huán)境中出現(xiàn)故障時(shí),僅依靠操作描述和問(wèn)題現(xiàn)象的記錄,很難定位故障點(diǎn)[3-4]以及分析引發(fā)異常的原因[5-7],占用實(shí)際運(yùn)行環(huán)境來(lái)進(jìn)行故障復(fù)現(xiàn)是不現(xiàn)實(shí)的,還可能引入新的問(wèn)題,造成更加嚴(yán)重的后果與不良影響[8-9]。
目前,通用的日志服務(wù)[10-12]是將當(dāng)前運(yùn)行任務(wù)和系統(tǒng)所有歷史記錄的日志信息,保存到外部存儲(chǔ)介質(zhì)文件系統(tǒng)中。當(dāng)系統(tǒng)需要診斷時(shí),導(dǎo)出日志文件并打開(kāi)獲取所有的日志記錄信息[8,13-15]。但這種方式主要有以下局限性:外部存儲(chǔ)介質(zhì)使用壽命有限;訪問(wèn)速度慢;頻繁的讀寫訪問(wèn)對(duì)系統(tǒng)資源消耗嚴(yán)重,增大文件系統(tǒng)損壞的概率,使用效率低[16-17];讀取所有日志記錄信息,龐大而無(wú)序[18],不易區(qū)分,難以實(shí)現(xiàn)快速、準(zhǔn)確的系統(tǒng)故障診斷與定位。對(duì)于嵌入式實(shí)時(shí)系統(tǒng)來(lái)說(shuō),異常故障導(dǎo)致看門狗復(fù)位系統(tǒng)隨時(shí)可能發(fā)生,基于文件系統(tǒng)進(jìn)行日志文件的讀寫訪問(wèn)被中斷,將可能引起文件系統(tǒng)的不一致性,導(dǎo)致文件系統(tǒng)被破壞和文件數(shù)據(jù)的丟失。因此,傳統(tǒng)的日志文件系統(tǒng)服務(wù)不完全適用于嵌入式實(shí)時(shí)系統(tǒng)的需求,嵌入式實(shí)時(shí)系統(tǒng)的日志服務(wù)需要具備以下特性:
(1) 獨(dú)立性,不依賴過(guò)多的外設(shè)與系統(tǒng)資源。
(2) 實(shí)時(shí)性,日志服務(wù)須要在看門狗復(fù)位系統(tǒng)前,在確定的時(shí)間內(nèi)將所有信息保存記錄下來(lái)[4]。
(3) 信息完整性,在記錄和讀取解析日志信息時(shí)須進(jìn)行校驗(yàn),保證日志的完整有效性。
銳華[19-20](ReWorks)嵌入式實(shí)時(shí)操作系統(tǒng)是上海華元?jiǎng)?chuàng)信軟件有限公司在國(guó)家科技重大專項(xiàng)課題“核心電子器件、高端通用芯片及基礎(chǔ)軟件產(chǎn)品”中定制發(fā)布的面向高安全工業(yè)控制領(lǐng)域的嵌入式實(shí)時(shí)操作系統(tǒng),支持PowerPC、X86、ARM、龍芯、飛騰、C-sky等主流處理器架構(gòu),目前廣泛應(yīng)用于國(guó)防電子、工業(yè)控制以及軌道交通領(lǐng)域。飛騰[21]FT-2000A/2是國(guó)防科技大學(xué)自主研發(fā)的國(guó)產(chǎn)高性能通用雙核微處理器,兼容ARMv8指令集,主要面向嵌入式裝備和工控領(lǐng)域應(yīng)用產(chǎn)品?;阡J華(ReWorks)嵌入式實(shí)時(shí)操作系統(tǒng)需提供可靠、有效的日志服務(wù),能在國(guó)產(chǎn)硬件平臺(tái)的應(yīng)用程序出現(xiàn)錯(cuò)誤,甚至程序崩潰系統(tǒng)異常時(shí),完整記錄錯(cuò)誤出現(xiàn)時(shí)間、異常任務(wù)上下文[22]和異常任務(wù)?;厮輀23]等程序故障的現(xiàn)場(chǎng)信息[24],并提供方便備份導(dǎo)出分析的方法。
日志服務(wù)主要包括:熱重啟后內(nèi)容不被破壞的內(nèi)存空間管理、異常與用戶信息日志記錄、日志查詢顯示和日志備份導(dǎo)出。ReWorks在FT2000A/2國(guó)產(chǎn)硬件平臺(tái)上的內(nèi)存日志服務(wù)總體架構(gòu)如圖1所示。
圖1 ReWorks內(nèi)存日志服務(wù)系統(tǒng)架構(gòu)
在系統(tǒng)初始化時(shí),通過(guò)日志內(nèi)存管理分配日志服務(wù)所需內(nèi)存空間,并設(shè)置非cache訪問(wèn),保證內(nèi)存讀寫一致性。在軟復(fù)位時(shí)設(shè)置內(nèi)存自刷新模式,保證在熱重啟的情況下該內(nèi)存空間內(nèi)的數(shù)據(jù)不會(huì)被破壞,為日志服務(wù)提供基礎(chǔ)保障。
出現(xiàn)異常故障時(shí),系統(tǒng)捕獲的異常信息與用戶添加的日志信息以及日志校驗(yàn)碼將寫入日志內(nèi)存中,當(dāng)發(fā)生嚴(yán)重系統(tǒng)錯(cuò)誤時(shí)看門狗將復(fù)位系統(tǒng),在不掉電的軟重啟過(guò)程中,日志信息仍保持在日志內(nèi)存中。通過(guò)日志顯示能夠按照日志的級(jí)別與類型、起始編號(hào)與條數(shù)查詢并顯示內(nèi)存中記錄的通過(guò)校驗(yàn)有效的日志信息,即使在系統(tǒng)軟重啟后,仍可查詢顯示。日志服務(wù)為用戶提供4種接口,分別是日志服務(wù)初始化、日志記錄、日志查詢顯示和日志備份。內(nèi)存日志服務(wù)工作原理設(shè)計(jì)如圖2所示。
圖2 日志服務(wù)工作原理設(shè)計(jì)圖
日志服務(wù)初始化內(nèi)容包括內(nèi)存地址和日志內(nèi)存數(shù)據(jù)結(jié)構(gòu),初始化之后即可使用日志記錄、顯示和備份功能。內(nèi)存日志服務(wù)的關(guān)鍵基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)是日志內(nèi)存環(huán)形資源池。日志內(nèi)存數(shù)據(jù)結(jié)構(gòu)主要包括日志內(nèi)存的魔數(shù)、單元大小、總空間、版本、初始化次數(shù)、重啟次數(shù)等基本配置參數(shù)和日志單元環(huán)形鏈表,日志內(nèi)存數(shù)據(jù)結(jié)構(gòu)如下:
struct err_log
{
u32 magic;/*日志魔數(shù)*/
u32 payload_size;/*日志單元大小*/
u32 size;/*日志內(nèi)存總大小*/
u32 os_version;/*操作系統(tǒng)版本*/
u32 gen_count;/*日志初始化次數(shù)*/
u32boot_count;/*系統(tǒng)軟重啟次數(shù)*/
ERR_LOG_NODE_LIST nodeList;/*日志單元鏈表*/
};
日志內(nèi)存環(huán)形鏈表由日志單元總數(shù)、空閑單元號(hào)、已記錄單元數(shù)、校驗(yàn)數(shù)和內(nèi)存日志單元節(jié)點(diǎn)構(gòu)成,日志內(nèi)存環(huán)形鏈表數(shù)據(jù)結(jié)構(gòu)如下:
typedef struct err_log_node_list
{
u32 max_node_count;/*最大節(jié)點(diǎn)數(shù)*/
u32 nextNode;/*空閑日志單元節(jié)點(diǎn)號(hào)*/
u32 nodeCount;/*已使用日志單元數(shù)*/
u32 check_sum;/*環(huán)形鏈表校驗(yàn)數(shù)*/
ERR_LOG_NODE node[1];/*日志單元節(jié)點(diǎn)*/
} ERR_LOG_NODE_LIST;
內(nèi)存日志單元包含單元節(jié)點(diǎn)狀態(tài)、位置、提交狀態(tài)、校驗(yàn)數(shù)和記錄日志數(shù)據(jù)存儲(chǔ)區(qū)入口,內(nèi)存日志單元數(shù)據(jù)結(jié)構(gòu)如下:
typedef struct err_log_node
{
u32 status;/*當(dāng)前日志單元節(jié)點(diǎn)狀態(tài)*/
u32 position;/*日志單元節(jié)點(diǎn)位置*/
u32 committed;/*單元提交狀態(tài)*/
u32 check_sum;/* 日志單元校驗(yàn)數(shù)*/
char data[1];/*日志單元記錄數(shù)據(jù)入口*/
} ERR_LOG_NODE;
日志服務(wù)首先初始化內(nèi)存日志數(shù)據(jù)結(jié)構(gòu)的基本信息,再建立由內(nèi)存日志單元構(gòu)成的環(huán)形鏈表而形成的資源池。記錄日志時(shí),先從資源池取出一個(gè)日志單元,根據(jù)入?yún)⑴渲们闆r保存日志基礎(chǔ)信息、異常信息、上下文信息、?;厮菪畔⒑陀脩粜畔⒌饺罩締卧挠涗洈?shù)據(jù)存儲(chǔ)區(qū)中,經(jīng)校驗(yàn)完整性后再提交到日志資源池。查詢顯示或備份導(dǎo)出內(nèi)存日志時(shí),都是通過(guò)日志數(shù)據(jù)單元遍歷迭代器,從日志內(nèi)存環(huán)形資源池中讀取符合相應(yīng)條件的日志單元中記錄的日志數(shù)據(jù)。
日志服務(wù)初始化時(shí)指定內(nèi)存日志地址和大小,并可選擇是否保留日志內(nèi)存中已有數(shù)據(jù)。初始化成功后,即可使用日志記錄系統(tǒng)運(yùn)行時(shí)的系統(tǒng)異常與用戶自定義日志信息。日志服務(wù)初始化的軟件工作流程如圖3所示。
圖3 日志服務(wù)初始化流程
日志記錄首先獲取當(dāng)前任務(wù)ID、CPU ID與日志地址;判斷日志內(nèi)存數(shù)據(jù)結(jié)構(gòu)有效后,申請(qǐng)一個(gè)日志記錄單元;再將獲取的日志級(jí)別與類型、時(shí)間、源文件名、行號(hào)、用戶自定義信息、CPU異常信息、上下文寄存器信息、任務(wù)?;厮菪畔⒌戎匾漠惓>€程信息插入日志內(nèi)存中。最后將生成當(dāng)前日志記錄單元的校驗(yàn)碼和日志記錄單元一并提交插入日志內(nèi)存環(huán)形鏈表,以此為信息完整性提供支撐。日志記錄的軟件工作流程如圖4所示。
圖4 日志記錄流程
日志顯示能夠按照日志的級(jí)別與類型、起始編號(hào)與條數(shù)查詢并顯示記錄的日志信息。日志顯示軟件流程如圖5所示。
圖5 日志顯示軟件流程
用戶可以根據(jù)日志內(nèi)存容量及實(shí)際情況,將內(nèi)存中的日志信息按照日志的起始編號(hào)與條數(shù)保存到文件系統(tǒng)中,為分析與歸檔系統(tǒng)運(yùn)行故障統(tǒng)計(jì)提供幫助,其設(shè)計(jì)流程與日志顯示類似。同時(shí),當(dāng)用戶選擇日志備份功能時(shí)還可以將掉電情況下的日志信息保存在硬盤、FLASH、SD卡等存儲(chǔ)設(shè)備中,實(shí)現(xiàn)掉電情況下的日志保存。
本文設(shè)計(jì)的銳華(ReWorks)嵌入式實(shí)時(shí)操作系統(tǒng)的內(nèi)存日志服務(wù),基于國(guó)產(chǎn)飛騰FT-2000A/2平臺(tái)模塊(主頻1 GHz/ 2 GB DDR/ 256 GB SSD)開(kāi)展日志服務(wù)功能測(cè)試。在日志服務(wù)初始化成功后,通過(guò)制造一個(gè)未定義指令異常,系統(tǒng)捕獲到ARM體系架構(gòu)的異常信息與用戶添加的日志信息將寫入日志內(nèi)存環(huán)形緩沖區(qū)中。通過(guò)日志顯示能夠查詢并顯示內(nèi)存中記錄的日志信息,如圖6所示。
圖6 日志信息顯示截圖
圖6中ReWorks操作系統(tǒng)Shell控制臺(tái)的日志信息顯示所示,日志管理信息包含:日志內(nèi)存總大小、單條日志大小、最大記錄條數(shù)、記錄日志數(shù)等;日志基礎(chǔ)信息包含:日志的等級(jí)、所屬模塊、啟動(dòng)次數(shù)、系統(tǒng)版本、CPU ID、日期時(shí)間、任務(wù)名和日志插入點(diǎn)等信息;用戶記錄信息包含內(nèi)容與長(zhǎng)度;異常信息描述包含:任務(wù)ID、異常號(hào)、中斷嵌套層數(shù)、異常名稱和錯(cuò)誤地址;上下文信息包含相關(guān)通用寄存器和狀態(tài)寄存器信息;?;厮菪畔t顯示的是出現(xiàn)問(wèn)題點(diǎn)的任務(wù)調(diào)用函數(shù)?;厮?。通過(guò)查看日志信息,可以很快定位到故障點(diǎn)在任務(wù)p3的函數(shù)exception_uni偏移0xc處,并分析出現(xiàn)問(wèn)題的原因是由于未定義指令“0xeeeeeeee”造成的,且即使在系統(tǒng)軟重啟后,仍可查詢顯示日志內(nèi)存中的該條日志信息。
由于內(nèi)存日志服務(wù)功能僅依賴CPU和內(nèi)存,且在記錄操作過(guò)程中不存在阻塞操作,因而能夠在中斷上下文中進(jìn)行日志記錄。用戶也可以備份內(nèi)存中記錄的日志信息到外部存儲(chǔ)的文件系統(tǒng)中,為分析與歸檔系統(tǒng)運(yùn)行故障統(tǒng)計(jì)提供幫助。
1) 實(shí)時(shí)性優(yōu)化。日志服務(wù)的記錄性能主要體現(xiàn)在記錄一條日志信息所消耗的時(shí)間?;贔T-2000A/2模塊開(kāi)展內(nèi)存日志服務(wù)性能測(cè)試,記錄各項(xiàng)日志信息進(jìn)行性能分析。
為便于分析內(nèi)存日志性能,初始化日志單元大小為4 096字節(jié),日志管理、日志基礎(chǔ)、異常、上下文寄存器和棧回溯信息內(nèi)容確定,所占空間大小相對(duì)固定。用戶信息從100字節(jié)起以100字節(jié)的步長(zhǎng)增加到8 000字節(jié),記錄所有信息內(nèi)存日志所需時(shí)間如圖7所示。可看出,當(dāng)記錄所有日志信息大小超過(guò)日志單元大小后,記錄時(shí)間仍然隨著用戶信息的增加而增大。該現(xiàn)象說(shuō)明記錄用戶信息大小的不確定將導(dǎo)致日志記錄時(shí)間的不確定,這將影響實(shí)時(shí)系統(tǒng)的確定性。
圖7 內(nèi)存日志記錄性能圖
經(jīng)分析調(diào)試,發(fā)現(xiàn)字符串拷貝函數(shù)strlcpy接口性能函數(shù)存在不收斂情況。為保證嵌入式系統(tǒng)接口調(diào)用的實(shí)時(shí)性和確定性,對(duì)字符拷貝函數(shù)strlcpy與strncpy接口性能做了對(duì)比,如圖8所示。
圖8 往4 096字節(jié)內(nèi)存拷貝不同大小內(nèi)存的性能對(duì)比
圖 8為針對(duì)目的地址內(nèi)存4 096字節(jié)大小,分別采用strlcpy和strncpy拷貝限制4 096字節(jié)長(zhǎng)度內(nèi)存,而源地址內(nèi)存大小從100字節(jié)以100字節(jié)的步長(zhǎng)增加到6 000字節(jié)??煽闯?,隨著拷貝源信息的增加,在限制長(zhǎng)度4 096字節(jié)之后,strncpy拷貝時(shí)間收斂,而strlcpy呈發(fā)散趨勢(shì),拷貝時(shí)間隨著源地址內(nèi)容的增大而增加。對(duì)于上述現(xiàn)象,分析如下:
strlcpy的原型為size_t strlcpy(char *dest, const char *src, size_t size)。當(dāng)size小于等于源地址src長(zhǎng)度時(shí),拷貝size-1個(gè)字符到目的地址dest中,并自動(dòng)在字符串末尾填充結(jié)束符“ 武城县| 天峨县| 浑源县| 淮阳县| 临沧市| 白朗县| 都兰县| 朝阳区| 崇左市| 延吉市| 肃宁县| 石家庄市| 大足县| 朝阳区| 孟津县| 新和县| 胶州市| 和平区| 孝义市| 三明市| 莱阳市| 商水县| 吉水县| 都江堰市| 同心县| 宾川县| 习水县| 万山特区| 日喀则市| 白朗县| 遂宁市| 潜山县| 普陀区| 咸阳市| 化州市| 达拉特旗| 山阳县| 抚顺市| 涪陵区| 蒲城县| 额敏县|