衡阳派盒市场营销有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

嵌入式軟件可靠性設(shè)計三方面的考慮:防錯,判錯,容錯的詳細概述

GReq_mcu168 ? 來源:未知 ? 作者:易水寒 ? 2018-06-18 12:40 ? 次閱讀

0.前言

設(shè)備的可靠性涉及多個方面:穩(wěn)定的硬件、優(yōu)秀的軟件架構(gòu)、嚴格的測試以及市場和時間的檢驗等等。這里著重談一下作者自己對嵌入式軟件可靠性設(shè)計的一些理解,通過一定的技巧和方法提高軟件可靠性。這里所說的嵌入式設(shè)備,是指使用單片機ARM7、Cortex-M0,M3之類為核心的測控或工控系統(tǒng)。

嵌入式軟件可靠性設(shè)計應(yīng)該從防錯、判錯和容錯三方面進行考慮. 此外,還需理解自己所使用的編譯器特性。

1.防錯

良好的軟件架構(gòu)、清晰的代碼結(jié)構(gòu)、掌握硬件、深入理解C語言是防錯的要點,這里只談一下C語言。

“人的思維和經(jīng)驗積累對軟件可靠性有很大影響"。C語言詭異且有種種陷阱和缺陷,需要程序員多年歷練才能達到較為完善的地步。“軟件的質(zhì)量是由程序員的質(zhì)量以及他們相互之間的協(xié)作決定的”。因此,作者認為防錯的重點是要考慮人的因素。

“深入一門語言編程,不要浮于表面”。軟件的可靠性,與你理解的語言深度密切相關(guān),嵌入式C更是如此。除了語言,作者認為嵌入式開發(fā)還必須深入理解編譯器。

本節(jié)將對C語言的陷阱和缺陷做初步探討。

1.1 處處皆陷阱

最初開始編程時,除了英文標點被誤寫成中文標點外,可能被大家普遍遇到的是將比較運算符==誤寫成賦值運算符=,代碼如下所示:

if(x=5) { … }

這里本意是比較變量x是否等于常量5,但是誤將’==’寫成了’=’,if語句恒為真。如果在邏輯判斷表達式中出現(xiàn)賦值運算符,現(xiàn)在的大多數(shù)編譯器會給出警告信息。并非所有程序員都會注意到這類警告,因此有經(jīng)驗的程序員使用下面的代碼來避免此類錯誤:

if(5==x) { … }

將常量放在變量x的左邊,即使程序員誤將’==’寫成了’=’,編譯器會產(chǎn)生一個任誰也不能無視的語法錯誤信息:不可給常量賦值!

+=與=+、-=與=-也是容易寫混的。復(fù)合賦值運算符(+=、*=等等)雖然可以使表達式更加簡潔并有可能產(chǎn)生更高效的機器代碼,但某些復(fù)合賦值運算符也會給程序帶來隱含Bug,如下所示代碼:

tmp=+1;

該代碼本意是想表達tmp=tmp+1,但是將復(fù)合賦值運算符+=誤寫成=+:將正整數(shù)常量1賦值給變量tmp。編譯器會欣然接受這類代碼,連警告都不會產(chǎn)生。

如果你能在調(diào)試階段就發(fā)現(xiàn)這個Bug,你真應(yīng)該慶祝一下,否則這很可能會成為一個重大隱含Bug,且不易被察覺。

-=與=-也是同樣道理。與之類似的還有邏輯與&&和位與&、邏輯或||和位或|、邏輯非!和位取反~。此外字母l和數(shù)字1、字母O和數(shù)字0也易混淆,這種情況可借助編譯器來糾正。

很多的軟件BUG自于輸入錯誤。在Google上搜索的時候,有些結(jié)果列表項中帶有一條警告,表明Google認為它帶有惡意代碼。如果你在2009年1月31日一大早使用Google搜索的話,你就會看到,在那天早晨55分鐘的時間內(nèi),Google的搜索結(jié)果標明每個站點對你的PC都是有害的。這涉及到整個Internet上的所有站點,包括Google自己的所有站點和服務(wù)。Google的惡意軟件檢測功能通過在一個已知攻擊者的列表上查找站點,從而識別出危險站點。在1月31日早晨,對這個列表的更新意外地包含了一條斜杠(“/”)。所有的URL都包含一條斜杠,并且,反惡意軟件功能把這條斜杠理解為所有的URL都是可疑的,因此,它愉快地對搜索結(jié)果中的每個站點都添加一條警告。很少見到如此簡單的一個輸入錯誤帶來的結(jié)果如此奇怪且影響如此廣泛,但程序就是這樣,容不得一絲疏忽。

數(shù)組常常也是引起程序不穩(wěn)定的重要因素,C語言數(shù)組的迷惑性與數(shù)組下標從0開始密不可分,你可以定義int a[30],但是你絕不可以使用數(shù)組元素a[30],除非你自己明確知道在做什么。

switch…case語句可以很方便的實現(xiàn)多分支結(jié)構(gòu),但要注意在合適的位置添加break關(guān)鍵字。程序員往往容易漏加break從而引起順序執(zhí)行多個case語句,這也許是C的一個缺陷之處。對于switch…case語句,從概率論上說,絕大多數(shù)程序一次只需執(zhí)行一個匹配的case語句,而每一個這樣的case語句后都必須跟一個break。去復(fù)雜化大概率事件,這多少有些不合常情。

break關(guān)鍵字用于跳出最近的那層循環(huán)語句或者switch語句,但程序員往往不夠重視這一點。

1990年1月15日,AT&T電話網(wǎng)絡(luò)位于紐約的一臺交換機當機并且重啟,引起它鄰近交換機癱瘓,由此及彼,一個連著一個,很快,114臺交換機每六秒當機重啟一次,六萬人九小時內(nèi)不能打長途電話。當時的解決方式:工程師重裝了以前的軟件版本。事后的事故調(diào)查發(fā)現(xiàn),這是break關(guān)鍵字誤用造成的?!禖專家編程》提供了一個簡化版的問題源碼:

networkcode()

{

switch(line){

caseTHING1:

doit1();

break;

caseTHING2:

if(x==STUFF){

do_first_stuff();

if(y==OTHER_STUFF)

break;

do_later_stuff();

}/*代碼的意圖是跳轉(zhuǎn)到這里……*/

initialize_modes_pointer();

break;

default:

processing();

}/*……但事實上跳到了這里。*/

use_modes_pointer();/*致使modes_pointer未初始化*/

}

那個程序員希望從if語句跳出,但他卻忘記了break關(guān)鍵字實際上跳出最近的那層循環(huán)語句或者switch語句?,F(xiàn)在它跳出了switch語句,執(zhí)行了use_modes_pointer()函數(shù)。但必要的初始化工作并未完成,為將來程序的失敗埋下了伏筆。

