2011-03-10 34 views
1

有時,在各種Unix體系結構中,在程序運行時重新編譯程序會導致程序崩潰並顯示「總線錯誤」。任何人都可以解釋發生這種情況的條件嗎首先,如何更新磁盤上的二進制文件對內存中的代碼執行任何操作?我能想象的唯一情況是有些系統將代碼映射到內存中,當編譯器重寫磁盤映像時,這會導致mmap無效。這種方法的好處是什麼?通過更改可執行文件來使運行代碼崩潰看起來非常不理想。重新編譯二進制文件時發生總線錯誤

回答

2

在本地文件系統上,所有主流的類Unix系統都支持通過刪除文件來解決此問題。舊的vnode保持打開狀態,即使在目錄條目消失之後重新用於新映像,舊文件仍然存在,未更改,現在未命名,直到最後一次引用它(在此情況下,內核)消失爲止。

但是,如果你剛開始重寫它,那麼是的,它是mmap(3)'ed。當塊被重寫的兩種情況之一可以取決於哪個MMAP發生(3)選項的動態鏈接程序使用:

  1. 內核將無效相應的頁面,或
  2. 磁盤映像將改變,但現有的存儲器頁不會

無論哪種方式,正在運行的程序可能有麻煩。在第一種情況下,它基本上保證爆炸,而在第二種情況下,除非所有的頁面都被引用,分頁並且從不丟棄,否則它將被破壞。

有兩個mmap標誌旨在解決這個問題。一個是MAP_DENYWRITE(防止寫入),另一個是MAP_COPY,它保留了原始版本的純粹版本,並防止作者更改映射圖像。

但是出於安全原因,DENYWRITE已被禁用,並且在任何主要的類Unix系統中都沒有執行COPY。

+0

因此編譯器一般不會刪除和重寫,而是覆蓋輸出文件? – Lutorm 2011-03-10 18:56:43

0

這是一個有點複雜的情況,可能會發生在你的情況。這個錯誤的原因通常是內存對齊問題。總線錯誤在基於FreeBSD的系統中更常見。考慮一個場景,你有一個類似的結構,

struct MyStruct { char ch [29]; // 29字節 int32 i; // 4字節 }

所以這個結構的總大小應該是33個字節。現在考慮一個你有32字節緩存行的系統。此結構不能加載到單個緩存行中。現在考慮以下語句

Struct MyStruct abc; char * cptr = &abc; //結構起始處的char點指向 int32 * iptr =(cptr + 1)// iptr指向結構的第2個字節。

現在總體結構大小爲33字節,您的int指針指向第2個字節,因此您可以從int指針讀取32字節數據(因爲分配的內存總大小爲33字節)。但是當你嘗試讀取它時,如果結構被分配在緩存線的邊界,那麼操作系統不可能在一次調用中讀取32個字節。由於當前緩存行僅包含31個字節的數據,其餘1個字節位於下一個緩存行。這將導致一個無效的地址,並會給「布斯錯誤」。大多數操作系統通過在內部生成兩個內存讀取調用來處理這種情況,但一些Unix系統不處理這種情況。爲避免這種情況,建議您注意內存對齊。大多數情況下,當您嘗試將結構轉換爲另一種數據類型並嘗試讀取該結構的內存時,會發生這種情況。

該情況有點複雜,所以我不確定我是否可以用更簡單的方式解釋它。我希望你瞭解這種情況。

+0

感謝您的回答,但我不明白這與更改磁盤上的映像有何關係。代碼運行良好,除非我在可執行文件運行時重新編譯它。 – Lutorm 2011-03-10 18:55:29

相關問題