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

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

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

在Linux上什么是段錯誤?如何獲得一個核心轉(zhuǎn)儲?

Linux愛好者 ? 來源:未知 ? 作者:李倩 ? 2018-07-16 16:53 ? 次閱讀

本周工作中,我花了整整一周的時間來嘗試調(diào)試一個段錯誤。我以前從來沒有這樣做過,我花了很長時間才弄清楚其中涉及的一些基本事情(獲得核心轉(zhuǎn)儲、找到導(dǎo)致段錯誤的行號)。于是便有了這篇博客來解釋如何做那些事情!

在看完這篇博客后,你應(yīng)該知道如何從“哦,我的程序出現(xiàn)段錯誤,但我不知道正在發(fā)生什么”到“我知道它出現(xiàn)段錯誤時的堆棧、行號了! ”。

什么是段錯誤?

“段錯誤segmentation fault”是指你的程序嘗試訪問不允許訪問的內(nèi)存地址的情況。這可能是由于:

試圖解引用空指針(你不被允許訪問內(nèi)存地址0);

試圖解引用其他一些不在你內(nèi)存(LCTT 譯注:指不在合法的內(nèi)存地址區(qū)間內(nèi))中的指針;

一個已被破壞并且指向錯誤的地方的 C++ 虛表指針C++ vtable pointer,這導(dǎo)致程序嘗試執(zhí)行沒有執(zhí)行權(quán)限的內(nèi)存中的指令;

其他一些我不明白的事情,比如我認為訪問未對齊的內(nèi)存地址也可能會導(dǎo)致段錯誤(LCTT 譯注:在要求自然邊界對齊的體系結(jié)構(gòu),如 MIPS、ARM 中更容易因非對齊訪問產(chǎn)生段錯誤)。

這個“C++ 虛表指針”是我的程序發(fā)生段錯誤的情況。我可能會在未來的博客中解釋這個,因為我最初并不知道任何關(guān)于 C++ 的知識,并且這種虛表查找導(dǎo)致程序段錯誤的情況也是我所不了解的。

但是!這篇博客后不是關(guān)于 C++ 問題的。讓我們談?wù)摰幕镜臇|西,比如,我們?nèi)绾蔚玫揭粋€核心轉(zhuǎn)儲?

運行 valgrind

我發(fā)現(xiàn)找出為什么我的程序出現(xiàn)段錯誤的最簡單的方式是使用valgrind:我運行

valgrind -vyour-program

這給了我一個故障時的堆棧調(diào)用序列。 簡潔!

但我想也希望做一個更深入調(diào)查,并找出些valgrind沒告訴我的信息! 所以我想獲得一個核心轉(zhuǎn)儲并探索它。

如何獲得一個核心轉(zhuǎn)儲

核心轉(zhuǎn)儲core dump是您的程序內(nèi)存的一個副本,并且當(dāng)您試圖調(diào)試您的有問題的程序哪里出錯的時候它非常有用。

當(dāng)您的程序出現(xiàn)段錯誤,Linux 的內(nèi)核有時會把一個核心轉(zhuǎn)儲寫到磁盤。 當(dāng)我最初試圖獲得一個核心轉(zhuǎn)儲時,我很長一段時間非常沮喪,因為 – Linux 沒有生成核心轉(zhuǎn)儲!我的核心轉(zhuǎn)儲在哪里?

這就是我最終做的事情:

在啟動我的程序之前運行ulimit -c unlimited

運行sudo sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t

ulimit:設(shè)置核心轉(zhuǎn)儲的最大尺寸

ulimit -c設(shè)置核心轉(zhuǎn)儲的最大尺寸。 它往往設(shè)置為 0,這意味著內(nèi)核根本不會寫核心轉(zhuǎn)儲。 它以千字節(jié)為單位。ulimit是按每個進程分別設(shè)置的 —— 你可以通過運行cat /proc/PID/limit看到一個進程的各種資源限制。

例如這些是我的系統(tǒng)上一個隨便一個 Firefox 進程的資源限制:

內(nèi)核在決定寫入多大的核心轉(zhuǎn)儲文件時使用軟限制soft limit(在這種情況下,max core file size = 0)。 您可以使用 shell 內(nèi)置命令 ulimit(ulimit -c unlimited) 將軟限制增加到硬限制hard limit。

kernel.core_pattern:核心轉(zhuǎn)儲保存在哪里

kernel.core_pattern 是一個內(nèi)核參數(shù),或者叫 “sysctl 設(shè)置”,它控制 Linux 內(nèi)核將核心轉(zhuǎn)儲文件寫到磁盤的哪里。

內(nèi)核參數(shù)是一種設(shè)定您的系統(tǒng)全局設(shè)置的方法。您可以通過運行 sysctl -a 得到一個包含每個內(nèi)核參數(shù)的列表,或使用 sysctl kernel.core_pattern 來專門查看 kernel.core_pattern 設(shè)置。

所以 sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t 將核心轉(zhuǎn)儲保存到目錄 /tmp 下,并以 core 加上一系列能夠標(biāo)識(出故障的)進程的參數(shù)構(gòu)成的后綴為文件名。

如果你想知道這些形如 %e、%p 的參數(shù)都表示什么,請參考 man core。

有一點很重要,kernel.core_pattern 是一個全局設(shè)置 —— 修改它的時候最好小心一點,因為有可能其它系統(tǒng)功能依賴于把它被設(shè)置為一個特定的方式(才能正常工作)。

kernel.core_pattern 和 Ubuntu

默認情況下在 ubuntu 系統(tǒng)中,kernel.core_pattern 被設(shè)置為下面的值:

$sysctl kernel.core_pattern

kernel.core_pattern = |/usr/share/apport/apport %p %s %c %d %P

這引起了我的迷惑(這 apport 是干什么的,它對我的核心轉(zhuǎn)儲做了什么?)。以下關(guān)于這個我了解到的:

Ubuntu 使用一種叫做 apport 的系統(tǒng)來報告 apt 包有關(guān)的崩潰信息。

設(shè)定kernel.core_pattern=|/usr/share/apport/apport %p %s %c %d %P意味著核心轉(zhuǎn)儲將被通過管道送給apport程序。

apport 的日志保存在文件/var/log/apport.log中。

apport 默認會忽略來自不屬于 Ubuntu 軟件包一部分的二進制文件的崩潰信息

我最終只是跳過了 apport,并把kernel.core_pattern重新設(shè)置為sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t,因為我在一臺開發(fā)機上,我不在乎 apport 是否工作,我也不想嘗試讓 apport 把我的核心轉(zhuǎn)儲留在磁盤上。

現(xiàn)在你有了核心轉(zhuǎn)儲,接下來干什么?

好的,現(xiàn)在我們了解了ulimit和kernel.core_pattern,并且實際上在磁盤的/tmp目錄中有了一個核心轉(zhuǎn)儲文件。太好了!接下來干什么?我們?nèi)匀徊恢涝摮绦驗槭裁磿霈F(xiàn)段錯誤!

下一步將使用gdb打開核心轉(zhuǎn)儲文件并獲取堆棧調(diào)用序列。

從 gdb 中得到堆棧調(diào)用序列

你可以像這樣用gdb打開一個核心轉(zhuǎn)儲文件:

$gdb -cmy_core_file

接下來,我們想知道程序崩潰時的堆棧是什么樣的。在 gdb 提示符下運行 bt 會給你一個調(diào)用序列backtrace。在我的例子里,gdb 沒有為二進制文件加載符號信息,所以這些函數(shù)名就像 “??????”。幸運的是,(我們通過)加載符號修復(fù)了它。

下面是如何加載調(diào)試符號。

symbol-file /path/to/my/binary

sharedlibrary

這從二進制文件及其引用的任何共享庫中加載符號。一旦我這樣做了,當(dāng)我執(zhí)行 bt 時,gdb 給了我一個帶有行號的漂亮的堆棧跟蹤!

如果你想它能工作,二進制文件應(yīng)該以帶有調(diào)試符號信息的方式被編譯。在試圖找出程序崩潰的原因時,堆棧跟蹤中的行號非常有幫助。:)

查看每個線程的堆棧

通過以下方式在 gdb 中獲取每個線程的調(diào)用棧!

thread apply all bt full

gdb + 核心轉(zhuǎn)儲 = 驚喜

如果你有一個帶調(diào)試符號的核心轉(zhuǎn)儲以及gdb,那太棒了!您可以上下查看調(diào)用堆棧(LCTT 譯注:指跳進調(diào)用序列不同的函數(shù)中以便于查看局部變量),打印變量,并查看內(nèi)存來得知發(fā)生了什么。這是最好的。

如果您仍然正在基于 gdb 向?qū)砉ぷ魃?,只打印出棧跟蹤與bt也可以。 :)

ASAN

另一種搞清楚您的段錯誤的方法是使用 AddressSanitizer 選項編譯程序(“ASAN”,即$CC -fsanitize=address)然后運行它。 本文中我不準(zhǔn)備討論那個,因為本文已經(jīng)相當(dāng)長了,并且在我的例子中打開 ASAN 后段錯誤消失了,可能是因為 ASAN 使用了一個不同的內(nèi)存分配器(系統(tǒng)內(nèi)存分配器,而不是 tcmalloc)。

在未來如果我能讓 ASAN 工作,我可能會多寫點有關(guān)它的東西。(LCTT 譯注:這里指使用 ASAN 也能復(fù)現(xiàn)段錯誤)

從一個核心轉(zhuǎn)儲得到一個堆棧跟蹤真的很親切!

這個博客聽起來很多,當(dāng)我做這些的時候很困惑,但說真的,從一個段錯誤的程序中獲得一個堆棧調(diào)用序列不需要那么多步驟:

試試用valgrind

如果那沒用,或者你想要拿到一個核心轉(zhuǎn)儲來調(diào)查:

確保二進制文件編譯時帶有調(diào)試符號信息;

正確的設(shè)置ulimit和kernel.core_pattern;

運行程序;

一旦你用gdb調(diào)試核心轉(zhuǎn)儲了,加載符號并運行bt;

嘗試找出發(fā)生了什么!

我可以使用gdb弄清楚有個 C++ 的虛表條目指向一些被破壞的內(nèi)存,這有點幫助,并且使我感覺好像更懂了 C++ 一點。也許有一天我們會更多地討論如何使用gdb來查找問題!

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

    關(guān)注

    87

    文章

    11304

    瀏覽量

    209543
  • C++
    C++
    +關(guān)注

    關(guān)注

    22

    文章

    2108

    瀏覽量

    73657
  • 線程
    +關(guān)注

    關(guān)注

    0

    文章

    504

    瀏覽量

    19687

原文標(biāo)題:在 Linux 上如何得到一個段錯誤的核心轉(zhuǎn)儲

