亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

Linux下的代碼淬火技術

2019-11-17 05:27:39
字體:
來源:轉載
供稿:網友
    我們知道,鐵匠經常把金屬工件加熱到一定溫度,然后忽然浸在水或油中使其冷卻,從而有效增加其硬度。類似的,軟件開發人員也可以采取某種手段,從而顯著提高軟件的可靠性和質量,我們形象地稱之為代碼淬火技術。     一、代碼淬火的好處    理想中,軟件淬火技術首先能夠預見代碼中的哪些地方可能出錯,然后,要么帶給我們一種能夠完全避免錯誤的方式編寫代碼,要么能及時識別出錯誤,從而更輕松地跟蹤源代碼。尤其是對于C語言,因為它不是一種安全語言,所以更需要軟件淬火技術來提高軟件的可靠性。本文將具體介紹linux下常用的代碼淬火方法。    二、常見的碼淬火技術    代碼淬火技術的形式各種各樣,我們這里要講的是幫助我們構建更加強健的代碼的各種技術。    1 .緩沖區溢出問題    緩沖區是一個非常嚴重的安全問題,最好的情況下可能導致軟件的行為錯亂;嚴重時將會導致被緩沖區利用程序所控制而執行任何攻擊者所想要執行的代碼。請看下面的示例代碼:
static char ourArray[100];
    ...
    int i;
    for ( i = 0 ; i < 100 ; i++ ) {
      ourArray[i] = (char)(0x30+i);
    }
    ourArray[i] = 0;    // 越界,危險!
    上例中,我們的賦值操作已經越過了數組的邊界。它并沒有在我們的數組內執行寫操作,它是在數組邊界之后的第一個字節內進行的寫操作。換句話說,數組之后的第一個對象,無論它是結構體也好,還是指針也罷,現在已經被破壞了。為了改變這種糟糕的情況,我們只需要養成一種良好的習慣,那就是使用符號常量來指示數組的邊界。就像在下例中一樣,我們創建了一個常量,來定義數組的長度。這里有一個小技巧,那就是假設我們需要的數組長度為A,那么在定義數組長度時,實際長度是A+1。這時,無論數組長度如何,可以肯定的是,數組[A]一定是該數組的邊界。換句話說,我們留了一個元素的余量。如下所示:
#define ARRAY_SIZE    100
    static char ourArray[ARRAY_SIZE+1];
    ...
    int i;
    for (i = 0 ; i < ARRAY_SIZE ; i++ ) {
      ourArray[i] = (char)(0x30+i);
    }
ourArray[ARRAY_SIZE] = 0;  //為數組的最后一個元素賦值
    2.檢驗返回值    現在,軟件中最常見的錯誤是忽略了返回值的檢驗。許多程序員在調用系統函數或用戶自定義的函數后,通常會盲目樂觀地認為這些函數會成功的執行。當我們打造經淬火處理的軟件時,應當檢查返回值,并且在返回失敗時,還要進行妥善處理。比如:
    ret =     if ( ret < 0 ) {
      ret = printf( "An error occured emitting value./n" );
    }
    3.檢查輸入/輸出數據    當我們開發應用時,無論涉及到用戶的輸入,或者是通過網絡進行輸入,一定要嚴密關切輸入數據的情況:比如對于給定的操作數據是否充分,或者收到的數據是否超過了預留的緩沖區空間。    4.為分支語句提供備選方案    對于switch語句來說,經常會遺漏default部分,這會導致無法預料的后果。舉例來說:
switch( mode ) {
      case
OperaTIONAL_MODE:
        /* 切換到運行模式進行處理 */
        break;
      case BUILT_IN_TEST_MODE:
        /* 切換到測試模式進行處理*/
        break;
    }


    假設現在我們又添加了一種模式,但是上面的代碼段沒有及時得到更新,這時假如執行了這段代碼,執行結果將無法預料。假如包括default部分,至少也能利用該部分在出現問題時通知調用者,即使在此放一個assert,也能在調試時捕捉當時的狀況。如下例所示:
switch( mode ) {
      case OPERATIONAL_MODE:
        /*切換到運行模式進行處理 */
        break;
      case BUILT_IN_TEST_MODE:
        /* 切換到測試模式進行處理 */
        break;
      default:
        assert(0);
        break;
}
    上述問題除了存在于switch語句外,還存在于if/then/else分支語句中。下面分別舉例說明:
float coefficient = 0.0;
    if (state == FIRST_STAGE) coefficient = 0.75;
    else if (state == SECOND_STAGE) coefficient = 1.25;
    作者的意圖很明顯,它是希望根據變量state的值的不同,也讓變量coefficient取不同的值。但是假如變量state的值被破壞(比如碰到上面提到的緩沖區溢出的破壞),或者使它取了一個預期之外的值,那么變量coefficient的值就會一直是0.0。假如這是用來計算付費的話,后果的嚴重可想而知。因此,else分支語句至少應該包含一個分支用來捕捉錯誤,例如:
float coefficient = 0.0;
    if (state == FIRST_STAGE) coefficient = 0.75;
    else if (state == SECOND_STAGE) coefficient = 1.25;
    else coefficient = SAFE_ COEFFICIENT;
    盡管許多情況下尾隨的else分支不是必須的,但這樣做無疑為我們的代碼加了一把保護傘,以免鑄成大錯。   4.自標識結構體    在強類型語言中,假如使用非法的數據類型將會導致運行時錯誤。但對于弱類型語言,比如C語言,在指針的傳遞和類型轉換過程中,很輕易把類型混淆。為此,我們引入類似于強類型語言中的運行時類型檢驗思想。    現在我們通過實例來講解自標識結構體,具體代碼如下所示:
#include <stdio.h>
#include <stdlib.h>
            #include <assert.h>
           
            #define TARGET_MARKER_SIG       0xFAF32000
           
            typedef strUCt {
           
              unsigned int signature;
              unsigned int targetType;
              double       x, y, z;
           
            } targetMarker_t;
           
           
            #define INIT_TARGET_MARKER(ptr) /
                      ((( targetMarker_t *)ptr)->signature = TARGET_MARKER_SIG)
            #define CHECK_TARGET_MARKER(ptr) /
                      assert(((targetMarker_t *)ptr)->signature == /
                                TARGET_MARKER_SIG)
           
           
            void displayTarget( targetMarker_t *target )
            {
           
              /* 預先檢查 target結構體 */
              CHECK_TARGET_MARKER(target);
           
             printf( "Target type is %d/n", target->targetType );
           
              return;
            }
           
           
            int main()
            {
              void *object1, *object2;
           
              /* 新建兩個對象 */
              object1 = (void *)malloc( sizeof(targetMarker_t) );
              assert(object1);
              object2 = (void *)malloc( sizeof(targetMarker_t) );
              assert(object2);
              /*按照target marker結構體初始化object1 */
              INIT_TARGET_MARKER(object1);
           
              /* 嘗試顯示object1 */
              displayTarget( (targetMarker_t *)object1 );
           
              /* 嘗試顯示object2 */
              displayTarget( (targetMarker_t *)object2 );
           
              return 0;
            }

    在代碼的第6-12行,定義了我們的目標結構體,其中有一個專門的頭部,名為signature(識別標志),即該結構類型的運行時類型標識符。并且在第4行為該類型定義了一個識別標志,作為該結構類型的唯一描述符號。此外,代碼中還提供了兩個宏INIT_TARGET_MARKER和CHECK_TARGET_MARKER,分別用來初始化和檢驗該結構體中的識別標志。    繼續往后看,請注重代碼的34-54行,其中分配了兩個等同長度(targetMarker_t)的內存空間來供兩個對象使用,然后利用宏INIT_TARGET_MARKER將其中一個初始化,最后,分別利用displayTarget函數顯示。    對于第22-31行的displayTarget函數,首先調用CHECK_TARGET_MARKER來檢驗收到的對象的識別標志,假如該標志非法,assert就會發揮作用。當然,這里只是一個概念性的演示。  5.錯誤報告    對于錯誤報告而言,根據開發的應用類型的不同,需要采取不同的處理方法。例如,假如開發的是命令行工具的話,常用的方法是通過把錯誤消息遞給stderr來告知用用戶出錯情況。假如開發的是諸如嵌入式Linux應用之類的具有I/O能力的應用程序的話,錯誤報告的形式就多了,比如專門的日志或標準系統日志(syslog)。其中,syslog函數的原型如下所示:
    #include <syslog.h>
    void syslog( int priority, char *format, ... );
    對于syslog函數,我們需要提供優先級、格式化字符串等參數。它的用法類似于printf函數。優先級可以從這里選擇一個:LOG_EMERG、 LOG_ALERT、LOG_CRIT、LOG_ERR、LOG_WARNING、LOG_NOTICE、LOG_INFO或者 LOG_DEBUG。 下面以實例說明如何使用syslog函數為系統日志生成一個消息:
#include <syslog.h>
           
            int main()
            {
           
              syslog( LOG_ERR, "Unable to load configuration!" );
           
              return 0;
            }
    程序執行后,位于 /var/log/_messages的系統日志更新如下:
 Aug 16 21:33:16 Ian test: Unable to load configuration!
    本例中,test是上面列出的程序名,Ian是主機名。系統日志的好處是可以記錄許多的錯誤報告。憑借它,開發人員能夠了解消息在哪里生成的、哪一個能夠幫助理解問題等等。對于系統應用和守護進程來說,syslog的格外有用。    除此之外,錯誤報告還有一個注重事項,那就是報告的錯誤一定要具體。為了能夠讓用戶恰當的處理發生的錯誤,錯誤消息一定要唯一的標識出錯誤來。錯誤消息本身不能模棱兩可,或者具有歧義性。   6.降低代碼的復雜性    降低復雜性就等于降低出錯的機率,因為代碼越復雜,包含bug的可能性越大,并且找到bug的難度也越大。為此,我們可以將一個復雜的代碼段,分解成多個更輕易理解的幾段代碼。這樣一來,隨著代碼的可維護性的提高,軟件質量自然有很大提升。     7.自保護性函數    使用自保護性函數是一種有效的調試機制,它能保證軟件的正確性。這里的自保護意味著,當你寫一個函數的時候,必需審核該函數的輸入;并且在輸入數據處理成后,還要審核數據的輸出,從而確保沒有出錯。下面我們用一個實例函數加以說明:
STATUS_T checkRegisterStatus( REGISTER_T register, MODE_T *mode )
            {
              REGISTER_STS_T retStatus;
           
              /* 驗證輸入 */
              assert( validRegister( register ) );
              assert( validMode( mode ) );
           
           
              /*--------------------*/
              /*這里省略checkRegisterStatus 的內部處理部分 */
              /*--------------------*/
           
           
              /* 可能改變了模式,檢驗之 */
              assert( validMode( mode ) );
           
              return retStatus;
            }

    注重,假如表達是結果為非(即0),assert函數就會停止應用,并在標準輸出設備上生成一個錯誤消息??梢酝ㄟ^定義符號NDEBUG的方法來停用assert。    從上例中我們看到,函數首先通過驗證輸入來確保得到的數據是正確的,然后通過驗證輸出來保證它給出的數據也是正確的。根據具體情況,我們可能收到錯誤消息,即使這樣我們也能輕而易舉地發現錯誤之所在。另外,assert的作用不僅限于確保函數的輸入輸出的正確性,還能用來確保內部的一致性。所有應該在調試期間發現的嚴重錯誤,都可以利用assert來輕松處理。    8.優化調試輸出    太多的輸出能夠掩蓋錯誤,但輸出過少也會漏掉錯誤,因此我們需要尋找一個平衡點,使得提供的調試輸出和錯誤消息夠用,但又不會過量,這需要在實踐中具體把握。   9.內存調試技術    在Linux中,有許多程序庫都可以用來調試動態內存治理。最常見的 Electric Fence是一個功能強大并且能及時發現內存錯誤的庫,它不僅可以利用低層處理器的內存治理單元MMU的段故障來捕捉內存錯誤,而且還能偵察數組越界問題。    10.編譯器的支持    實際上,編譯器本身就是一個識別代碼問題的無價之寶,當構建程序時,一定要使用-Wall項來啟用報警功能。    此外,還可以利用-Werror把警告作為錯誤對待,從而停止對源文件進行進一步的編譯,這選項對于大型應用程序格外有用。當我們構建有多個源文件組成的應用程序時,我們可以將兩個選項組合使用。如下例所示:
    gcc -Wall -Werror test.c -o test
    假如我們想讓源代碼能夠兼容ANSI,我們還能利用編譯器來進行檢查,用法如下所示:
    gcc -ansi -pedantic test.c -o test
    確認變量已經初始化是非常有用的,除了使用報警選項外,還需要使用優化選項,因為只有經過優化的代碼才能使用數據流信息:
    gcc -Wall -O -Wuninitialized test.c -o test
    要想了解更多,可以參考gcc的main頁。    三、 小結    本文具體介紹了代碼淬火技術的概念以及Linux下常用的幾種代碼淬火方法。在下一篇文章中,我們將繼續介紹用來提高Linux應用程序安全性和可靠性的開源工具和代碼追蹤技術。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久偷看各类女兵18女厕嘘嘘| 欧美成人精品激情在线观看| 亚洲精品国产拍免费91在线| 亚洲欧美精品一区| 精品无码久久久久久国产| 亚洲一区二区三区视频播放| 久久精品电影网站| 91av中文字幕| www.亚洲人.com| 亚洲自拍欧美另类| 在线视频亚洲欧美| 美女啪啪无遮挡免费久久网站| 欧美xxxx14xxxxx性爽| 国产成人一区二区三区小说| 久久久久久久久网站| 国产日韩欧美中文在线播放| 国产精品99久久久久久白浆小说| 欧美性xxxx| 欧美日韩亚洲视频| 91精品视频免费看| 性夜试看影院91社区| 亚洲精品久久视频| 国产午夜精品全部视频播放| 午夜精品一区二区三区在线视| 91免费版网站入口| 亚洲另类激情图| 亚洲精品久久久久久久久久久久久| 国内成人精品视频| 欧美韩日一区二区| 亚洲精品福利资源站| 欧美激情综合色综合啪啪五月| 久久99久久99精品免观看粉嫩| 国产一区私人高清影院| 欧美大片在线影院| 日韩在线观看网站| 亚洲综合日韩在线| 欧美激情综合色综合啪啪五月| 欧美xxxwww| 久久99久国产精品黄毛片入口| 亚洲国产精品成人av| 亚洲欧美另类自拍| 欧美精品激情视频| 国产日韩精品入口| 亚洲最新中文字幕| 久久99热这里只有精品国产| 人妖精品videosex性欧美| 色噜噜亚洲精品中文字幕| 日韩精品中文字幕有码专区| 91久久精品在线| 亚洲最大福利视频网| 国产日韩av高清| 久色乳综合思思在线视频| 国产精品天天狠天天看| 国产一区二区欧美日韩| 国产成人高清激情视频在线观看| 亚洲欧洲中文天堂| 亚洲视频在线免费看| 成人福利视频网| 日韩国产欧美精品在线| 久久久国产在线视频| 色综合男人天堂| 欧美亚洲免费电影| 国内偷自视频区视频综合| 国产精彩精品视频| 欧美激情成人在线视频| 国产精品永久免费观看| 欧美日韩在线观看视频小说| 日韩电视剧在线观看免费网站| 欧美男插女视频| 九九久久久久99精品| 亚洲国产精品一区二区久| 欧美福利视频网站| 欧美视频中文在线看| 欧美在线亚洲在线| 亚洲人成在线观看网站高清| 久久激情视频免费观看| 久久久久久伊人| 欧美激情喷水视频| 欧美午夜激情在线| 日韩中文有码在线视频| 亚洲欧洲一区二区三区久久| 欧美激情在线有限公司| 欧美中文字幕视频在线观看| 亚洲欧洲偷拍精品| 色婷婷av一区二区三区久久| 欧美超级免费视 在线| 91国产视频在线播放| 亚洲综合在线小说| 国产91成人video| 国产综合在线视频| 91免费综合在线| 国产精品男人爽免费视频1| 久久视频国产精品免费视频在线| 欧美性猛交xxxx| 亚洲视频精品在线| 欧美疯狂xxxx大交乱88av| 91干在线观看| 亚洲欧洲国产一区| 国产91色在线|| 91成品人片a无限观看| 隔壁老王国产在线精品| 国产一区二区动漫| 超碰97人人做人人爱少妇| 色老头一区二区三区在线观看| 成人深夜直播免费观看| 成人黄色中文字幕| 国产精品久久久久久久午夜| 国产视频精品一区二区三区| 色婷婷av一区二区三区久久| 国产在线精品成人一区二区三区| 国产精品久久久久免费a∨| 日韩激情视频在线播放| 欧美野外wwwxxx| 国产欧美日韩综合精品| 91中文精品字幕在线视频| 自拍视频国产精品| 2019亚洲日韩新视频| 狠狠躁夜夜躁人人躁婷婷91| 97国产精品免费视频| 国产精品影片在线观看| 欧美日韩亚洲激情| 久久综合伊人77777蜜臀| 亚洲欧美另类中文字幕| 国产精品免费在线免费| 黄色成人在线播放| 青青草原一区二区| 久久久亚洲国产| 97在线视频观看| 久久久亚洲福利精品午夜| 国产suv精品一区二区三区88区| 精品日韩美女的视频高清| 亚洲人成免费电影| www.99久久热国产日韩欧美.com| 成人黄色片在线| 亚洲第一区中文字幕| 成人黄色av网站| 国产精品日韩在线一区| 精品国产乱码久久久久久虫虫漫画| 欧美天天综合色影久久精品| 国产精品久久久久久亚洲影视| 国产精品久久不能| 国内精品一区二区三区| 日韩精品在线免费播放| 成人黄色午夜影院| 日韩激情在线视频| 啊v视频在线一区二区三区| 欧美大片第1页| 中文一区二区视频| 国产成人av网址| 欧美一区二区三区免费视| 日韩av在线精品| 中文字幕亚洲欧美| 中文字幕久久久| 欧美激情久久久| www.日韩.com| 日韩最新免费不卡| 国产精品久久电影观看| 国产精品久久久久久久久久久久久| 欧美日韩美女视频| 欧美性xxxx在线播放| 亚洲精品电影久久久| 96精品视频在线| 久操成人在线视频| 国产精品成人播放|