將一個整形常量賦值給變量,代碼如下所示:

int a=34, b=034;

變量a和b相等嗎?答案是不相等的。我們知道,16進制常量以’0x’為前綴,10進制常量不需要前綴,那么8進制呢?它與10進制和16進制表示方法都不相通,它以數(shù)字’0’為前綴,這多少有點奇葩:三種進制的表示方法完全不相通。如果8進制也像16進制那樣以數(shù)字和字母表示前綴的話,或許更有利于減少軟件Bug,畢竟你使用8進制的次數(shù)可能都不會有誤使用的次數(shù)多!下面展示一個誤用8進制的例子,最后一個數(shù)組元素賦值錯誤:

a[0]=106;/*十進制數(shù)106*/

a[1]=112;/*十進制數(shù)112*/

a[2]=052;/*實際為十進制數(shù)42,本意為十進制52*/

指針的加減運算是特殊的。下面的代碼運行在32位ARM架構(gòu)上,執(zhí)行之后,a和p的值分別是多少?

inta=1;

int*p=(int*)0x00001000;

a=a+1;

p=p+1;

對于a的值很容判斷出結(jié)果為2,但是p的結(jié)果卻是0x00001004。指針p加1后,p的值增加了4,這是為什么呢?原因是指針做加減運算時是以指針的數(shù)據(jù)類型為單位。p+1實際上是p+1*sizeof(int)。不理解這一點,在使用指針直接操作數(shù)據(jù)時極易犯錯。比如下面對連續(xù)RAM初始化零操作代碼:

unsignedint*pRAMaddr;//定義地址指針變量

for(pRAMaddr=StartAddr;pRAMaddr

{

*pRAMaddr=0x00000000;//指定RAM地址清零

}

由于pRAMaddr是一個指針變量,所以pRAMaddr+=4代碼其實使pRAMaddr偏移了4*sizeof(int)=16個字節(jié),所以每執(zhí)行一次for循環(huán),會使變量pRAMaddr偏移16個字節(jié)空間,但只有4字節(jié)空間被初始化為零。其它的12字節(jié)數(shù)據(jù)的內(nèi)容,在大多數(shù)架構(gòu)處理器中都會是隨機數(shù)。

對于sizeof(),這里強調(diào)兩點,第一它是一個關(guān)鍵字,而不是函數(shù),并且它默認返回無符號整形數(shù)據(jù)(要記住是無符號);第二,使用sizeof獲取數(shù)組長度時,不要對指針應(yīng)用sizeof操作符,比如下面的例子:

voidClearRAM(chararray[])

{

inti;

for(i=0;i

{

array[i]=0x00;

}

}

intmain(void)

{

charFle[20];

ClearRAM(Fle);//只能清除數(shù)組Fle中的前四個元素

}

我們知道,對于一個數(shù)組array[20],我們使用代碼sizeof(array)/sizeof(array[0])可以獲得數(shù)組的元素(這里為20),但數(shù)組名和指針往往是容易混淆的,而且有且只有一種情況下是可以當做指針的,那就是數(shù)組名作為函數(shù)形參時,數(shù)組名被認為是指針。同時,它不能再兼任數(shù)組名。注意只有這種情況下,數(shù)組名才可以當做指針,但不幸的是這種情況下容易引發(fā)風險。在ClearRAM函數(shù)內(nèi),作為形參的array[]不再是數(shù)組名了,而成了指針。sizeof(array)相當于求指針變量占用的字節(jié)數(shù),在32位系統(tǒng)下,該值為4,sizeof(array)/sizeof(array[0])的運算結(jié)果也為4。所以在main函數(shù)中調(diào)用ClearRAM(Fle),也只能清除數(shù)組Fle中的前四個元素了。

增量運算符++和減量運算符--既可以做前綴也可以做后綴。前綴和后綴的區(qū)別在于值的增加或減少這一動作發(fā)生的時間是不同的。作為前綴是先自加或自減然后做別的運算,作為后綴時,是先做運算,之后再自加或自減。許多程序員對此認識不夠,就容易埋下隱患。下面的例子可以很好的解釋前綴和后綴的區(qū)別。

inta=8,b=2,y;

y=a+++--b;

代碼執(zhí)行后,y的值是多少?

這個例子并非是挖空心思設(shè)計出來專門讓你絞盡腦汁的C難題(如果你覺得自己對C細節(jié)掌握很有信心,做一些C難題檢驗一下是個不錯的選擇。那么,《The C Puzzle Book》這本書一定不要錯過。),你甚至可以將這個難懂的語句作為不友好代碼的反面例子。但是它也可以讓你更好的理解C語言。根據(jù)運算符優(yōu)先級以及編譯器識別字符的貪心法原則,代碼y=a+++--b;可以寫成更明確的形式:

y=(a++)+(--b);

當賦值給變量y時,a的值為8,b的值為1,所以變量y的值為9;賦值完成后,變量a自加,a的值變?yōu)?,千萬不要以為y的值為10。這條賦值語句相當于下面的兩條語句:

y=a+(--b);

a=a+1;

1.2 玩具般的編譯器語義檢查

為了更簡單的設(shè)計編譯器,目前幾乎所有編譯器的語義檢查都比較弱小,加之為了獲得更快的執(zhí)行效率,C語言被設(shè)計的足夠靈活且?guī)缀醪贿M行任何運行時檢查,比如數(shù)組越界、指針是否合法、運算結(jié)果是否溢出等等。

C語言足夠靈活,對于一個數(shù)組a[30],它允許使用像a[-1]這樣的形式來快速獲取數(shù)組首元素所在地址前面的數(shù)據(jù);允許將一個常數(shù)強制轉(zhuǎn)換為函數(shù)指針,使用代碼(*((void(*)())0))()來調(diào)用位于0地址的函數(shù)。C語言給了程序員足夠的自由,但也由程序員承擔濫用自由帶來的責任。下面的兩個例子都是死循環(huán),如果在不常用分支中出現(xiàn)類似代碼,將會造成看似莫名其妙的死機或者重啟。

a. unsigned char i; b.unsigned chari;

for(i=0;i<256;i++)? {… }?????????????????for(i=10;i>=0;i--) { … }

對于無符號char類型,表示的范圍為0~255,所以無符號char類型變量i永遠小于256(第一個for循環(huán)無限執(zhí)行),永遠大于等于0(第二個for循環(huán)無線執(zhí)行)。需要說明的是,賦值代碼i=256是被C語言允許的,即使這個初值已經(jīng)超出了變量i可以表示的范圍。C語言會千方百計的為程序員創(chuàng)造出錯的機會,可見一斑。

假如你在if語句后誤加了一個分號改變了程序邏輯,編譯器也會很配合的幫忙掩蓋,甚至連警告都不提示。代碼如下:

if(a>b);//這里誤加了一個分號

a=b;//這句代碼一直被執(zhí)行

不但如此,編譯器還會忽略掉多余的空格符和換行符,就像下面的代碼也不會給出足夠提示:

if(n<3)??

return//這里少加了一個分號

logrec.data=x[0];

logrec.time=x[1];

logrec.code=x[2];

這段代碼的本意是n<3時程序直接返回,由于程序員的失誤,return少了一個結(jié)束分號。編譯器將它翻譯成返回表達式logrec.data=x[0]的結(jié)果,return后面即使是一個表達式也是C語言允許的。這樣當n>=3時,表達式logrec.data=x[0];就不會被執(zhí)行,給程序埋下了隱患。

可以毫不客氣的說,弱小的編譯器語義檢查在很大程度上縱容了不可靠代碼可以肆無忌憚的存在。

上文曾提到數(shù)組常常是引起程序不穩(wěn)定的重要因素,程序員往往不經(jīng)意間就會寫數(shù)組越界。一位同事的代碼在硬件上運行,一段時間后就會發(fā)現(xiàn)LCD顯示屏上的一個數(shù)字不正常的被改變。經(jīng)過一段時間的調(diào)試,問題被定位到下面的一段代碼中:

intSensorData[30];

…for(i=30;i>0;i--)

{

SensorData[i]=…;

}

這里聲明了擁有30個元素的數(shù)組,不幸的是for循環(huán)代碼中誤用了本不存在的數(shù)組元素SensorData[30],但C語言卻默許這么使用,并欣然的按照代碼改變了數(shù)組元素SensorData[30]所在位置的值, SensorData[30]所在的位置原本是一個LCD顯示變量,這正是顯示屏上的那個值不正常被改變的原因。真慶幸這么輕而易舉的發(fā)現(xiàn)了這個Bug。

其實很多編譯器會對上述代碼產(chǎn)生一個警告:賦值超出數(shù)組界限。但并非所有程序員都對編譯器警告保持足夠敏感,況且,編譯器也并不能檢查出數(shù)組越界的所有情況。舉一個例子,你在模塊A中定義數(shù)組:

int SensorData[30];

在模塊B中引用該數(shù)組,但由于你引用代碼并不規(guī)范,這里沒有顯示聲明數(shù)組大小,但編譯器也允許這么做:

extern int SensorData[];

如果在模塊B中存在和上面一樣的代碼:

for(i=30;i>0;i--)

{

SensorData[i]=…;

}

這次,編譯器不會給出警告信息,因為編譯器壓根就不知道數(shù)組的元素個數(shù)。所以,當一個數(shù)組聲明為具有外部鏈接,它的大小應(yīng)該顯式聲明。

再舉一個編譯器檢查不出數(shù)組越界的例子。函數(shù)func()的形參是一個數(shù)組形式,函數(shù)代碼簡化如下所示:

char*func(charSensorData[30])

{

unsignedinti;

for(i=30;i>0;i--)

{

SensorData[i]=…;

}

}

這個給SensorData[30]賦初值的語句,編譯器也是不給任何警告的。實際上,編譯器是將數(shù)組名Sensor隱含的轉(zhuǎn)化為指向數(shù)組第一個元素的指針,函數(shù)體是使用指針的形式來訪問數(shù)組的,它當然也不會知道數(shù)組元素的個數(shù)了。造成這種局面的原因之一是C編譯器的作者們認為指針代替數(shù)組可以提高程序效率,而且,還可以簡化編譯器的復(fù)雜度。

指針和數(shù)組是容易給程序造成混亂的,我們有必要仔細的區(qū)分它們的不同。其實換一個角度想想,它們也是容易區(qū)分的:可以將數(shù)組名等同于指針的情況有且只有一處,就是上面例子提到的數(shù)組作為函數(shù)形參時。其它時候,數(shù)組名是數(shù)組名,指針是指針。

下面的例子編譯器同樣檢查不出數(shù)組越界。

我們常常用數(shù)組來緩存通訊中的一幀數(shù)據(jù)。在通訊中斷中將接收的數(shù)據(jù)保存到數(shù)組中,直到一幀數(shù)據(jù)完全接收后再進行處理。即使定義的數(shù)組長度足夠長,接收數(shù)據(jù)的過程中也可能發(fā)生數(shù)組越界,特別是干擾嚴重時。這是由于外界的干擾破壞了數(shù)據(jù)幀的某些位,對一幀的數(shù)據(jù)長度判斷錯誤,接收的數(shù)據(jù)超出數(shù)組范圍,多余的數(shù)據(jù)改寫與數(shù)組相鄰的變量,造成系統(tǒng)崩潰。由于中斷事件的異步性,這類數(shù)組越界編譯器無法檢查到。

如果局部數(shù)組越界,可能引發(fā)ARM架構(gòu)硬件異常。同事的一個設(shè)備用于接收無線傳感器的數(shù)據(jù),一次軟件升級后,發(fā)現(xiàn)接收設(shè)備工作一段時間后會死機。調(diào)試表明ARM7處理器發(fā)生了硬件異常,異常處理代碼是一段死循環(huán)(死機的直接原因)。接收設(shè)備有一個硬件模塊用于接收無線傳感器的整包數(shù)據(jù)并存在自己的硬件緩沖區(qū)中,當一幀數(shù)據(jù)接收完成后,使用外部中斷通知設(shè)備取數(shù)據(jù),外部中斷服務(wù)程序精簡后如下所示:

__irqExintHandler(void)

{

unsignedcharDataBuf[50];

GetData(DataBug);//從硬件緩沖區(qū)取一幀數(shù)據(jù)

}

由于存在多個無線傳感器近乎同時發(fā)送數(shù)據(jù)的可能加之GetData()函數(shù)保護力度不夠,數(shù)組DataBuf在取數(shù)據(jù)過程中發(fā)生越界。由于數(shù)組DataBuf為局部變量,被分配在堆棧中,同在此堆棧中的還有中斷發(fā)生時的運行環(huán)境以及中斷返回地址。溢出的數(shù)據(jù)將這些數(shù)據(jù)破壞掉,中斷返回時PC指針可能變成一個不合法值,硬件異常由此產(chǎn)生。

如果我們精心設(shè)計溢出部分的數(shù)據(jù),化數(shù)據(jù)為指令,就可以利用數(shù)組越界來修改PC指針的值,使之指向我們希望執(zhí)行的代碼。1988年,第一個網(wǎng)絡(luò)蠕蟲在一天之內(nèi)感染了2000到6000臺計算機,這個蠕蟲程序利用的正是一個標準輸入庫函數(shù)的數(shù)組越界Bug。起因是一個標準輸入輸出庫函數(shù)gets(),原來設(shè)計為從數(shù)據(jù)流中獲取一段文本,遺憾的是,gets()函數(shù)沒有規(guī)定輸入文本的長度。gets()函數(shù)內(nèi)部定義了一個500字節(jié)的數(shù)組,攻擊者發(fā)送了大于500字節(jié)的數(shù)據(jù),利用溢出的數(shù)據(jù)修改了堆棧中的PC指針,從而獲取了系統(tǒng)權(quán)限。

一個程序模塊通常由兩個文件組成,源文件和頭文件。如果你在源文件定義變量:

unsigned int a;

并在頭文件中聲明該變量:extern unsigned long a;

編譯器會提示一個語法錯誤:變量’a’聲明類型不一致。但如果你在源文件定義變量:

volatile unsigned int a,

在頭文件中聲明變量:extern unsigned int a; /*缺少volatile限定符*/

編譯器卻不會給出錯誤信息(有些編譯器僅給出一條警告)。這里volatile屬于類型限定符,另一個常見的類型限定符是const關(guān)鍵字。限定符volatile在嵌入式軟件中至關(guān)重要,用來告訴編譯器不要優(yōu)化它修飾的變量。這里舉一個刻意構(gòu)造出的例子,因為現(xiàn)實中的volatile使用Bug大都隱含且難以理解。

在模塊A的源文件中,定義變量:

volatile unsigned int TimerCount=0;

該變量用來在一個定時器服務(wù)程序中進行軟件計時:

TimerCount++; //讀取IO端口1的值

在模塊A的頭文件中,聲明變量:

extern unsigned int TimerCount; //這里漏掉了類型限定符volatile

在模塊B中,要使用TimerCount變量進行精確的軟件延時:

#include“...A.h”//首先包含模塊A的頭文件

TimerCount=0;

while(TimerCount>=TIMER_VALUE);//延時一段時間

實際上,這是一個死循環(huán)。由于模塊A頭文件中聲明變量TimerCount時漏掉了volatile限定符,在模塊B中,變量TimerCount是被當作unsigned int類型變量。由于寄存器速度遠快于RAM,編譯器在使用非volatile限定變量時是先將變量從RAM中拷貝到寄存器中,如果同一個代碼塊再次用到該變量,就不再從RAM中拷貝數(shù)據(jù)而是直接使用之前寄存器備份值。代碼while(TimerCount>=TIMER_VALUE)中,變量TimerCount僅第一次執(zhí)行時被使用,之后都是使用的寄存器備份值,而這個寄存器值一直為0,所以程序無限循環(huán)。下面的流程圖說明了程序使用限定符volatile和不使用volatile的執(zhí)行過程。

嵌入式軟件可靠性設(shè)計三方面的考慮:防錯,判錯,容錯的詳細概述

ARM架構(gòu)下的編譯器會頻繁的使用堆棧,堆棧用于存儲函數(shù)的返回值、AAPCS規(guī)定的必須保護的寄存器以及局部變量,包括局部數(shù)組、結(jié)構(gòu)體、聯(lián)合體和C++的類。從堆棧中分配的局部變量的初值是不確定的,因此需要運行時顯式初始化該變量。一旦離開局部變量的作用域,這個變量立即被釋放,其它代碼也就可以使用它,因此堆棧中的一個內(nèi)存位置可能對應(yīng)整個程序的多個變量。

局部變量必須顯式初始化,除非你確定知道你要做什么。下面的代碼得到的溫度值跟預(yù)期會有很大差別,因為在使用局部變量sum時,并不能保證它的初值為0。編譯器會在第一次運行時清零堆棧區(qū)域,這加重了此類Bug的隱蔽性。

unsignedintGetTempValue(void)

{

unsignedintsum;//定義局部變量,保存總值

for(i=0;i<10;i++)??

{

sum+=CollectTemp();//函數(shù)CollectTemp可以得到當前的溫度值

}

return(sum/10);

}

由于一旦程序離開局部變量的作用域即被釋放,所以下面代碼返回指向局部變量的指針是沒有實際意義的,該指針指向的區(qū)域可能會被其它程序使用,其值會被改變。

char*GetData(void)

{

charbuffer[100];//局部數(shù)組

returnbuffer;

}

讓人欣慰的是,現(xiàn)在越來越多的編譯器意識到了語義檢查的重要性,編譯器的語義檢查也越來越強大,比如著名的Keil MDK編譯器在其 V4.47或以上版本中增加了動態(tài)語法檢查并加強了語義檢查,可以友好的提示更多警告信息。

1.3 不合理的優(yōu)先級

C語言有32個關(guān)鍵字,卻有34個運算符。要記住所有運算符的優(yōu)先級是困難的。不合理的#define會加重優(yōu)先級問題,讓問題變得更加隱蔽。

#defineREADSDAIO0PIN&(1<<11)????????????//定義宏,讀IO口p0.11的端口狀態(tài)??

//判斷端口p0.11是否為高電平

if(READSDA==(1<<11))????

{

}

編譯器在編譯后將宏帶入,原if語句變?yōu)?

if(IO0PIN&(1<<11)?==(1<<11))??

{

}

運算符'=='的優(yōu)先級是大于'&'的,代碼IO0PIN&(1<<11)?==(1<<11))等效為IO0PIN&0x00000001:判斷端口P0.0是否為高電平,這與原意相差甚遠。

為了制造更多的軟件Bug,C語言的運算符當然不會只止步于數(shù)目繁多。在此基礎(chǔ)上,按照常規(guī)方式使用時,可能引起誤會的運算符更是比比皆是!如下表所示:

常被誤會的
優(yōu)先級
表達式 常被誤認為: 其實是:
取值運算符*與自增運算符++優(yōu)先級相同,但它們是自右向左結(jié)合 *p++ (*p)++ *(p++)
成員選擇運算符.高于取值運算符* *p.f (*p).f *(p.f)
數(shù)組下標運算符[]優(yōu)先級高于取值運算符* int *ap[] int (*ap)[]
ap為數(shù)組指針
int *(ap[])
ap為指針數(shù)組
函數(shù)()優(yōu)先級高于取值運算符* int * fp() int (*fp)()
fp為函數(shù)指針
int * (fp())
fp為函數(shù),返回指針
等于==和不等于!=運算符優(yōu)先級高于位操作運算符&、^ 和 | val & mask != 0 (val & mask)!= 0 val &(mask != 0)
等于==和不等于!=運算符高于賦值運算符= c=getchar()!=EOF (c=getchar())!=EOF c=(getchar()!=EOF)
算數(shù)運算符+和-優(yōu)先級高于移位運算符<<和>> msb<<4+lsb (msb<<4)+lsb msb<<(4+lsb)

1.4 隱式轉(zhuǎn)換和強制轉(zhuǎn)換

這又是C語言的一大詭異之處,它造成的危害程度與數(shù)組和指針有的一拼。語句或表達式通常應(yīng)該只使用一種類型的變量和常量。然而,如果你混合使用類型,C使用一個規(guī)則集合來自動完成類型轉(zhuǎn)換。這可能很方便,但也很危險。

a.當出現(xiàn)在表達式里時,有符號和無符號的char和short類型都將自動被轉(zhuǎn)換為int類型,在需要的情況下,將自動被轉(zhuǎn)換為unsigned int(在short和int具有相同大小時)。這稱為類型提升。提升在算數(shù)運算中通常不會有什么大的壞處,但如果位運算符 ~ 和 << 應(yīng)用在基本類型為unsigned char或unsigned short 的操作數(shù),結(jié)果應(yīng)該立即強制轉(zhuǎn)換為unsigned char或者unsigned short類型(取決于操作時使用的類型)。

uint8_tport=0x5aU;

uint8_tresult_8;

result_8=(~port)>>4;

假如我們不了解表達式里的類型提升,認為在運算過程中變量port一直是unsigned char類型的。我們來看一下運算過程:~port結(jié)果為0xa5,0xa5>>4結(jié)果為0x0a,這是我們期望的值。但實際上,result_8的結(jié)果卻是0xfa!在ARM結(jié)構(gòu)下,int類型為32位。變量port在運算前被提升為int類型:~port結(jié)果為0xffffffa5,0xa5>>4結(jié)果為0x0ffffffa,賦值給變量result_8,發(fā)生類型截斷(這也是隱式的?。瑀esult_8=0xfa。經(jīng)過這么詭異的隱式轉(zhuǎn)換,結(jié)果跟我們期望的值,已經(jīng)大相徑庭!正確的表達式語句應(yīng)該為:

result_8=(unsigned char) (~port) >> 4;/*強制轉(zhuǎn)換*/

b.在包含兩種數(shù)據(jù)類型的任何運算里,兩個值都會被轉(zhuǎn)換成兩種類型里較高的級別。類型級別從高到低的順序是long double、double、float、unsigned long long、long long、unsigned long、long、unsigned int、int。這種類型提升通常都是件好事,但往往有很多程序員不能真正理解這句話,從而做一些想當然的事情,比如下面的例子,int類型表示16位。

uint16_tu16a=40000;/*16位無符號變量*/

uint16_tu16b=30000;/*16位無符號變量*/

uint32_tu32x;/*32位無符號變量*/

uint32_tu32y;

u32x=u16a+u16b;/*u32x=70000還是4464?*/

u32y=(uint32_t)(u16a+u16b);/*u32y=70000還是4464?*/

u32x和u32y的結(jié)果都是4464(70000%65536)!不要認為表達式中有一個高類別uint32_t類型變量,編譯器都會幫你把所有其他低類別都提升到uint32_t類型。正確的書寫方式:

u32x=(uint32_t)u16a+(uint32_t)u16b;或者:

u32x=(uint32_t)u16a+u16b;

后一種寫法在本表達式中是正確的,但是在其它表達式中不一定正確,比如:

uint16_tu16a,u16b,u16c;

uint32_tu32x;

u32x=u16a+u16b+(uint32_t)u16c;/*錯誤寫法,u16a+u16b仍可能溢出*/

c.在賦值語句里,計算的最后結(jié)果被轉(zhuǎn)換成將要被賦予值得那個變量的類型。這一過程可能導(dǎo)致類型提升也可能導(dǎo)致類型降級。降級可能會導(dǎo)致問題。比如將運算結(jié)果為321的值賦值給8位char類型變量。程序必須對運算時的數(shù)據(jù)溢出做合理的處理。

很多其他語言,像Pascal語言(好笑的是C語言設(shè)計者之一曾撰文狠狠批評過Pascal語言),都不允許混合使用類型,但C語言不會限制你的自由,即便這經(jīng)常引起B(yǎng)ug。

d.當作為函數(shù)的參數(shù)被傳遞時,char和short會被轉(zhuǎn)換為int,float會被轉(zhuǎn)換為double。

e.C語言支持強制類型轉(zhuǎn)換,如果你必須要進行強制類型轉(zhuǎn)換時,要確保你對類型轉(zhuǎn)換有足夠了解:

并非所有強制類型轉(zhuǎn)換都是由風險的,把一個整數(shù)值轉(zhuǎn)換為一種具有相同符號的更寬類型時,是絕對安全的。

精度高的類型強制轉(zhuǎn)換為精度低的類型時,通過丟棄適當數(shù)量的最高有效位來獲取結(jié)果,也就是說會發(fā)生數(shù)據(jù)截斷,并且可能改變數(shù)據(jù)的符號位。

精度低的類型強制轉(zhuǎn)換為精度高的類型時,如果兩種類型具有相同的符號,那么沒什么問題;需要注意的是負的有符號精度低類型強制轉(zhuǎn)換為無符號精度高類型時,會不直觀的執(zhí)行符號擴展,例如:

unsignedintbob;

signedcharfred=-1;

bob=(unsignedint)fred;/*發(fā)生符號擴展,此時bob為0xFFFFFFFF*/

一些編程建議:

深入理解嵌入式C語言以及編譯器

細致、謹慎的編程

使用好的風格和合理的設(shè)計

不要倉促編寫代碼,寫每一行的代碼時都要三思而后行:可能會出現(xiàn)什么樣的錯誤?是否考慮了所有的邏輯分支?

打開編譯器所有警告開關(guān)

使用靜態(tài)分析工具分析代碼

安全的讀寫數(shù)據(jù)(檢查所有數(shù)組邊界…)

檢查指針的合法性

檢查函數(shù)入口參數(shù)合法性

檢查所有返回值

在聲明變量位置初始化所有變量

合理的使用括號

謹慎的進行強制轉(zhuǎn)換

使用好的診斷信息日志和工具

2. 判錯

工欲善其事必先利其器。判錯的最終目的是用來暴露設(shè)計中的Bug并加以改正,所以將錯誤信息提供給編程者是必要的。有時候需要將故障信息儲存于非易失性存儲器中,便于查看。這里以使用串口打印錯誤信息到PC顯示屏為例,來說明一般需要顯示什么信息。

編寫或移植一個類似C標準庫中的printf函數(shù),可以格式化打印字符、字符串、十進制整數(shù)、十六進制整數(shù)。這里稱為UARTprintf()。

unsignedintWriteData(unsignedintaddr)

{

if((addr>=BASE_ADDR)&&(addr<=END_ADDR))?{??

…/*地址合法,進行處理*/

}else{/*地址錯誤,打印錯誤信息*/

UARTprintf("文件%s的第%d行寫數(shù)據(jù)時發(fā)生地址錯誤,錯誤地址為:0x%x\n",__FILE__,__LINE__,addr);

…/*錯誤處理代碼*/

}

假設(shè)UARTprintf()函數(shù)位于main.c模塊的第256行,并且WriteData()函數(shù)在讀數(shù)據(jù)時傳遞了錯誤地址0x00000011,則會執(zhí)行UARTprintf()函數(shù),打印如下所示的信息:

文件main.c的第256行寫數(shù)據(jù)時發(fā)生地址錯誤,錯誤地址為:0x00000011。

類似這樣的信息會有助于程序員定位分析錯誤產(chǎn)生的根源,更快的消除Bug。

2.1具有形參的函數(shù),需判斷傳遞來的實參是否合法。

程序員可能無意識的傳遞了錯誤參數(shù);外界的強干擾可能將傳遞的參數(shù)修改掉,或者使用隨機參數(shù)意外的調(diào)用函數(shù),因此在執(zhí)行函數(shù)主體前,需要先確定實參是否合法。

intexam_fun(unsignedchar*str)

{

if(str!=NULL){//檢查“假設(shè)指針不為空”這個條件

...//正常處理代碼

}else{

UARTprintf(…);//打印錯誤信息

…//處理錯誤代碼

}

}

2.2 仔細檢查函數(shù)的返回值

對函數(shù)返回的錯誤碼,要進行全面仔細處理,必要時做錯誤記錄。

char*DoSomething(…)

{

char*p;

p=malloc(1024);

if(p==NULL){/*對函數(shù)返回值作出判斷*/

UARTprintf(…);/*打印錯誤信息*/

returnNULL;

}

retuenp;

}

2.3 防止指針越界

如果動態(tài)計算一個地址時,要保證被計算的地址是合理的并指向某個有意義的地方。特別對于指向一個結(jié)構(gòu)或數(shù)組的內(nèi)部的指針,當指針增加或者改變后仍然指向同一個結(jié)構(gòu)或數(shù)組。

2.4 防止數(shù)組越界

數(shù)組越界的問題前文已經(jīng)講述的很多了,由于C不會對數(shù)組進行有效的檢測,因此必須在應(yīng)用中顯式的檢測數(shù)組越界問題。下面的例子可用于中斷接收通訊數(shù)據(jù)。

#defineREC_BUF_LEN100

unsignedcharRecBuf[REC_BUF_LEN];

…//其它代碼

voidUart_IRQHandler(void)

{

staticRecCount=0;//接收數(shù)據(jù)長度計數(shù)器

…//其它代碼

if(RecCount

RecBuf[RecCount]=…;//從硬件取數(shù)據(jù)

RecCount++;

…//其它代碼

}else{

UARTprintf(…);//打印錯誤信息

…//其它錯誤處理代碼

}

}

在使用一些庫函數(shù)時,同樣需要對邊界進行檢查:

#defineREC_BUF_LEN100

unsignedcharRecBuf[REC_BUF_LEN];

if(len

memset(RecBuf,0,len);//將數(shù)組RecBuf清零

}else{

//處理錯誤

}

2.5 數(shù)學(xué)算數(shù)運算

檢測除數(shù)是否為零

檢測運算溢出情況

2.5.1 有符號整數(shù)除法,僅檢測除數(shù)為零就夠了嗎?

兩個整數(shù)相除,除了要檢測除數(shù)是否為零外,還要檢測除法是否溢出。對于一個signed long類型變量,它能表示的數(shù)值范圍為:-2147483648 ~ +2147483647,如果讓-2147483648 / -1,那么結(jié)果應(yīng)該是+ 2147483648,但是這個結(jié)果已經(jīng)超出了signed long所能表示的范圍了。

#include

signedlongsl1,sl2,result;

/*初始化sl1和sl2*/

if((sl2==0)||((sl1==LONG_MIN)&&(sl2==-1))){

//處理錯誤

}else{

result=sl1/sl2;

}

2.5.2 加法溢出檢測

a)無符號加法

#include

unsignedinta,b,result;

/*初始化a,b*/

if(UINT_MAX-a

//處理溢出

}else{

result=a+b;

}

b)有符號加法

