Segmentation Fault產生根本緣由

前言

本文譯自Intel® Developer Zone上文章Determining Root Cause of Segmentation Faults SIGSEGV or SIGBUS errorslinux

正文

問題:當我運行由Intel Fortran編譯器編譯的代碼時,在Linux平臺獲得’sigsegv’ 錯誤提示(或Mac OS X平臺sigbus提示)。這份代碼在<先去編譯器/平臺>上運行多年沒出 問題。這是Intel編譯器一個bug嗎?shell

運行環境:linux 或 Mac OS X編程

根本緣由:有許多可能緣由。段錯誤(segmentation fault)(Mac OS X下是bus error)是一個有多種緣由的通用錯誤。下面咱們描述潛在的緣由並給出建議以避 免段錯誤。數組

可能緣由 #1 Fortran指定棧空間耗盡:解決方法 -heap-arrays編譯選項bash

Intel Fortran編譯器使用棧空間分配許多數組數據的臨時或中間副本。app

非OpenMP和非自動並行應用:若是你的程序未使用OpenMP或 Auto-parallelization(-parallel編譯開關)且編譯器版本是Linux v9.1.037(或全部Mac OS 編譯器),那麼能夠嘗試 -heap-arrays 編譯選項。OpenMP或 Auto-parallelization用戶如用低於v9.1.0137的Linux 編譯器請閱讀可能緣由 #2關於不限制棧大小的提示。函數

1
-heap-arrays

若是這個解決了sigsegv或bus error錯誤的話,能夠不用往下讀了。你可能想讀pdf附件 學習關於臨時數組什麼時候何處被建立內容。改變一點代碼能夠避免一些臨時數組,從而減小 對臨時副本的需求(改善性能)。同時,-heap-arrays編譯器選項有一個可選參數[size] 來指定大於[size]的數組分配到堆(heap)中的閾值大小,單位爲Kbytes,其它小於等於[size]的 分配到堆棧中。例如:工具

1
-heap-arrays 10

將全部大於10Kbytes的自動和臨時數組放入堆中。性能

可能緣由 #2 堆棧空間耗盡。 解決方法:增長OpenMP應用或其它應用的堆棧大小學習

首先嚐試增長Linux和Mac OS X的shell堆棧限制。然而,該選項可能對OpenMP或自動並行 代碼的數據共享產生沒法預料的影響。所以,建議OpenMP和自動並行用戶不用 -heap-arrays,轉而嘗試關掉shell堆棧大小的限制。

Linux, bash: ulimit -s unlimited

Linux, csh/tcsh: unlimit stacksize

你能夠用如下方法檢查你的堆棧大小限制,並找到你的shell環境的stack size限制。

bash: ulimit -a

csh: limit

注意:若是你在一個批處理子系統下運行程序,可能須要將上面命令加入到我的啓動配置 文件中(~/.bashrc、~/.profile或~/.cshrc)

對於Mac OS X系統,shell堆棧大小有一個硬上限。對於大多系統,

bash: ulimit -s 65532

即設置堆棧限制爲64MB。

一個可替代方法是使用一個連接選項增長執行者默認shell堆棧大小,方法記錄在此:/en-us/articles/intel-fortran-compiler-increased-stack-usage-of-80-or-higher-compilers-causes-segmentation-fault

從新運行你的程序,若是這個方法解決此問題了,能夠不用往下讀了。若是你的應用仍然 產生sigsegv或bus error,繼續讀吧。

可能緣由 #2 主要的:因爲堆或通用內存耗盡致使堆棧耗盡

在進程內存映射中,堆和堆棧相互向對方增加。若是他們碰撞了,這也能引發一個關於堆分配或下一個堆棧分配的段錯誤。

也可能應用耗盡全部物理內存+swap緩衝區。記住,對於64位應用,虛擬內存其實是 unlimited。然而,事實上可消費的內存大小有一個上限,物理ram+swap空間(典型的是 物理內存大小的2倍)。能夠經過 free 命令得到該信息。物理內存也能夠經過cat /proc/meminfo中’MemTotal’項和’SwapTotal’項看到。系統自己也須要一些空間,因此 經驗法則是儘量保持應用的內存佔用(memory footprint)在MemTotal的80%左右而且不要超過 MemTotal+SwapTotal。

使用-g -traceback編譯連接來定位代碼終止的地方。

可能緣由 #3 因爲用戶代碼錯誤致使堆棧溢出

有許多用戶代碼錯誤可能引發堆棧溢出並致使運行時sigsegv或bus error。因爲段錯誤可 能發生在與堆棧最初溢出地方不相關的程序後面部分,從而致使錯誤很難發現。

第一步嘗試隔離代碼錯誤發生的地方。經過產生一個執行’traceback’來獲得。使用ifort 驅動和下面這些選項編譯、連接:

1
-g -traceback

當代碼出錯,一般會獲得關於錯誤發生時的調用堆棧的報告。若是沒有獲得一個堆棧 traceback,確保在編譯和連接時都用到了-g而且確保編譯時使用了-traceback。有 這樣的狀況:當程序在內核空間時段錯誤發生,所以沒有用戶堆棧用於trace back。 Intel正致力於在將來版本中改善這點。

trace back報告從下往上讀。找到最上面的子程序或函數並經過行號來隔離出哪條指令引 起錯誤的。檢查這條語句的用戶代碼錯誤。若是沒有明顯用戶錯誤,繼續往下。

可能緣由 #4 數組越界. 解決方法:-check bounds

-check bounds編譯選項提供數組訪問和字符串表達式運行時檢查保證索引在數組邊界 內。這個檢查有助於找到索引超出數組申明大小的狀況。這個選項對性能影響很大,影響 程度取決於應用中多少數組訪問被執行。同時,-check bounds數組越界檢查不對最外 維指定爲*的虛參數組或上下維都爲1的數組執行。爲開啓邊界檢查,用下面選項編譯:

1
-check bounds -g

並運行程序。檢查在運行時執行,而不是在編譯時執行。若是這樣能發現錯誤,停下來。 沒有,繼續往下讀。

可能緣由 #5 把函數看成子程序調用(calling)或把子程序看成函數調用(invoking)

當用戶作相似於這樣的事,用戶代碼錯誤:

1
2
3
4
5
6
7
8
9
10
--- main program --- ... call ThisIsIllegal( some_arguments ) ... --- end main program --- --- ThisIsIllegal --- integer function ThisIsIllegal( some_arguments ) ... --- end ThisIsIllegal ---

上面例子中,主程序以子程序調用方式調用ThisIsIllegal,然而ThisIsIllegal申明是函 數。這會引發堆棧溢出。爲測試這些狀況,嘗試使用編譯選項

1
-fp-stack-check -g -traceback

用這些選項編譯並運行。若是堆棧因爲相似上面的緣由崩潰,代碼將推出並給出一個 stack trace。

能夠用一個編譯時檢查來檢查程序接口:

1
-gen-interfaces -warn interfaces

編譯時檢查將爲你的程序產生INTERFACE塊。-warn接口隨後將使用這些編譯器產生的接口並 檢查程序調用確保參數和接口在被調用者和調用者之間匹配。注意這個檢查只對Fortran 源代碼起做用。對混編程序不檢查接口。

可能緣由 #6 傳遞非連續數組部分引發的大的臨時數組. 解決方法:使用 -check arg_temp_created 偵測並經過包含顯式接口和assumed shaped 數組代碼修復

考慮這個例子:

1
2
3
4
5
6
7
--- main program --- real(8) :: f(1800,3600,1) external sub ... call sub( f(1:900,:,:) ) ... --- end main program ---

子程序‘sub’在單獨的編譯源文件中:

1
2
3
4
5
--- external subroutine "sub" --- subroutine sub( f ) real(8) :: f(900,3600,1) ... --- end subroutine "sub" --- 

這種狀況下,‘sub’指望一個連續數組,大小爲90036001。然而,調用傳遞一個內存中 非連續的數組。這種狀況下,編譯器將在調用時產生一個臨時數組複製數組」f」非連續塊 元素使之成爲連續數組,這這是」sub「所期待的。除非指定 -heap-arrays,不然這個臨時數組分配在堆棧中。

爲了檢測代碼裏是否發生這樣的狀況,用下面選項編譯:

1
-check arg_temp_created

並運行程序。當臨時參數被建立,將輸出信息。爲了解決這個問題,建立一個顯式接口, 並在」sub「使用一個assumed shaped數組將移除臨時數組的須要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
--- main --- real(8) :: f(1800,3600,1) interface subroutine sub(f) real(8) :: f(:,:,:) end subroutine sub end interface ... call sub( f(1:900,:,:) ) ... --- end main program --- --- "sub" --- subroutine sub( f ) real(8) :: f(:,:,:) ... end subroutine sub 

記住,雖然這樣避免了臨時數組,編譯器知道」sub」內數組」f」多是非連續的。所以,一 些使用」f」的語句的優化可能關閉,進而影響性能。

不屬於以上狀況:解決方法-進一步深刻分析

99%的sigsegv或bus error錯誤緣由是上面列舉的狀況。然而,也有其餘可能狀況致使段 錯誤。

若是你的應用連接外部庫,確保庫和編譯器兼容。外部庫是否用Intel編譯器編譯的?如 果是,是否主要版本一致-即庫用Intel Fortran v9.1編譯的,但你的應用用Intel Fortran v10.x或v11.x編譯的? Intel只保證主版本內兼容(9,10,11就是主版本例子)

若是外部庫來自於軟件銷售商或工具:該銷售商是否明確Intel編譯器兼容,若是確認, 他們用哪一個版本驗證他們的庫?你應該只是用銷售商驗證過的Intel編譯器版本。

當全部都失敗了…

提交給用戶論壇吧!

相關文章
相關標籤/搜索