• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            興海北路

            ---男兒仗劍自橫行
            <2008年3月>
            2425262728291
            2345678
            9101112131415
            16171819202122
            23242526272829
            303112345

            統(tǒng)計

            • 隨筆 - 85
            • 文章 - 0
            • 評論 - 17
            • 引用 - 0

            常用鏈接

            留言簿(6)

            隨筆分類

            隨筆檔案

            收藏夾

            全是知識啊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            在Linux上找出并解決程序錯誤的主要方法
            來源:http://www.y768.com/content/view/5755/109/

            Steve Best(sbest@us.ibm.com)
            JFS 核心小組成員,IBM
            2002 年 8 月
            您可以用各種方法來監(jiān)控運行著的用戶空間程序:可以為其運行調(diào)試器并單步調(diào)試該程序,添加打印語句,或者添加工具來分析程序。本文描述了幾種可以用來調(diào)試在 Linux 上運行的程序的方法。我們將回顧四種調(diào)試問題的情況,這些問題包括段錯誤,內(nèi)存溢出和泄漏,還有掛起。
            本 文討論了四種調(diào)試 Linux 程序的情況。在第 1 種情況中,我們使用了兩個有內(nèi)存分配問題的樣本程序,使用 MEMWATCH 和 Yet Another Malloc Debugger(YAMD)工具來調(diào)試它們。在第 2 種情況中,我們使用了 Linux 中的 strace 實用程序,它能夠跟蹤系統(tǒng)調(diào)用和信號,從而找出程序發(fā)生錯誤的地方。在第 3 種情況中,我們使用 Linux 內(nèi)核的 Oops 功能來解決程序的段錯誤,并向您展示如何設(shè)置內(nèi)核源代碼級調(diào)試器(kernel source level debugger,kgdb),以使用 GNU 調(diào)試器(GNU debugger,gdb)來解決相同的問題;kgdb 程序是使用串行連接的 Linux 內(nèi)核遠程 gdb。在第 4 種情況中,我們使用 Linux 上提供的魔術(shù)鍵控順序(magic key sequence)來顯示引發(fā)掛起問題的組件的信息。

            常見調(diào)試方法
            當您的程序中包含錯誤時,很可能在代碼中某處有一個條件,您認為它為真(true),但實際上是假(false)。找出錯誤的過程也就是在找出錯誤后推翻以前一直確信為真的某個條件過程。

            以下幾個示例是您可能確信成立的條件的一些類型:

            在源代碼中的某處,某變量有特定的值。
            在給定的地方,某個結(jié)構(gòu)已被正確設(shè)置。
            對于給定的 if-then-else 語句,if 部分就是被執(zhí)行的路徑。
            當子例程被調(diào)用時,該例程正確地接收到了它的參數(shù)。

            找 出錯誤也就是要確定上述所有情況是否存在。如果您確信在子例程被調(diào)用時某變量應(yīng)該有特定的值,那么就檢查一下情況是否如此。如果您相信 if 結(jié)構(gòu)會被執(zhí)行,那么也檢查一下情況是否如此。通常,您的假設(shè)都會是正確的,但最終您會找到與假設(shè)不符的情況。結(jié)果,您就會找出發(fā)生錯誤的地方。

            調(diào)試是您無法逃避的任務(wù)。進行調(diào)試有很多種方法,比如將消息打印到屏幕上、使用調(diào)試器,或只是考慮程序執(zhí)行的情況并仔細地揣摩問題所在。

            在 修正問題之前,您必須找出它的源頭。舉例來說,對于段錯誤,您需要了解段錯誤發(fā)生在代碼的哪一行。一旦您發(fā)現(xiàn)了代碼中出錯的行,請確定該方法中變量的值、 方法被調(diào)用的方式以及關(guān)于錯誤如何發(fā)生的詳細情況。使用調(diào)試器將使找出所有這些信息變得很簡單。如果沒有調(diào)試器可用,您還可以使用其它的工具。(請注意, 產(chǎn)品環(huán)境中可能并不提供調(diào)試器,而且 Linux 內(nèi)核沒有內(nèi)建的調(diào)試器。)

            實用的內(nèi)存和內(nèi)核工具
            您可以使用 Linux 上的調(diào)試工具,通過各種方式跟蹤用戶空間和內(nèi)核問題。請使用下面的工具和技術(shù)來構(gòu)建和調(diào)試您的源代碼:
            用戶空間工具:

            內(nèi)存工具:MEMWATCH 和 YAMD
            strace
            GNU 調(diào)試器(gdb)
            魔術(shù)鍵控順序

            內(nèi)核工具:

            內(nèi)核源代碼級調(diào)試器(kgdb)
            內(nèi)建內(nèi)核調(diào)試器(kdb)
            Oops


            本文將討論一類通過人工檢查代碼不容易找到的問題,而且此類問題只在很少見的情況下存在。內(nèi)存錯誤通常在多種情況同時存在時出現(xiàn),而且您有時只能在部署程序之后才能發(fā)現(xiàn)內(nèi)存錯誤。

            第 1 種情況:內(nèi)存調(diào)試工具
            C 語言作為 Linux 系統(tǒng)上標準的編程語言給予了我們對動態(tài)內(nèi)存分配很大的控制權(quán)。然而,這種自由可能會導(dǎo)致嚴重的內(nèi)存管理問題,而這些問題可能導(dǎo)致程序崩潰或隨時間的推移導(dǎo)致性能降級。

            內(nèi)存泄漏(即 malloc() 內(nèi)存在對應(yīng)的 free() 調(diào)用執(zhí)行后永不被釋放)和緩沖區(qū)溢出(例如對以前分配到某數(shù)組的內(nèi)存進行寫操作)是一些常見的問題,它們可能很難檢測到。這一部分將討論幾個調(diào)試工具,它們極大地簡化了檢測和找出內(nèi)存問題的過程。

            MEMWATCH
            MEMWATCH 由 Johan Lindh 編寫,是一個開放源代碼 C 語言內(nèi)存錯誤檢測工具,您可以自己下載它(請參閱本文后面部分的參考資料)。只要在代碼中添加一個頭文件并在 gcc 語句中定義了 MEMWATCH 之后,您就可以跟蹤程序中的內(nèi)存泄漏和錯誤了。MEMWATCH 支持 ANSI C,它提供結(jié)果日志紀錄,能檢測雙重釋放(double-free)、錯誤釋放(erroneous free)、沒有釋放的內(nèi)存(unfreed memory)、溢出和下溢等等。

            清單 1. 內(nèi)存樣本(test1.c)
            #include "STDLIB.H"
            #include "STDIO.H"
            #include "memwatch.h"

            int main(void)
            {
            char *ptr1;
            char *ptr2;

            ptr1 = malloc(512);
            ptr2 = malloc(512);

            ptr2 = ptr1;
            free(ptr2);
            free(ptr1);
            }



            清單 1 中的代碼將分配兩個 512 字節(jié)的內(nèi)存塊,然后指向第一個內(nèi)存塊的指針被設(shè)定為指向第二個內(nèi)存塊。結(jié)果,第二個內(nèi)存塊的地址丟失,從而產(chǎn)生了內(nèi)存泄漏。

            現(xiàn)在我們編譯清單 1 的 memwatch.c。下面是一個 makefile 示例:

            test1
            gcc -DMEMWATCH -DMW_STDIO test1.c memwatch
            c -o test1



            當您運行 test1 程序后,它會生成一個關(guān)于泄漏的內(nèi)存的報告。清單 2 展示了示例 memwatch.log 輸出文件。

            清單 2. test1 memwatch.log 文件
            MEMWATCH 2.67 Copyright (C) 1992-1999 Johan Lindh

            ...
            double-free: <4> test1.c(15), 0x80517b4 was freed from test1.c(14)
            ...
            unfreed: <2> test1.c(11), 512 bytes at 0x80519e4
            {FE FE FE FE FE FE FE FE FE FE FE FE ..............}

            Memory usage statistics (global):
            N)umber of allocations made: 2
            L)argest memory usage : 1024
            T)otal of all alloc() calls: 1024
            U)nfreed bytes totals : 512



            MEMWATCH 為您顯示真正導(dǎo)致問題的行。如果您釋放一個已經(jīng)釋放過的指針,它會告訴您。對于沒有釋放的內(nèi)存也一樣。日志結(jié)尾部分顯示統(tǒng)計信息,包括泄漏了多少內(nèi)存,使用了多少內(nèi)存,以及總共分配了多少內(nèi)存。

            YAMD
            YAMD 軟件包由 Nate Eldredge 編寫,可以查找 C 和 C++ 中動態(tài)的、與內(nèi)存分配有關(guān)的問題。在撰寫本文時,YAMD 的最新版本為 0.32。請下載 yamd-0.32.tar.gz(請參閱參考資料)。執(zhí)行 make 命令來構(gòu)建程序;然后執(zhí)行 make install 命令安裝程序并設(shè)置工具。

            一旦您下載了 YAMD 之后,請在 test1.c 上使用它。請刪除 #include memwatch.h 并對 makefile 進行如下小小的修改:

            使用 YAMD 的 test1
            gcc -g test1.c -o test1



            清單 3 展示了來自 test1 上的 YAMD 的輸出。

            清單 3. 使用 YAMD 的 test1 輸出
            YAMD version 0.32
            Executable: /usr/src/test/yamd-0.32/test1
            ...
            INFO: Normal allocation of this block
            Address 0x40025e00, size 512
            ...
            INFO: Normal allocation of this block
            Address 0x40028e00, size 512
            ...
            INFO: Normal deallocation of this block
            Address 0x40025e00, size 512
            ...
            ERROR: Multiple freeing At
            free of pointer already freed
            Address 0x40025e00, size 512
            ...
            WARNING: Memory leak
            Address 0x40028e00, size 512
            WARNING: Total memory leaks:
            1 unfreed allocations totaling 512 bytes

            *** Finished at Tue ... 10:07:15 2002
            Allocated a grand total of 1024 bytes 2 allocations
            Average of 512 bytes per allocation
            Max bytes allocated at one time: 1024
            24 K alloced internally / 12 K mapped now / 8 K max
            Virtual program size is 1416 K
            End.



            YAMD 顯示我們已經(jīng)釋放了內(nèi)存,而且存在內(nèi)存泄漏。讓我們在清單 4 中另一個樣本程序上試試 YAMD。

            清單 4. 內(nèi)存代碼(test2.c)
            #include "STDLIB.H"
            #include "STDIO.H"

            int main(void)
            {
            char *ptr1;
            char *ptr2;
            char *chptr;
            int i = 1;
            ptr1 = malloc(512);
            ptr2 = malloc(512);
            chptr = (char *)malloc(512);
            for (i; i <= 512; i++) {
              chptr = 'S';
            }
            ptr2 = ptr1;
            free(ptr2);
            free(ptr1);
            free(chptr);
            }



            您可以使用下面的命令來啟動 YAMD:

            ./run-yamd /usr/src/test/test2/test2
            清單 5 顯示了在樣本程序 test2 上使用 YAMD 得到的輸出。YAMD 告訴我們在 for 循環(huán)中有“越界(out-of-bounds)”的情況。

            清單 5. 使用 YAMD 的 test2 輸出
            Running /usr/src/test/test2/test2
            Temp output to /tmp/yamd-out.1243
            *********
            ./run-yamd: line 101: 1248 Segmentation fault (core dumped)
            YAMD version 0.32
            Starting run: /usr/src/test/test2/test2
            Executable: /usr/src/test/test2/test2
            Virtual program size is 1380 K
            ...
            INFO: Normal allocation of this block
            Address 0x40025e00, size 512
            ...
            INFO: Normal allocation of this block
            Address 0x40028e00, size 512
            ...
            INFO: Normal allocation of this block
            Address 0x4002be00, size 512
            ERROR: Crash
            ...
            Tried to write address 0x4002c000
            Seems to be part of this block:
            Address 0x4002be00, size 512
            ...
            Address in question is at offset 512 (out of bounds)
            Will dump core after checking heap.
            Done.



            MEMWATCH 和 YAMD 都是很有用的調(diào)試工具,它們的使用方法有所不同。對于 MEMWATCH,您需要添加包含文件 memwatch.h 并打開兩個編譯時間標記。對于鏈接(link)語句,YAMD 只需要 -g 選項。

            Electric Fence
            多 數(shù) Linux 分發(fā)版包含一個 Electric Fence 包,不過您也可以選擇下載它。Electric Fence 是一個由 Bruce Perens 編寫的 malloc() 調(diào)試庫。它就在您分配內(nèi)存后分配受保護的內(nèi)存。如果存在 fencepost 錯誤(超過數(shù)組末尾運行),程序就會產(chǎn)生保護錯誤,并立即結(jié)束。通過結(jié)合 Electric Fence 和 gdb,您可以精確地跟蹤到哪一行試圖訪問受保護內(nèi)存。Electric Fence 的另一個功能就是能夠檢測內(nèi)存泄漏。

            第 2 種情況:使用 strace
            strace 命令是一種強大的工具,它能夠顯示所有由用戶空間程序發(fā)出的系統(tǒng)調(diào)用。strace 顯示這些調(diào)用的參數(shù)并返回符號形式的值。strace 從內(nèi)核接收信息,而且不需要以任何特殊的方式來構(gòu)建內(nèi)核。將跟蹤信息發(fā)送到應(yīng)用程序及內(nèi)核開發(fā)者都很有用。在清單 6 中,分區(qū)的一種格式有錯誤,清單顯示了 strace 的開頭部分,內(nèi)容是關(guān)于調(diào)出創(chuàng)建文件系統(tǒng)操作(mkfs)的。strace 確定哪個調(diào)用導(dǎo)致問題出現(xiàn)。

            清單 6. mkfs 上 strace 的開頭部分
            execve("/sbin/mkfs.jfs", ["mkfs.jfs", "-f", "/dev/test1"], &
            ...
            open("/dev/test1", O_RDWR|O_LARGEFILE) = 4
            stat64("/dev/test1", {st_mode=&, st_rdev=makedev(63, 255), ...}) = 0
            ioctl(4, 0x40041271, 0xbfffe128) = -1 EINVAL (Invalid argument)
            write(2, "mkfs.jfs: warning - cannot setb" ..., 98mkfs.jfs: warning -
            cannot set blocksize on block device /dev/test1: Invalid argument )
            = 98
            stat64("/dev/test1", {st_mode=&, st_rdev=makedev(63, 255), ...}) = 0
            open("/dev/test1", O_RDONLY|O_LARGEFILE) = 5
            ioctl(5, 0x80041272, 0xbfffe124) = -1 EINVAL (Invalid argument)
            write(2, "mkfs.jfs: can\'t determine device"..., ..._exit(1)
            = ?



            清 單 6 顯示 ioctl 調(diào)用導(dǎo)致用來格式化分區(qū)的 mkfs 程序失敗。ioctl BLKGETSIZE64 失敗。(BLKGET-SIZE64 在調(diào)用 ioctl 的源代碼中定義。) BLKGETSIZE64 ioctl 將被添加到 Linux 中所有的設(shè)備,而在這里,邏輯卷管理器還不支持它。因此,如果 BLKGETSIZE64 ioctl 調(diào)用失敗,mkfs 代碼將改為調(diào)用較早的 ioctl 調(diào)用;這使得 mkfs 適用于邏輯卷管理器。

            第 3 種情況:使用 gdb 和 Oops
            您可以從命令行使用 gdb 程序(Free Software Foundation 的調(diào)試器)來找出錯誤,也可以從諸如 Data Display Debugger(DDD)這樣的幾個圖形工具之一使用 gdb 程序來找出錯誤。您可以使用 gdb 來調(diào)試用戶空間程序或 Linux 內(nèi)核。這一部分只討論從命令行運行 gdb 的情況。

            使用 gdb program name 命令啟動 gdb。gdb 將載入可執(zhí)行程序符號并顯示輸入提示符,讓您可以開始使用調(diào)試器。您可以通過三種方式用 gdb 查看進程:

            使用 attach 命令開始查看一個已經(jīng)運行的進程;attach 將停止進程。


            使用 run 命令執(zhí)行程序并從頭開始調(diào)試程序。


            查看已有的核心文件來確定進程終止時的狀態(tài)。要查看核心文件,請用下面的命令啟動 gdb。
            gdb programname corefilename
            要用核心文件進行調(diào)試,您不僅需要程序的可執(zhí)行文件和源文件,還需要核心文件本身。要用核心文件啟動 gdb,請使用 -c 選項:

            gdb -c core programname

            gdb 顯示哪行代碼導(dǎo)致程序發(fā)生核心轉(zhuǎn)儲。

            在運行程序或連接到已經(jīng)運行的程序之前,請列出您覺得有錯誤的源代碼,設(shè)置斷點,然后開始調(diào)試程序。您可以使用 help 命令查看全面的 gdb 在線幫助和詳細的教程。

            kgdb
            kgdb 程序(使用 gdb 的遠程主機 Linux 內(nèi)核調(diào)試器)提供了一種使用 gdb 調(diào)試 Linux 內(nèi)核的機制。kgdb 程序是內(nèi)核的擴展,它讓您能夠在遠程主機上運行 gdb 時連接到運行用 kgdb 擴展的內(nèi)核機器。您可以接著深入到內(nèi)核中、設(shè)置斷點、檢查數(shù)據(jù)并進行其它操作(類似于您在應(yīng)用程序上使用 gdb 的方式)。這個補丁的主要特點之一就是運行 gdb 的主機在引導(dǎo)過程中連接到目標機器(運行要被調(diào)試的內(nèi)核)。這讓您能夠盡早開始調(diào)試。請注意,補丁為 Linux 內(nèi)核添加了功能,所以 gdb 可以用來調(diào)試 Linux 內(nèi)核。

            使用 kgdb 需要兩臺機器:一臺是開發(fā)機器,另一臺是測試機器。一條串行線(空調(diào)制解調(diào)器電纜)將通過機器的串口連接它們。您希望調(diào)試的內(nèi)核在測試機器上運行;gdb 在開發(fā)機器上運行。gdb 使用串行線與您要調(diào)試的內(nèi)核通信。

            請遵循下面的步驟來設(shè)置 kgdb 調(diào)試環(huán)境:


            下載您的 Linux 內(nèi)核版本適用的補丁。


            將 組件構(gòu)建到內(nèi)核,因為這是使用 kgdb 最簡單的方法。(請注意,有兩種方法可以構(gòu)建多數(shù)內(nèi)核組件,比如作為模塊或直接構(gòu)建到內(nèi)核中。舉例來說,日志紀錄文件系統(tǒng)(Journaled File System,JFS)可以作為模塊構(gòu)建,或直接構(gòu)建到內(nèi)核中。通過使用 gdb 補丁,我們就可以將 JFS 直接構(gòu)建到內(nèi)核中。)


            應(yīng)用內(nèi)核補丁并重新構(gòu)建內(nèi)核。


            創(chuàng)建一個名為 .gdbinit 的文件,并將其保存在內(nèi)核源文件子目錄中(換句話說就是 /usr/src/linux)。文件 .gdbinit 中有下面四行代碼:
            set remotebaud 115200
            symbol-file vmlinux
            target remote /dev/ttyS0
            set output-radix 16


            將 append=gdb 這一行添加到 lilo,lilo 是用來在引導(dǎo)內(nèi)核時選擇使用哪個內(nèi)核的引導(dǎo)載入程序。
            image=/boot/bzImage-2.4.17
            label=gdb2417
            read-only
            root=/dev/sda8
            append="gdb gdbttyS=1 gdb-baud=115200 nmi_watchdog=0"

            清單 7 是一個腳本示例,它將您在開發(fā)機器上構(gòu)建的內(nèi)核和模塊引入測試機器。您需要修改下面幾項:


            best@sfb:用戶標識和機器名。
            /usr/src/linux-2.4.17:內(nèi)核源代碼樹的目錄。
            bzImage-2.4.17:測試機器上將引導(dǎo)的內(nèi)核名。
            rcp 和 rsync:必須允許它在構(gòu)建內(nèi)核的機器上運行。

            清單 7. 引入測試機器的內(nèi)核和模塊的腳本
            set -x
            rcp best@sfb: /usr/src/linux-2.4.17/arch/i386/boot/bzImage /boot/bzImage-2.4.17
            rcp best@sfb:/usr/src/linux-2.4.17/System.map /boot/System.map-2.4.17
            rm -rf /lib/modules/2.4.17
            rsync -a best@sfb:/lib/modules/2.4.17 /lib/modules
            chown -R root /lib/modules/2.4.17
            lilo



            現(xiàn)在我們可以通過改為使用內(nèi)核源代碼樹開始的目錄來啟動開發(fā)機器上的 gdb 程序了。在本示例中,內(nèi)核源代碼樹位于 /usr/src/linux-2.4.17。輸入 gdb 啟動程序。

            如果一切正常,測試機器將在啟動過程中停止。輸入 gdb 命令 cont 以繼續(xù)啟動過程。一個常見的問題是,空調(diào)制解調(diào)器電纜可能會被連接到錯誤的串口。如果 gdb 不啟動,將端口改為第二個串口,這會使 gdb 啟動。

            使用 kgdb 調(diào)試內(nèi)核問題
            清單 8 列出了 jfs_mount.c 文件的源代碼中被修改過的代碼,我們在代碼中創(chuàng)建了一個空指針異常,從而使代碼在第 109 行產(chǎn)生段錯誤。

            清單 8. 修改過后的 jfs_mount.c 代碼
            int jfs_mount(struct super_block *sb)
            {
            ...
            int ptr; /* line 1 added */
            jFYI(1, ("\nMount JFS\n"));
            / *
            * read/validate superblock
            * (initialize mount inode from the superblock)
            * /
            if ((rc = chkSuper(sb))) {
            goto errout20;
            }
            108 ptr=0; /* line 2 added */
            109 printk("%d\n",*ptr); /* line 3 added */



            清 單 9 在向文件系統(tǒng)發(fā)出 mount 命令之后顯示一個 gdb 異常。kgdb 提供了幾條命令,如顯示數(shù)據(jù)結(jié)構(gòu)和變量值以及顯示系統(tǒng)中的所有任務(wù)處于什么狀態(tài)、它們駐留在何處、它們在哪些地方使用了 CPU 等等。清單 9 將顯示回溯跟蹤為該問題提供的信息;where 命令用來執(zhí)行反跟蹤,它將告訴被執(zhí)行的調(diào)用在代碼中的什么地方停止。

            清單 9. gdb 異常和反跟蹤
            mount -t jfs /dev/sdb /jfs

            Program received signal SIGSEGV, Segmentation fault.
            jfs_mount (sb=0xf78a3800) at jfs_mount.c:109
            109 printk("%d\n",*ptr);
            (gdb)where
            #0 jfs_mount (sb=0xf78a3800) at jfs_mount.c:109
            #1 0xc01a0dbb in jfs_read_super ... at super.c:280
            #2 0xc0149ff5 in get_sb_bdev ... at super.c:620
            #3 0xc014a89f in do_kern_mount ... at super.c:849
            #4 0xc0160e66 in do_add_mount ... at namespace.c:569
            #5 0xc01610f4 in do_mount ... at namespace.c:683
            #6 0xc01611ea in sys_mount ... at namespace.c:716
            #7 0xc01074a7 in system_call () at af_packet.c:1891
            #8 0x0 in ?? ()
            (gdb)



            下一部分還將討論這個相同的 JFS 段錯誤問題,但不設(shè)置調(diào)試器,如果您在非 kgdb 內(nèi)核環(huán)境中執(zhí)行清單 8 中的代碼,那么它使用內(nèi)核可能生成的 Oops 消息。

            Oops 分析
            Oops (也稱 panic,慌張)消息包含系統(tǒng)錯誤的細節(jié),如 CPU 寄存器的內(nèi)容。在 Linux 中,調(diào)試系統(tǒng)崩潰的傳統(tǒng)方法是分析在發(fā)生崩潰時發(fā)送到系統(tǒng)控制臺的 Oops 消息。一旦您掌握了細節(jié),就可以將消息發(fā)送到 ksymoops 實用程序,它將試圖將代碼轉(zhuǎn)換為指令并將堆棧值映射到內(nèi)核符號。在很多情況下,這些信息就足夠您確定錯誤的可能原因是什么了。請注意,Oops 消息并不包括核心文件。

            讓我們假設(shè)系統(tǒng)剛剛創(chuàng)建了一條 Oops 消息。作為編寫代碼的人,您希望解決問題并確定什么導(dǎo)致了 Oops 消息的產(chǎn)生,或者您希望向顯示了 Oops 消息的代碼的開發(fā)者提供有關(guān)您的問題的大部分信息,從而及時地解決問題。Oops 消息是等式的一部分,但如果不通過 ksymoops 程序運行它也于事無補。下面的圖顯示了格式化 Oops 消息的過程。

            格式化 Oops 消息



            ksymoops 需要幾項內(nèi)容:Oops 消息輸出、來自正在運行的內(nèi)核的 System.map 文件,還有 /proc/ksyms、vmlinux 和 /proc/modules。關(guān)于如何使用 ksymoops,內(nèi)核源代碼 /usr/src/linux/Documentation/oops-tracing.txt 中或 ksymoops 手冊頁上有完整的說明可以參考。Ksymoops 反匯編代碼部分,指出發(fā)生錯誤的指令,并顯示一個跟蹤部分表明代碼如何被調(diào)用。

            首先,將 Oops 消息保存在一個文件中以便通過 ksymoops 實用程序運行它。清單 10 顯示了由安裝 JFS 文件系統(tǒng)的 mount 命令創(chuàng)建的 Oops 消息,問題是由清單 8 中添加到 JFS 安裝代碼的那三行代碼產(chǎn)生的。

            清單 10. ksymoops 處理后的 Oops 消息
            ksymoops 2.4.0 on i686 2.4.17. Options used
            ... 15:59:37 sfb1 kernel: Unable to handle kernel NULL pointer dereference at
            virtual address 0000000
            ... 15:59:37 sfb1 kernel: c01588fc
            ... 15:59:37 sfb1 kernel: *pde = 0000000
            ... 15:59:37 sfb1 kernel: Oops: 0000
            ... 15:59:37 sfb1 kernel: CPU:   0
            ... 15:59:37 sfb1 kernel: EIP:   0010:[jfs_mount+60/704]

            ... 15:59:37 sfb1 kernel: Call Trace: [jfs_read_super+287/688]
            [get_sb_bdev+563/736] [do_kern_mount+189/336] [do_add_mount+35/208]
            [do_page_fault+0/1264]
            ... 15:59:37 sfb1 kernel: Call Trace: []...
            ... 15:59:37 sfb1 kernel: [>EIP; c01588fc <=====
            ...
            Trace; c0106cf3
            Code; c01588fc
            00000000 <_EIP>:
            Code; c01588fc   <=====
              0: 8b 2d 00 00 00 00 mov 0x0,%ebp   <=====
            Code; c0158902
              6: 55 push %ebp



            接 下來,您要確定 jfs_mount 中的哪一行代碼引起了這個問題。Oops 消息告訴我們問題是由位于偏移地址 3c 的指令引起的。做這件事的辦法之一是對 jfs_mount.o 文件使用 objdump 實用程序,然后查看偏移地址 3c。Objdump 用來反匯編模塊函數(shù),看看您的 C 源代碼會產(chǎn)生什么匯編指令。清單 11 顯示了使用 objdump 后您將看到的內(nèi)容,接著,我們查看 jfs_mount 的 C 代碼,可以看到空值是第 109 行引起的。偏移地址 3c 之所以很重要,是因為 Oops 消息將該處標識為引起問題的位置。

            清單 11. jfs_mount 的匯編程序清單
            109 printk("%d\n",*ptr);

            objdump jfs_mount.o

            jfs_mount.o: file format elf32-i386

            Disassembly of section .text:

            00000000 :
              0:55 push %ebp
            ...
            2c: e8 cf 03 00 00   call   400
            31: 89 c3     mov   %eax,%ebx
            33: 58   pop   %eax
            34: 85 db     test %ebx,%ebx
            36: 0f 85 55 02 00 00 jne 291
            3c: 8b 2d 00 00 00 00 mov 0x0,%ebp << problem line above
            42: 55 push %ebp



            kdb
            Linux 內(nèi)核調(diào)試器(Linux kernel debugger,kdb)是 Linux 內(nèi)核的補丁,它提供了一種在系統(tǒng)能運行時對內(nèi)核內(nèi)存和數(shù)據(jù)結(jié)構(gòu)進行檢查的辦法。請注意,kdb 不需要兩臺機器,不過它也不允許您像 kgdb 那樣進行源代碼級別上的調(diào)試。您可以添加額外的命令,給出該數(shù)據(jù)結(jié)構(gòu)的標識或地址,這些命令便可以格式化和顯示基本的系統(tǒng)數(shù)據(jù)結(jié)構(gòu)。目前的命令集允許您控 制包括以下操作在內(nèi)的內(nèi)核操作:


            處理器單步執(zhí)行
            執(zhí)行到某條特定指令時停止
            當存?。ɑ蛐薷模┠硞€特定的虛擬內(nèi)存位置時停止
            當存取輸入/輸出地址空間中的寄存器時停止
            對當前活動的任務(wù)和所有其它任務(wù)進行堆?;厮莞櫍ㄍㄟ^進程 ID)
            對指令進行反匯編

            追擊內(nèi)存溢出

            您肯定不想陷入類似在幾千次調(diào)用之后發(fā)生分配溢出這樣的情形。

            我 們的小組花了許許多多時間來跟蹤稀奇古怪的內(nèi)存錯誤問題。應(yīng)用程序在我們的開發(fā)工作站上能運行,但在新的產(chǎn)品工作站上,這個應(yīng)用程序在調(diào)用 malloc() 兩百萬次之后就不能運行了。真正的問題是在大約一百萬次調(diào)用之后發(fā)生了溢出。新系統(tǒng)之所有存在這個問題,是因為被保留的 malloc() 區(qū)域的布局有所不同,從而這些零散內(nèi)存被放置在了不同的地方,在發(fā)生溢出時破壞了一些不同的內(nèi)容。

            我們用多種不同技術(shù) 來解決這個問題,其中一種是使用調(diào)試器,另一種是在源代碼中添加跟蹤功能。在我職業(yè)生涯的大概也是這個時候,我便開始關(guān)注內(nèi)存調(diào)試工具,希望能更快更有效 地解決這些類型的問題。在開始一個新項目時,我最先做的事情之一就是運行 MEMWATCH 和 YAMD,看看它們是不是會指出內(nèi)存管理方面的問題。

            內(nèi)存泄漏是應(yīng)用程序中常見的問題,不過您可以使用本文所講述的工具來解決這些問題。

            第 4 種情況:使用魔術(shù)鍵控順序進行回溯跟蹤
            如果在 Linux 掛起時您的鍵盤仍然能用,那請您使用以下方法來幫助解決掛起問題的根源。遵循這些步驟,您便可以顯示當前運行的進程和所有使用魔術(shù)鍵控順序的進程的回溯跟蹤。


            您正在運行的內(nèi)核必須是在啟用 CONFIG_MAGIC_SYS-REQ 的情況下構(gòu)建的。您還必須處在文本模式。CLTR+ALT+F1 會使您進入文本模式,CLTR+ALT+F7 會使您回到 X Windows。
            當在文本模式時,請按 ,然后按 。上述魔術(shù)的擊鍵會分別給出當前運行的進程和所有進程的堆棧跟蹤。
            請查找 /var/log/messages。如果一切設(shè)置正確,則系統(tǒng)應(yīng)該已經(jīng)為您轉(zhuǎn)換了內(nèi)核的符號地址?;厮莞檶⒈粚懙?/var/log/messages 文件中。

            結(jié)束語
            幫助調(diào)試 Linux 上的程序有許多不同的工具可供使用。本文講述的工具可以幫助您解決許多編碼問題。能顯示內(nèi)存泄漏、溢出等等的位置的工具可以解決內(nèi)存管理問題,我發(fā)現(xiàn) MEMWATCH 和 YAMD 很有幫助。

            使 用 Linux 內(nèi)核補丁會使 gdb 能在 Linux 內(nèi)核上工作,這對解決我工作中使用的 Linux 的文件系統(tǒng)方面的問題很有幫助。此外,跟蹤實用程序能幫助確定在系統(tǒng)調(diào)用期間文件系統(tǒng)實用程序什么地方出了故障。下次當您要擺平 Linux 中的錯誤時,請試試這些工具中的某一個。

            posted on 2008-03-14 13:58 隨意門 閱讀(240) 評論(0)  編輯 收藏 引用


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            看久久久久久a级毛片| 久久久一本精品99久久精品66| segui久久国产精品| 久久亚洲AV成人无码国产| 色婷婷久久久SWAG精品| 久久综合综合久久97色| 国内精品综合久久久40p| 99久久无码一区人妻a黑| 综合久久一区二区三区 | 精品久久亚洲中文无码| 久久久久亚洲AV无码专区首JN | 亚洲AV无码1区2区久久| 99久久综合狠狠综合久久止| 久久久精品视频免费观看| 国产午夜福利精品久久2021| 色婷婷狠狠久久综合五月| 四虎国产精品免费久久5151| 性高湖久久久久久久久| 国内精品久久久久久久亚洲 | 久久精品无码专区免费青青| 欧洲性大片xxxxx久久久| 99久久久国产精品免费无卡顿| 伊人 久久 精品| 国产精品成人99久久久久 | 精品久久久久久久中文字幕| 国产Av激情久久无码天堂| 亚洲AV无码久久精品色欲| 久久久这里有精品| 亚洲精品美女久久久久99小说| 国产亚州精品女人久久久久久 | 丁香五月网久久综合| 久久99精品久久久久久动态图| 久久人人添人人爽添人人片牛牛| 日韩欧美亚洲综合久久影院Ds | 亚洲欧美精品伊人久久| 久久电影网一区| 久久综合久久综合九色| 91精品国产91久久久久久蜜臀| 中文字幕成人精品久久不卡| 久久久久无码中| 久久久久久伊人高潮影院|