#include

signedinta,b,result;

/*初始化a,b*/

if((a>0&&INT_MAX-ab)){

//處理溢出

}else{

result=a+b;

}

2.5.3 乘法溢出檢測

a)無符號乘法

#include

unsignedinta,b,result;

/*初始化a,b*/

if((a!=0)&&(UINT_MAX/a

//

}else{

result=a*b;

}

b)有符號乘法

#include

signedinta,b,tmp,result;

/*初始化a,b*/

tmp=a*b;

if(a!=0&&tmp/a!=b){

//

}else{

result=tmp;

}

檢測移位時丟失有效位

2.6 其它可能出現(xiàn)運行時錯誤的地方

運行時錯誤檢查是C 程序員需要加以特別的注意的,這是因為C語言在提供任何運行時檢測方面能力較弱。對于要求可靠性較高的軟件來說,動態(tài)檢測是必需的。因此C 程序員需要謹慎考慮的問題是,在任何可能出現(xiàn)運行時錯誤的地方增加代碼的動態(tài)檢測。大多數(shù)的動態(tài)檢測與應(yīng)用緊密相關(guān),在程序設(shè)計過程中要根據(jù)系統(tǒng)需求設(shè)置動態(tài)代碼檢測。

3.容錯

1980年,美蘇尚處于冷戰(zhàn)階段。這年,北美防空聯(lián)合司令部曾報告稱美國遭受導(dǎo)彈襲擊。后來證實,這是反饋系統(tǒng)電路故障問題,但反饋系統(tǒng)軟件沒有考慮故障問題引發(fā)的誤報。

3.1 關(guān)鍵數(shù)據(jù)多區(qū)備份,取數(shù)據(jù)采用“表決法”

RAM中的數(shù)據(jù)在受到干擾情況下有可能被改變,對于系統(tǒng)關(guān)鍵數(shù)據(jù)必須進行保護。關(guān)鍵數(shù)據(jù)包括全局變量、靜態(tài)變量以及需要保護的數(shù)據(jù)區(qū)域。數(shù)據(jù)備份與原數(shù)據(jù)不應(yīng)該處于相鄰位置,因此不應(yīng)由編譯器默認分配備份數(shù)據(jù)位置,而應(yīng)該由程序員指定區(qū)域存儲??梢詫AM分為3個區(qū)域,第一個區(qū)域保存原碼,第二個區(qū)域保存反碼,第三個區(qū)域保存異或碼,區(qū)域之間預(yù)留一定量的“空白”RAM作為隔離。可以使用編譯器的“分散加載”機制將變量分別存儲在這些區(qū)域。需要進行讀取時,同時讀出3份數(shù)據(jù)并進行表決,取至少有兩個相同的那個值。

3.2 非易失性存儲器的數(shù)據(jù)存儲

非易失性存儲器包括但不限于Flash、EEPROM、鐵電。僅僅將寫入非易失性存儲器中的數(shù)據(jù)再讀出校驗是不夠的。強干擾情況下可能導(dǎo)致非易失性存儲器內(nèi)的數(shù)據(jù)錯誤,在寫非易失性存儲器的期間系統(tǒng)掉電將導(dǎo)致數(shù)據(jù)丟失,因干擾導(dǎo)致程序跑飛到寫非易失性存儲器函數(shù)中,將導(dǎo)致數(shù)據(jù)存儲紊亂。一種可靠的辦法是將非易失性存儲器分成多個區(qū),每個數(shù)據(jù)都將按照不同的形式寫入到這些分區(qū)中,需要進行讀取時,同時讀出多份數(shù)據(jù)并進行表決,取相同數(shù)目較多的那個值。

對于因干擾導(dǎo)致程序跑飛到寫非易失性存儲器函數(shù),還應(yīng)該配合軟件鎖以及嚴格的入口檢驗,單單依靠寫數(shù)據(jù)到多個區(qū)是不夠的也是不明智的,應(yīng)該在源頭進行阻截。

3.3 軟件鎖

軟件鎖可以實現(xiàn)但不局限于環(huán)環(huán)相扣。對于初始化序列或者有一定先后順序的函數(shù)調(diào)用,為了保證調(diào)用順序或者確保每個函數(shù)都被調(diào)用,我們可以使用環(huán)環(huán)相扣,實質(zhì)上這也是一種軟件鎖。此外對于一些安全關(guān)鍵代碼語句(是語句,而不是函數(shù)),可以給它們設(shè)置軟件鎖,只有持有特定鑰匙的,才可以訪問這些關(guān)鍵代碼。比如,向Flash寫一個數(shù)據(jù),我們會判斷數(shù)據(jù)是否合法、寫入的地址是否合法,計算要寫入的扇區(qū)。之后調(diào)用寫Flash子程序,在這個子程序中,判斷扇區(qū)地址是否合法、數(shù)據(jù)長度是否合法,之后就要將數(shù)據(jù)寫入Flash。由于寫Flash語句是安全關(guān)鍵代碼,所以程序給這些語句上鎖:必須具有正確的鑰匙才可以寫Flash。這樣即使是程序跑飛到寫Flash子程序,也能大大降低誤寫的風險。

3.4 通信數(shù)據(jù)的檢錯

通訊線上的數(shù)據(jù)誤碼相對嚴重,通訊線越長,所處的環(huán)境越惡劣,誤碼會越嚴重。通訊數(shù)據(jù)除了傳統(tǒng)的硬件奇偶校驗外,還應(yīng)該增加軟件CRC校驗。超過16字節(jié)的數(shù)據(jù)應(yīng)至少使用CRC16。在通訊過程中,如果檢測到發(fā)生了數(shù)據(jù)錯誤,則要求重新發(fā)送當前幀數(shù)據(jù)。

3.5 開關(guān)量輸入的檢測、確認

開關(guān)量容易受到尖脈沖干擾,如果不進行濾除,可能會造成誤動作。一般情況下,需要對開關(guān)量輸入信號進行多次采樣,并進行邏輯判斷直到確認信號無誤為止。多次采樣之間需要有一定時間間隔,具體跟開關(guān)量的最大切換頻率有關(guān),一般不小于1ms。

3.6 開關(guān)量輸出

開關(guān)信號簡單的一次輸出是不安全的,干擾信號可能會翻轉(zhuǎn)開關(guān)量輸出的狀態(tài)。采取重復(fù)刷新輸出可以有效防止電平的翻轉(zhuǎn)。

3.7 初始化信息的保存與恢復(fù)

微處理器的寄存器值也可能會因外界干擾而改變,外設(shè)初始化值需要在寄存器中長期保存,最容易被破壞。由于Flash中的數(shù)據(jù)相對不易被破壞,可以將初始化信息預(yù)先寫入Flash,待程序空閑時比較與初始化相關(guān)的寄存器值是否被更改,如果發(fā)現(xiàn)非法更改則使用Flash中的值進行恢復(fù)。

3.8 陷阱

對于8051內(nèi)核單片機,由于沒有相應(yīng)的硬件支持,可以用純軟件設(shè)置軟件陷阱,用來攔截一些程序跑飛。對于ARM7或者Cortex-M系列單片機,硬件已經(jīng)內(nèi)建了多種異常,軟件需要根據(jù)硬件異常來編寫陷阱程序,用來快速定位甚至恢復(fù)錯誤。

3.9 while循環(huán)

有時候程序員會使用while(!flag);語句來等待標志flag改變,比如串口發(fā)送時用來等待一字節(jié)數(shù)據(jù)發(fā)送完成。這樣的代碼時存在風險的,如果因為某些原因標志位一直不改變則會造成系統(tǒng)死機。良好冗余的程序是設(shè)置一個超時定時器,超過一定時間后,強制程序退出while循環(huán)。

2003年8月11日發(fā)生的W32.Blaster.Worm蠕蟲事件導(dǎo)致全球經(jīng)濟損失高達5億美元,這個漏洞是利用了Windows分布式組件對象模型的遠程過程調(diào)用接口中的一個邏輯缺陷:在調(diào)用GetMachineName()函數(shù)時,循環(huán)只設(shè)置了一個不充分的結(jié)束條件。

原代碼簡化如下所示:

HRESULTGetMachineName(WCHAR*pwszPath,

WCHARwszMachineName[MAX_COMPUTTERNAME_LENGTH_FQDN+1])

{

WCHAR*pwszServerName=wszMachineName;

WCHAR*pwszTemp=pwszPath+2;

while(*pwszTemp!=L’\\’)/*這句代碼循環(huán)結(jié)束條件不充分*/

*pwszServerName++=*pwszTemp++;

/*…*/

}

微軟發(fā)布的安全補丁MS03-026解決了這個問題,為GetMachineName()函數(shù)設(shè)置了充分終止條件。一個解決代碼簡化如下所示(并非微軟補丁代碼):

HRESULTGetMachineName(WCHAR*pwszPath,

WCHARwszMachineName[MAX_COMPUTTERNAME_LENGTH_FQDN+1])

{

WCHAR*pwszServerName=wszMachineName;

WCHAR*pwszTemp=pwszPath+2;

WCHAR*end_addr=pwszServerName+MAX_COMPUTTERNAME_LENGTH_FQDN;

while((*pwszTemp!=L’\\’)&&(*pwszTemp!=L’\0’)

&&(pwszServerName

*pwszServerName++=*pwszTemp++;

/*…*/

}

3.10 系統(tǒng)自檢

CPU、RAM、Flash、外部掉電保存存儲器以及其他線路自檢。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • C語言
    +關(guān)注

    關(guān)注

    180

    文章

    7614

    瀏覽量

    137712
  • 嵌入式軟件
    +關(guān)注

    關(guān)注

    4

    文章

    240

    瀏覽量

    26729
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1642

    瀏覽量

    49286

原文標題:嵌入式軟件可靠性設(shè)計的一些理解

文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    資深電子工程師分享:嵌入式軟件可靠性設(shè)計注意的問題

    的正確影響大的話,還需要對端口數(shù)據(jù)的正確進行檢查和判斷。嵌入式軟件可靠性編程方法的四個目的是
    發(fā)表于 10-11 14:28

    嵌入式軟件可靠性測試方法

    嵌入式軟件可靠性測試方法
    發(fā)表于 11-05 17:18

    嵌入式軟件可靠性設(shè)計,這些問題值得注意!

    影響大的話,還需要對端口數(shù)據(jù)的正確進行檢查和判斷。嵌入式軟件可靠性編程方法的四個目的是、
    發(fā)表于 03-30 16:08

    【我是電子發(fā)燒友】嵌入式軟件可靠性設(shè)計要注意的一些問題

    影響大的話,還需要對端口數(shù)據(jù)的正確進行檢查和判斷。嵌入式軟件可靠性編程方法的四個目的是、
    發(fā)表于 05-04 09:27

    淺析嵌入式系統(tǒng)軟件可靠性測試難點

    就發(fā)現(xiàn)軟件中的缺陷,這對于對軟件可靠性和安全有著較高要求的嵌入式軟件十分重要。使用靜態(tài)分析發(fā)
    發(fā)表于 08-02 10:05

    嵌入式系統(tǒng)產(chǎn)品的可靠性

    關(guān)注有助于確保整個終端設(shè)備可靠性要求的裝置。集成電路在嵌入式系統(tǒng)的性能、尺寸和整體成本方面已經(jīng)實現(xiàn)重大突破,對各種存儲元件的依賴及使用小尺寸硅工藝技術(shù)可能產(chǎn)生的永久和瞬時誤差對可靠性產(chǎn)
    發(fā)表于 08-30 14:43

    嵌入式軟件可靠性設(shè)計的四個方面注意問題

    的后果不僅僅是數(shù)據(jù)錯誤而是有可能導(dǎo)致不可估量的災(zāi)難,所以總結(jié)起來,嵌入式軟件可靠性設(shè)計需注意的問題有四個方面
    發(fā)表于 05-16 10:44

    嵌入式軟件可靠性測試與可靠性增長評估

    關(guān)于嵌入式軟件可靠性、安全測試與評估的資料,希望有幫助。
    發(fā)表于 06-17 16:53

    如何對嵌入式軟件進行可靠性測試

    摘 要 本文針對目前嵌入式軟件設(shè)計可靠性測試用例的手段主要依靠手工分析,沿用傳統(tǒng)的軟件測試用例設(shè)計方法進行,不能夠滿足可靠性測試用例設(shè)計的基
    發(fā)表于 10-27 06:10

    嵌入式軟件可靠性測試方法是什么

    本文原文鏈接如下:https://www.jianshu.com/p/f6f5c3cd3fab目前,嵌入式軟件可靠性評價主要依賴測試,因為嵌入式
    發(fā)表于 12-21 07:09

    嵌入式系統(tǒng)軟件可靠性設(shè)計

    本文分析了 嵌入式系統(tǒng)軟件的復(fù)雜度、可靠性與穩(wěn)定性之間的關(guān)系,本給出了增加嵌入式系統(tǒng)可靠性的一般方法。
    發(fā)表于 11-17 17:43 ?38次下載

    軍用嵌入式系統(tǒng)的可靠性考慮因素

    1 可靠性是軍用嵌入式系統(tǒng)的重要因素嵌入式系統(tǒng)往往工作環(huán)境惡劣、受電噪聲干擾較大,而且隨著軟件越來越復(fù)
    發(fā)表于 08-18 11:00 ?712次閱讀

    嵌入式系統(tǒng)的可靠性設(shè)計

    嵌入式應(yīng)用系統(tǒng)是一個有計算機內(nèi)核,軟、硬件整合的智能化電子系統(tǒng)。與傳統(tǒng)的激勵響應(yīng)型電子系統(tǒng)的本質(zhì)差異,是它的智力嵌入,從而形成嵌入式應(yīng)用系統(tǒng)全新的可靠性設(shè)計觀念、方法與技術(shù)。這些全新的
    發(fā)表于 11-30 10:04 ?1414次閱讀
     <b class='flag-5'>嵌入式</b>系統(tǒng)的<b class='flag-5'>可靠性</b>設(shè)計

    軍用嵌入式系統(tǒng)的可靠性考慮因素

    嵌入式系統(tǒng)更應(yīng)高度重視其可靠性設(shè)計、測試和評估技術(shù),應(yīng)把可靠性作為嵌入式系統(tǒng)最重要的指標優(yōu)先考慮。這一
    發(fā)表于 12-02 19:42 ?717次閱讀

    嵌入式軟件可靠性設(shè)計

    設(shè)備的可靠性涉及多個方面:穩(wěn)定的硬件、優(yōu)秀的軟件架構(gòu)、嚴格的測試以及市場和時間的檢驗等等。這里著重談一下作者自己對嵌入式軟件
    的頭像 發(fā)表于 06-22 10:00 ?864次閱讀
    876棋牌游戏中心| 百家乐官网投注外围哪里好| 盐城百家乐的玩法技巧和规则 | 百家乐游戏作弊| 赙彩百家乐官网游戏规则| 百家乐博娱乐场开户注册| 百家乐官网开户最快的平台是哪家| 八大胜百家乐的玩法技巧和规则 | 喜达国际| 百家乐玩法既规则| 百家乐官网娱乐分析软| 百家乐路单走势图| 百家乐官网直杀| 大发888 注册账号| 澳门百家乐官网论坛及玩法| 佳豪国际| 百家乐ag厅投注限额| 百家乐官网3带厂家地址| 百家乐过滤工具| 百家乐官网网上真钱赌场娱乐网规则 | 兰桂坊百家乐官网的玩法技巧和规则 | 怎样玩百家乐的玩法技巧和规则 | 大发888娱乐场下载iypuqrd| 24山向大凶如何化解| 双柏县| 星期八百家乐的玩法技巧和规则 | 网上百家乐娱乐场开户注册| 狮威百家乐官网娱乐平台| 澳门线上赌场| 网络百家乐程序| 网上赌百家乐官网的玩法技巧和规则 | 百家乐官网管家| 百家乐官网纯数字玩法| 菲利宾太阳城娱乐网| 百家乐官网大光明影院| 百家乐官网试玩账户| 威尼斯人娱乐注册| 百家乐转盘技巧| 百家乐官网怎么样投注| 六合彩图库大全| 真人百家乐对决|