SIGBUS 在 x86 Linux 上並不多見,但一旦出現,其調用堆棧常常讓人摸不着頭腦,加之信號問題各平台系統間差異較大,更讓人難以理清,這里稍微總結一下 x86 Linux 上大概有哪些情形會觸發 BUS ERROR.
文件映射訪問異常##
這是 SIGBUS 在用戶態最為常見的場景,也最容易觸發,通常來說根本原因都是進程 mmap 了一個文件后,另外的進程把這個文件截斷了,導致 mmap 出來的某些內存頁超出文件的實際大小,訪問那些超出的內存頁就會觸發 SIGBUS,具體來說有以下幾種場景:
1、進程 mmap 一個文件后,其它進程 truncate 該文件到更小。
2、動態庫更新,直接 cp 覆蓋。
3、可執行文件更新,直接 cp 覆蓋。
系統讀取磁盤文件通常是按頁映射到內存,出於效率考慮常常使用 copy on write 機制,所以文件映射之后,如果對應的文件 page 不存在了(truncated),也不見得會馬上出問題,只有到訪問時才會出錯,因此有一定滯后期。
訪問不對齊的內存##
X86 平台上訪問不對齊的內存時,默認不會有問題,但用戶可以手動設置 EFLAGS 把 CPU 設置為不允許非對齊的內存訪問,此時如果出現不對齊的內存訪問,SIGBUS 就會拋出,具體例子參看【3】。

Stack fault exception
這種場景非常罕見,通常是 OS 或者內存硬件問題,從 intel 的開發者文件來看,這種異常屬於 trap,並不是我們用戶態常說的 exception,這種異常有三種起因【4】:
1、canonical address violation.
Canonical address 指的是 64 位模式下,地址的高 48 ~ 64 不是全部是 0 或 1 的地址。
如果通過棧指針 rbp 或 rsp 訪問了非 canonical address 內核就會發 stack fault trap,示例代碼如下:

需要注意的是只有棧指針操作才會 SIGBUS,非棧指針引發的這類異常,只會拋 SIGSEG。
2、棧指針操作引用了超出棧大小的地址。
這類操作我還沒法重現,只是文檔說了可以觸發。
3、棧操作引用了不存在的 stack segment。
這類操作通常是內核或編譯器的 bug。
綜上可知,stack fault 必然是與 rsp/rbp 這樣的棧指針操作相關,通常用戶態不大可能觸發,如果不是 mmap 相關的異常,大多可能是內核或硬件問題(這里有些絕對),這類異常通常會導致內核在 /var/log/messages 下輸出如下一條消息:

引用##
[1] https://stackoverflow.com/questions/2089167/debugging-sigbus-on-x86-linux
[2] http://orchistro.tistory.com/206
[3] https://sourceware.org/bugzilla/show_bug.cgi?id=11357
[4] https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf
