• 
    

    
    

      99热精品在线国产_美女午夜性视频免费_国产精品国产高清国产av_av欧美777_自拍偷自拍亚洲精品老妇_亚洲熟女精品中文字幕_www日本黄色视频网_国产精品野战在线观看 ?

      Linux x86平臺下程序崩潰的調試方法及量化分析

      2014-12-31 00:00:00徐伶伶趙靜
      計算機光盤軟件與應用 2014年10期

      摘 要:Linux中的段錯誤是編程中經(jīng)常遇到的問題,往往導致程序崩潰。本文對段錯誤產(chǎn)生的原因,結合程序運行的過程,對段錯誤進行量化分析。

      關鍵詞:段錯誤;量化分析;程序調試

      中圖分類號:TP316.81

      在Linux系統(tǒng)上做過程序開發(fā)的人一定都遇到過“段錯誤”(Segmentation fault),隨之程序異常退出。初學編程的人往往對此束手無策,不知道發(fā)生了什么事情,應該如何進行調試。其實Linux下的段錯誤和Windows平臺上臭名昭著的“該程序執(zhí)行了非法操作,即將被關閉”錯誤本質上是相同的,絕大部分都是對內存的非法訪問而導致的。

      很多新手程序員對于段錯誤往往無從下手,或是只能通過原始的方式,例如在程序中添加許多的printf語句來跟蹤程序的執(zhí)行。這樣往往效率低下,因此掌握一些調試技巧對于提高調試效率而言是十分重要的,使用正確的調試工具和方法往往能夠事半功倍,幫助準確定位程序出錯的地方,從而找到引發(fā)該錯誤的根本原因(root cause)。

      1 段錯誤產(chǎn)生的原因

      在Linux下程序崩潰基本上都是由于內存非法訪問造成的,當內存非法訪問發(fā)生時,CPU會產(chǎn)生一個軟中斷信號,如SIGSEGV,而該軟中斷信號的默認處理就是程序退出并產(chǎn)生一個core dump文件,該文件保存了程序崩潰時的現(xiàn)場,包括CPU寄存器的值,內存棧和堆里的數(shù)據(jù)。這些數(shù)據(jù)加上程序的二進制文件(即編譯后的可執(zhí)行文件)和程序源代碼就是我們進行分析的基礎。

      2 程序的運行過程

      在調試程序之前我們需要了解一下我們的程序是怎么執(zhí)行的。我們寫的C源碼經(jīng)過編譯鏈接后生成機器代碼,也就是匯編指令組成的可執(zhí)行文件,在Linux中是ELF(Executable and Linkable Format)格式的可執(zhí)行文件。匯編指令對內存和寄存器進行操作。而在X86所有的寄存器中,EAX,EBP,ESP,EIP是幾個最重要的寄存器。

      EAX:通用寄存器,并用于保存函數(shù)返回值。被調函數(shù)返回時將返回值放入EAX,調用者從EAX中獲取返回值。

      ESP:棧頂寄存器,指向工作棧的棧頂。每當進入一個函數(shù)時,會通過修改ESP在棧中開辟一塊空間供本函數(shù)使用。當退出一個函數(shù)時,ESP會恢復原值。

      EBP:棧底寄存器,指向當前函數(shù)的棧底。每當進入一個函數(shù)時,該函數(shù)會將原來的(即調用它的函數(shù)的)EBP保存在棧中,然后將原來的ESP作為新的EBP,即EBP指向當前函數(shù)的棧底。

      EIP:當前正在執(zhí)行的匯編指令的地址。

      函數(shù)的進入和退出都對應著對程序工作棧的修改,需要特別注意的是在X86中,棧是往低地址方向增長。所以進入一個函數(shù)分配??臻g是對ESP進行減操作(sub),而退出一個函數(shù)時是進行加(add)操作。每個函數(shù)在棧上都有自己一塊空間,稱為該函數(shù)的棧幀(stack frame)。如果函數(shù)f1()調用了f2(),目前正在執(zhí)行函數(shù)f2()中的代碼,那么工作棧將會有如圖1的布局:

      圖1

      表中的內存位置的寫法是x86的基址尋址的表達方式(采用GDB使用的ATT格式),例如-4(%esp)代表的是地址為ESP寄存器的值減去4的內存單元的值。

      3 實例分析

      我們來看一個經(jīng)過簡化的例子。我們有一個程序執(zhí)行時出現(xiàn)崩潰,產(chǎn)生了core dump文件。用gdb調試工具打開coredump文件可以看到如圖2輸出:

      圖2

      可以看出該程序發(fā)生了段錯誤,收到了一個SIGSEGV。同時GDB還指出了出錯的指令位于f2()函數(shù)的0x08048426地址。我們通過disassemble命令查看f2()的匯編代碼如圖3:

      圖3

      可以看到0x08048426的指令是mov(%eax),%edx,其含義是將EAX寄存器當作指針使用,將其所指向的內存的內容取到EDX中。這句指令出錯意味著EAX寄存器中存放的是非法的內存地址,該地址不可讀。我們可以通過info registers命令來查看EAX以及其它寄存器的值(部分)如圖4:

      圖4

      結果顯示EAX的值是0,即空指針NULL,顯然該地址是不可訪問的,所以CPU產(chǎn)生了一個軟中斷信號SIGSEGV。由此我們從匯編代碼的層次找到了程序崩潰的直接原因,但這還不夠,我們需要繼續(xù)分析為什么EAX寄存器是0。我們順藤摸瓜,查看EAX的值是從何而來。我們繼續(xù)查看f2()的匯編代碼可以發(fā)現(xiàn)上一條指令0x08048423即mov0x8(%ebp),%eax這條指令給EAX寄存器賦了值。我們知道m(xù)ov是一條賦值指令,0x8(%ebp)我們已經(jīng)講到,是f1()傳遞給f2()的第1個參數(shù),由此可以知道f2()的第一個參數(shù)的值為0,即p為空指針NULL,因此此處程序崩潰的原因是傳遞給f2()的參數(shù)為空指針,而f2()在使用前未對其進行檢查導致程序崩潰。

      4 其它可能導致段錯誤的情形

      上面例子是由于訪問非法指針引起的段錯誤,是在編程中,特別是初學者常犯的一種錯誤。除了非法指針外還有一些其他類型的段錯誤,比如:(1)寫局部變量數(shù)組時越界。由于局部變量數(shù)組是在棧上的,越界意味著覆蓋棧的其他部分,導致程序無法繼續(xù)執(zhí)行;(2)棧溢出。程序的棧的空間是有限的,如果函數(shù)嵌套層次太多,例如遞歸調用層數(shù)過多,每次調用都會分配一塊??臻g,導致棧溢出;(3)修改內存只讀區(qū)的內容,雙引號中的字符串,例如”abcd”是存放在只讀區(qū)中的,如果你嘗試通過指針去修改字符串的內容就會導致段錯誤。

      5 結束語

      本文介紹的調試方法雖然是基于Linux和x86的,但其思想同樣適用于其他操作系統(tǒng)和硬件平臺。另外,掌握程序的調試技巧固然十分重要,但更重要的提高自身的編程水平和養(yǎng)成良好的編程習慣,這樣才能寫出高質量的程序。畢竟程序調試是一種逆向工程,引入一個bug十分容易,而找到它往往需要付出很大的時間和精力的成本。

      參考文獻:

      [1]The Santa Cruz Operation.Inc.System V Application Binary Interface Intel386 Architecture Processor Supplement Fourth Edition[M],1997.

      [2]Randal E.Bryant.David R.O’Hallaron.Computer Systems A Programmer’s Perspective,Pittsburgh[M],2001.

      作者簡介:徐伶伶(1981.09-),女,江蘇太倉人,研究生,講師,計算機應用技術;趙靜女(1981-),山東青島人,研究生,講師,計算機應用技術。

      作者單位:青島工學院,山東青島 266300

      基金項目: “基于本體的教育信息化共享平臺研制”(項目編號:2012KY009)。

      子洲县| 莫力| 芜湖县| 淅川县| 斗六市| 泰兴市| 隆回县| 万山特区| 清丰县| 北碚区| 盈江县| 新竹县| 尼勒克县| 阳东县| 克什克腾旗| 普兰县| 斗六市| 江孜县| 兰溪市| 介休市| 成武县| 阿荣旗| 荆门市| 加查县| 孙吴县| 红桥区| 九龙坡区| 伽师县| 兴隆县| 彭州市| 湖州市| 循化| 潜山县| 宣武区| 宾阳县| 太仆寺旗| 云安县| 神池县| 商南县| 白银市| 丁青县|