文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    C程序中10與內(nèi)存有關(guān)的常見錯誤

    與內(nèi)存有關(guān)的錯誤,屬于那種最令人驚恐的錯誤時間和空間,經(jīng)常在距離錯誤一段距離之后才表現(xiàn)出
    發(fā)表于 06-20 10:41 ?692次閱讀

    有沒有辦法UART禁用引導(dǎo)加載程序轉(zhuǎn)啟動信息?

    有沒有辦法 UART 禁用引導(dǎo)加載程序轉(zhuǎn)啟動信息?我應(yīng)該改用 UART1 嗎? 我將創(chuàng)建
    發(fā)表于 07-12 13:20

    安裝Z-Stack_Linux_Gateway_1_0_1_installer.run出錯,請問是系統(tǒng)不支持嗎?

    本帖最后由 只耳朵怪 于 2018-5-24 11:28 編輯 我ubuntu12.04 32位的系統(tǒng)安裝Z-Stack_Linux_Gateway_1_0_1_insta
    發(fā)表于 05-22 08:38

    Linux上升級到ISE 11.3出現(xiàn)總線錯誤

    在下載11.3的tar文件后,我linux 32位機器從ISE 11.2更新了ISE 11.3。雖然更新成功,但是當(dāng)我運行bin / lin / ise時它在終端上給出了以下內(nèi)容:總線錯誤
    發(fā)表于 11-19 14:41

    Vivado 2014.1SLES10安裝失敗

    問題的框架:#C [ld-linux-x86-64.so .2 + 0x882f] do_lookup_x + 0xcf ##無法寫入核心轉(zhuǎn)。核心
    發(fā)表于 12-07 11:18

    位文件轉(zhuǎn)后合成設(shè)計更改

    合成設(shè)計時,所有FDCE端口都正確連接。但是將代碼轉(zhuǎn)到目標(biāo)之后,我得到的輸出是錯誤的。但是,如果我第二次轉(zhuǎn)
    發(fā)表于 04-16 07:46

    轉(zhuǎn)用于VHDL代碼的saif文件

    一直在使用xilinx vivado來測試些代碼。這些代碼通常用于無矢量功率估計。但是,當(dāng)我嘗試從模擬設(shè)置轉(zhuǎn)saif文件時,我收到以下錯誤
    發(fā)表于 04-20 10:14

    Vivado 2016.2錯誤核心訪問失敗的原因?

    你好我有設(shè)計合成和實現(xiàn)沒有任何計時錯誤。但是當(dāng)我轉(zhuǎn)比特流后觸發(fā)ILA時,出現(xiàn)了這個
    發(fā)表于 06-04 16:32

    有什么方法可以追溯地將加密標(biāo)志應(yīng)用于現(xiàn)有加密設(shè)備核心轉(zhuǎn)分區(qū)?

    我有生產(chǎn)固件的設(shè)備。我們啟用了閃存加密,并啟用了核心轉(zhuǎn)。不幸的是,我們沒有文檔中的任何地
    發(fā)表于 03-02 08:01

    Ubuntu 16.04系統(tǒng)中調(diào)試Apollo項目核心轉(zhuǎn)文件的方法

    核心轉(zhuǎn)對于程序員調(diào)試程序非常有益,因為有些程序錯誤是很難重現(xiàn)的,例如指針異常,而核心轉(zhuǎn)
    的頭像 發(fā)表于 03-23 09:30 ?7031次閱讀

    什么是錯誤?

    。 這個內(nèi)存區(qū)要么是不存在的,要么是受到系統(tǒng)保護的,還有可能是缺少文件或者文件損壞。 二、錯誤產(chǎn)生的原因下面是些典型的錯誤的原因: 非
    的頭像 發(fā)表于 09-11 14:04 ?1.7w次閱讀

    內(nèi)核轉(zhuǎn)的設(shè)置

    當(dāng)程序運行的過程中異常終止或崩潰,操作系統(tǒng)會將程序當(dāng)時的內(nèi)存狀態(tài)記錄下來,保存在文件中,這種行為就叫做 Core Dump(中文有的翻譯成“核心轉(zhuǎn)
    的頭像 發(fā)表于 12-19 13:42 ?727次閱讀

    教你Linux進度條小程序

    Linux 寫下一個簡易的進度條小程序。
    的頭像 發(fā)表于 01-29 11:37 ?1288次閱讀

    錯誤是什么意思?是何原因引起的?

    剛接觸指針的時候,經(jīng)常會遇到錯誤。所謂錯誤,就是訪問了不能訪問的內(nèi)存。
    的頭像 發(fā)表于 02-13 09:41 ?3345次閱讀

    Linux內(nèi)核Linux系統(tǒng)中到底處于什么樣的地位

    Linux操作系統(tǒng)是當(dāng)今世界最為廣泛使用的開源操作系統(tǒng)之,內(nèi)核則是操作系統(tǒng)的核心和靈魂所
    的頭像 發(fā)表于 07-06 11:48 ?1205次閱讀
    <b class='flag-5'>Linux</b>內(nèi)核<b class='flag-5'>在</b><b class='flag-5'>Linux</b>系統(tǒng)中到底處于<b class='flag-5'>一</b><b class='flag-5'>個</b>什么樣的地位