從用戶態(tài)程序開(kāi)發(fā)的角度,我們并不需要理解操作系統(tǒng)復(fù)雜的內(nèi)存管理機(jī)制,這是和硬件平臺(tái)相關(guān)的。但是了解內(nèi)核發(fā)送SIGSEGV信號(hào)的流程,對(duì)我們理解SIGSEGV是很有幫助的。
紅色部分展示了內(nèi)核發(fā)送SIGSEGV 信號(hào)給用戶態(tài)程序的總體流程。當(dāng)用戶態(tài) 程序訪問(wèn)一個(gè)會(huì)引發(fā)SIGSEGV 的地址時(shí),硬件首先產(chǎn)生一個(gè)page fault,即“缺頁(yè)異常”。 在內(nèi)核的page fault 處理函數(shù)中,首先判斷該地址是否屬于用戶態(tài)程序的地址空間。32位系統(tǒng)中虛擬存儲(chǔ)空間占4GB空間。linux內(nèi)核將這4G字節(jié)的空間分為兩部分。用戶態(tài)程序的地址空間為[0x00000000,0xBFFFFFFF],內(nèi)核地址空間為[0xC0000000,0xFFFFFFFF]。如果該地址屬于用戶態(tài)地址空間,檢查訪問(wèn)的類型是否和該內(nèi)存區(qū)域的類型是否匹配,不匹配,則發(fā)送SIGSEGV 信號(hào);如果該地址不屬于用戶態(tài)地址空間,檢查訪問(wèn)該地址的操作是否發(fā)生在用戶態(tài),如果是,發(fā)送SIGSEGV 信號(hào)。 當(dāng)程序不正常退出時(shí),內(nèi)核會(huì)在當(dāng)前目錄產(chǎn)生core文件(一個(gè)內(nèi)存的映像,同時(shí)加上調(diào)試信息),然后利用gdb查看core文件,可以指示出導(dǎo)致程序出錯(cuò)位置的文件和行數(shù)。
確定系統(tǒng)是否配置支持dump core的功能。通過(guò)ulimit -c或ulimit -a,如果為0,則不會(huì)產(chǎn)生對(duì)應(yīng)的coredump,需要進(jìn)行修改和設(shè)置。使用 ulimit –c [size]這里的size的單位是blocks,一般1block=512bytes在不知 size的大小的時(shí)候,可以u(píng)limit -c unlimited。
1.用gcc(或者交叉編譯工具)進(jìn)行編譯時(shí),選擇-g選項(xiàng)。 2.不能進(jìn)行strip操作,否則你將看不見(jiàn)程序的函數(shù)名、變量名,代替這些將是運(yùn)行時(shí)的內(nèi)存地址。
core文件默認(rèn)的存儲(chǔ)位置與對(duì)應(yīng)的可執(zhí)行程序在同一目錄下,文件名是core,大家可以通過(guò)下面的命令看到core文件的存儲(chǔ)位置和格式:cat /PRoc/sys/kernel/core_pattern。 其中core_pattern的格式見(jiàn)下面說(shuō)明: %% 單個(gè)%字符 %p 所dump進(jìn)程的進(jìn)程ID %u 所dump進(jìn)程的實(shí)際用戶ID %g 所dump進(jìn)程的實(shí)際組ID %s 導(dǎo)致本次core dump的信號(hào) %t core dump的時(shí)間 (由1970年1月1日計(jì)起的秒數(shù)) %h 主機(jī)名 %e 程序文件名 其中/proc/sys/kernel/core_uses_pid。如果這個(gè)文件的內(nèi)容被配置成1,即使core_pattern中沒(méi)有設(shè)置%p,最后生成的core dump文件名仍會(huì)加上進(jìn)程ID。以下是參數(shù)設(shè)置例子:
echo "1" > /proc/sys/kernel/core_uses_pid sysctl -w kernel.core_uses_pid=1; echo "/persistent/core-%e-%p-%t" > core_pattern; sysctl -w kernel.core_pattern=/persistent/core-%e-%p-%t注意:這里是指在進(jìn)程當(dāng)前工作目錄的下創(chuàng)建。通常與程序在相同的路徑下。但如果程序中調(diào)用了chdir函數(shù),則有可能改變了當(dāng)前工作目錄。這時(shí)core文件創(chuàng)建在chdir指定的路徑下。有好多程序崩潰了,我們卻找不到core文件放在什么位置。和chdir函數(shù)就有關(guān)系。當(dāng)然程序崩潰了不一定都產(chǎn)生core文件。
( a )進(jìn)程是設(shè)置-用戶-ID,而且當(dāng)前用戶并非程序文件的所有者 ( b )進(jìn)程是設(shè)置-組-ID,而且當(dāng)前用戶并非該程序文件的組所有者 ( c )用戶沒(méi)有寫當(dāng)前工作目錄的許可權(quán) ( d )文件太大。core文件的許可權(quán)(假定該文件在此之前并不存在)通常是用戶讀/,組讀和其他讀 友情提醒:當(dāng)環(huán)境當(dāng)好后,可以使用在終端執(zhí)行 “kill -11 應(yīng)用程序PID”命令,然后查看是否回生成core文件。
發(fā)生core dump之后, 用gdb進(jìn)行查看core文件的內(nèi)容, 以定位文件中引發(fā)段錯(cuò)誤的行。如下:
gdb [exec file] [core file]如: arm-none-linux-gnueabi-gdb queuetest core執(zhí)行結(jié)果如下:root@silent:/home/lianxi# arm-none-linux-gnueabi-gdb queuetest coreGNU gdb (Sourcery G++ Lite 2009q1-203) 6.8.50.20081022-cvsCopyright (C) 2008 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "--host=i686-pc-linux-gnu --target=arm-none-linux-gnueabi".For bug reporting instructions, please see:<https://support.codesourcery.com/GNUToolchain/>...Error while mapping shared library sections:lib/libdl.so.2: No such file or directory.Error while mapping shared library sections:lib/libgcc_s.so.1: No such file or directory.Error while mapping shared library sections:lib/libc.so.6: No such file or directory.Error while mapping shared library sections:lib/ld-linux.so.3: No such file or directory.Symbol file not found for /lib/libdl.so.2Symbol file not found for /lib/libgcc_s.so.1Symbol file not found for /lib/libc.so.6Symbol file not found for /lib/ld-linux.so.3warning: Unable to find dynamic linker.GDB will be unable to debug shared library initializersand track explicitly loaded dynamic code.Core was generated by `./queuetest'.Program terminated with signal 11, Segmentation fault.#0 0x00008548 in main (argc=1, argv=0xbeb26e24) at main.c:3838 printf("ptr = %d/n", *ptr);(gdb) where#0 0x00008548 in main (argc=1, argv=0xbeb26e24) at main.c:38gdb中鍵入where,就會(huì)看到程序崩潰時(shí)堆棧信息(當(dāng)前函數(shù)之前的所有已調(diào)用函數(shù)的列表),顯然錯(cuò)誤出現(xiàn)main.c:38中。 但往往調(diào)試沒(méi)這么簡(jiǎn)單,所以還得記住幾個(gè)常用的gdb命令,結(jié)合查看分析 bt 查看堆棧信息 bt full 完全顯示函數(shù)之間相互調(diào)用時(shí)傳遞的參數(shù)值和函數(shù)的內(nèi)部變量值 where 顯示在哪兒down掉 info locals 顯示目前的區(qū)域參數(shù) info threads 顯示當(dāng)前可調(diào)試的所有線程,每個(gè)線程會(huì)有一個(gè)gdb為其分配的ID,后面操作線程的時(shí)候會(huì)用這個(gè)ID,前面有*的是當(dāng)前調(diào)試的線程。 thread id 切換當(dāng)前調(diào)試的線程為指定ID的線程 list 往下列出代碼 info frame 查看楨的詳細(xì)信息 thread apply all command 讓所有被調(diào)試線程執(zhí)行g(shù)db命令command x/nfu 查看內(nèi)存信息
注:在交叉編譯環(huán)境中,經(jīng)常發(fā)現(xiàn)庫(kù)找不到的情況,這是因?yàn)闆](méi)有指定庫(kù)路徑的原因。可以通過(guò)以下兩個(gè)命令設(shè)置。
set solib-absolute-prefix 路徑set solib-search-path 路徑新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注