主頁 > 區塊鏈 > 為什么分段錯誤不可恢復?

為什么分段錯誤不可恢復?

2021-12-15 11:17:52 區塊鏈

我之前的一個問題之后,大多數評論都說“不要這樣做,你處于不確定狀態,你必須殺死一切并重新開始”。還有一個“安全”的解決方法

我不明白的是為什么分段錯誤本質上是不可恢復的。

寫入受保護記憶體的時刻被捕獲 - 否則,SIGSEGV將不會發送。

如果可以捕獲寫入受保護記憶體的時刻,我不明白為什么 - 從理論上講 - 它無法在某個低級別恢復,并且無法將 SIGSEGV 轉換為標準軟體例外。

請解釋為什么在分段錯誤后程式處于不確定狀態,很明顯,在實際更改記憶體之前拋出錯誤(我可能錯了,不明白為什么)。如果它被拋出,人們可以創建一個程式來更改受保護的記憶體,一次一個位元組,出現分段錯誤,并最終重新編程內核 - 一種不存在的安全風險,正如我們所看到的世界仍然存在。

  1. 分段錯誤究竟何時發生(= 何時SIGSEGV發送)?
  2. 為什么在那之后行程處于未定義的行為狀態?
  3. 為什么無法恢復?
  4. 為什么這個解決方案避免了不可恢復的狀態?甚至嗎?

uj5u.com熱心網友回復:

分段錯誤究竟何時發生(= SIGSEGV 何時發送)?

當您嘗試訪問您無權訪問的記憶體時,例如越界訪問陣列或取消參考無效指標。信號SIGSEGV是標準化的,但不同的作業系統可能會以不同的方式實作它。“分段錯誤”主要是在*nix 系統中使用的一個術語,Windows 稱之為“訪問沖突”。

為什么在那之后行程處于未定義行為狀態?

因為程式中的一個或幾個變數沒有按預期運行。假設您有一些應該存盤多個值的陣列,但是您沒有為所有這些值分配足夠的空間。所以只有那些你分配空間的人才能正確寫入,其余的寫在陣列邊界之外可以保存任何值。作業系統究竟如何知道那些越界值對您的應用程式運行有多重要?它對他們的目的一無所知。

此外,在允許的記憶體之外寫入通常會破壞其他不相關的變數,這顯然是危險的,并可能導致任何隨機行為。此類錯誤通常難以追蹤。例如,堆疊溢位就是這種易于覆寫相鄰變數的分段錯誤,除非錯誤被保護機制捕獲。

如果我們看看沒有任何作業系統和虛擬記憶體功能,只有原始物理記憶體的“裸機”微控制器系統的行為——它們只會按照指示默默地做——例如,覆寫不相關的變數并繼續前進。如果應用程式是關鍵任務,這反過來可能會導致災難性的行為。

為什么無法恢復?

因為作業系統不知道你的程式應該做什么。

盡管在上面的“裸機”場景中,系統可能足夠智能,可以將自己置于安全模式并繼續運行。不允許汽車和醫療技術等關鍵應用只是停止或重置,因為這本身可能是危險的。他們寧愿嘗試在功能有限的情況下“跛行回家”。

為什么這個解決方案避免了不可恢復的狀態?甚至嗎?

該解決方案只是忽略錯誤并繼續進行。它不能解決導致它的問題。這是一個非常臟的補丁,通常 setjmp/longjmp 是非常危險的函式,應該出于任何目的避免使用。

我們必須意識到分段錯誤是錯誤征兆,而不是原因

uj5u.com熱心網友回復:

請解釋為什么在分段錯誤后程式處于未確定狀態

我認為這是你的根本誤解——SEGV 不會導致不確定狀態,它是它的一個癥狀。所以問題是(通常)程式在 SIGSEGV 發生之前處于非法的、不可恢復的狀態,并且從 SIGSEGV 恢復不會改變這一點。

  • 分段錯誤究竟何時發生(= SIGSEGV 何時發送)?

SIGSEGV 發生的唯一標準方式是呼叫raise(SIGSEGV);. 如果這是 SIGSEGV 的來源,那么顯然可以通過使用 longjump 來恢復它。但這是一個在現實中從未發生過的微不足道的案例。有一些特定于平臺的做事方式可能會產生明確定義的 SEGV(例如,在 POSIX 系統上使用 mprotect),并且這些 SEGV 可能是可恢復的(但可能需要特定于平臺的恢復)。然而,與未定義行為相關的 SEGV 的危險通常意味著信號處理程式將非常仔細地檢查信號附帶的(平臺相關)資訊,以確保它是預期的。

  • 為什么在那之后行程處于未定義行為狀態?

在此之前它(通常)處于未定義的行為狀態;它只是沒有被注意到。這是 C 和 C 中未定義行為的大問題——沒有與之相關的特定行為,因此可能不會立即被注意到。

  • 為什么這個解決方案避免了不可恢復的狀態?甚至嗎?

它不會,它只是回到更早的某個點,但不會做任何事情來撤消甚至識別導致問題的未定義行為。

uj5u.com熱心網友回復:

當您的程式嘗試取消參考錯誤指標時,會發生段錯誤。(有關更多技術版本的資訊,請參閱下文,以及其他可能出現段錯誤的內容。)此時,您的程式已經被一個導致指標錯誤的錯誤絆倒了;試圖取消它通常不是真正的錯誤。

除非你故意做一些可能出現段錯誤的事情,并打算捕捉和處理這些情況(見下面的部分),否則你不會知道程式中的錯誤(或宇宙射線有點翻轉)之前搞砸了什么錯誤的訪問實際上是錯誤的。 (這通常需要用 asm 撰寫,或運行您自己 JIT 的代碼,而不是 C 或 C 。)

C 和 C 沒有定義導致分段錯誤的程式的行為,因此編譯器不會生成預期嘗試恢復的機器代碼。即使在手寫的 asm 程式中,嘗試也沒有意義,除非您預計會出現某些型別的段錯誤,沒有理智的方法可以嘗試真正恢復;至多你應該在退出前列印一條錯誤資訊。

如果您在嘗試訪問的訪問方式的任何地址處映射一些新記憶體,或者將其從只讀變為讀 寫(在 SIGSEGV 處理程式中),則可以讓錯誤指令執行,但這不太可能讓執行恢復. 大多數只讀記憶體都是只讀的,這是有原因的,讓某些內容寫入它不會有幫助。并且嘗試通過指標讀取某些內容可能需要獲取一些實際上位于其他地方的特定資料(或者根本不讀取,因為沒有什么可讀取的)。因此,將新的零頁映射到該地址將使執行繼續,但沒有正確執行。在 SIGSEGV 處理程式中修改主執行緒的指令指標也是如此,因此它會在出錯指令后恢復。然后任何加載或存盤都不會發生,使用之前在暫存器中的任何垃圾(用于加載),或者 CISCadd reg, [mem]或其他類似的其他結果

(您鏈接的捕獲 SIGSEGV 的示例取決于編譯器以明顯的方式生成機器代碼,而 setjump/longjump 取決于知道哪些代碼將發生段錯誤,并且它發生時沒有先覆寫一些有效記憶體,例如stdout資料結構在進入未映射的頁面之前, printf 取決于,就像回圈或 memcpy 可能發生的那樣。)


預期的 SIGSEGV,例如 JIT 沙箱

Java 或 Javascript 等語言(沒有未定義的行為)的 JIT 需要以明確定義的方式處理空指標取消參考,通過(Java)在客戶機中拋出 NullPointerException。

實作 Java 程式邏輯的機器代碼(由 JIT 編譯器創建,作為 JVM 的一部分)在使用前至少需要檢查一次每個參考,在任何情況下它都無法在 JIT 編譯時證明它是非空,如果它想避免出現 JITed 代碼錯誤。

但這很昂貴,因此 JIT 可能會通過允許錯誤發生在它生成的來賓 asm 中來消除一些空指標檢查,即使此類錯誤將首先捕獲到作業系統,然后才捕獲到 JVM 的 SIGSEGV 處理程式。

如果 JVM 小心地布置其生成的 asm 指令,那么任何可能的空指標 deref 都會在正確的時間發生。對其他資料的副作用,并且僅對應該發生的執行路徑產生副作用(例如,請參見@supercat 的回答),那么這是有效的。JVM 必須從信號處理程式中捕獲 SIGSEGV 和 longjmp 或任何其他資訊,以撰寫將 NullPointerException 傳遞給來賓的代碼。

但這里的關鍵部分是 JVM 假設它自己的代碼沒有錯誤,因此唯一可能“損壞”的狀態是來賓的實際狀態,而不是 JVM 的有關來賓的資料。這意味著 JVM 能夠處理來賓中發生的例外,而無需依賴可能已損壞的資料。

但是,如果來賓不期望 NullPointerException 并且因此不知道如何修復這種情況,那么本身可能無法做太多事情。除了列印錯誤訊息并退出或重新啟動之外,它可能不應該做更多的事情。(幾乎是正常的提前編譯的 C 程式的限制。)

當然,JVM 需要檢查 SIGSEGV 的錯誤地址并準確找出它在哪個訪客代碼中,才能知道將 NullPointerException 傳遞到哪里。(哪個 catch 塊,如果有的話。)如果錯誤地址根本不在 JITed 來賓代碼中,那么 JVM 就像任何其他提前編譯的 C/C 程式一樣,并且不應該做的不僅僅是列印錯誤訊息并退出。(或raise(SIGABRT)觸發核心轉儲。)

作為一個 JIT JVM,由于您自己的邏輯中的錯誤,意外的段錯誤中恢復并不容易關鍵是有一個沙盒來賓,您已經確保它不會弄亂主程式,并且主機 JVM 不會出現它的錯誤。(您不能允許來賓中的“托管”代碼具有可以指向任何地方的完全野指標,例如指向來賓代碼。但這通常很好。但是您仍然可以使用空指標,使用實際上實際執行的表示如果硬體嘗試取消參考它,則會出錯。這不會讓它寫入或讀取主機的狀態。)

有關這方面的更多資訊,請參閱如果段錯誤不可恢復為什么將其稱為故障(而不是中止)?用于段錯誤的 asm 級視圖。以及指向 JIT 技術的鏈接,這些技術讓來賓代碼出現頁面錯誤而不是執行運行時檢查:

  • Effective Null Pointer Check Elimination Utilizing Hardware Trap是一篇關于 Java 的研究論文,來自三位 IBM 科學家。

  • SableVM:6.2.4關于空指標檢查的各種架構的硬體支持

另一個技巧是將陣列的末尾放在頁面的末尾(后面是足夠大的未映射區域),因此硬體免費對每次訪問進行邊界檢查。如果您可以靜態證明索引始終為正,并且它不能大于 32 位,那么您就大功告成了。

  • Implicit Java Array Bounds Checking on 64-bit Architectures. They talk about what to do when array size isn't a multiple of the page size, and other caveats.

Background: what are segfaults

The usual reason for the OS delivering SIGSEGV is after your process triggers a page fault that the OS finds is "invalid". (I.e. it's your fault, not the OS's problem, so it can't fix it by paging in data that was swapped out to disk (hard page fault) or copy-on-write or zero a new anonymous page on first access (soft page fault), and updating the hardware page tables for that virtual page to match what your process logically has mapped.).

The page-fault handler can't repair the situation because the user-space thread normally because user-space hasn't asked the OS for any memory to be mapped to that virtual address. If it did just try to resume user-space without doing anything to the page table, the same instruction would just fault again, so instead the kernel delivers a SIGSEGV. The default action for that signal is to kill the process, but if user-space has installed a signal handler it can catch it.

Other reasons include (on Linux) trying to run a privileged instruction in user-space (e.g. an x86 #GP "General Protection Fault" hardware exception), or on x86 Linux a misaligned 16-byte SSE load or store (again a #GP exception). This can happen with manually-vectorized code using _mm_load_si128 instead of loadu, or even as a result of auto-vectorization in a program with undefined behaviour: Why does unaligned access to mmap'ed memory sometimes segfault on AMD64? (Some other OSes, e.g. MacOS / Darwin, deliver SIGBUS for misaligned SSE.)


Segfaults usually only happen after your program encountered a bug

所以你的程式狀態已經搞砸了,這就是為什么有一個 NULL 指標,你希望它是非 NULL,否則無效。(例如某些形式的 use-after free,或者指標被一些不代表有效指標的位覆寫。)

如果你很幸運,它會出現段錯誤并盡早和嘈雜地失敗,盡可能接近實際的錯誤;如果您不走運(例如損壞 malloc 簿記資訊),您實際上不會在執行錯誤代碼后很久才會出現段錯誤。

uj5u.com熱心網友回復:

關于分段錯誤,您必須了解的是,它們不是問題。他們是主近乎無限憐憫的一個例子(據我大學時的一位老教授說)。分段錯誤表明某些事情非常錯誤,您的程式認為訪問沒有記憶體的記憶體是個好主意。訪問本身不是問題;問題出現在某個不確定的時間之前,當出現問題時,最終導致您的程式認為此訪問是一個好主意。在這一點上訪問不存在的記憶只是一個癥狀,但是(這是主的憐憫進入它的地方)它很容易被發現癥狀。情況可能更糟;它可能是在有記憶體的地方訪問記憶體,只是,錯誤的記憶體。作業系統無法避免這種情況。

作業系統無法弄清楚是什么導致您的程式相信如此荒謬的事情,它唯一能做的就是關閉事物,然后再以作業系統無法輕易檢測到的方式做其他瘋狂的事情。通常,大多數作業系統還提供核心轉儲(程式記憶體的保存副本),理論上可以用來確定程式認為它在做什么。這對于任何非平凡的程式來說都不是很簡單,但這就是作業系統這樣做的原因,以防萬一。

uj5u.com熱心網友回復:

雖然您的問題專門詢問分段錯誤,但真正的問題是:

如果軟體或硬體組件被命令做一些荒謬甚至不可能的事情,它應該怎么做?什么都不做?猜猜實際上需要做什么并做到這一點?或者使用某種機制(例如“拋出例外”)來停止發出無意義命令的更高級別的計算?

許多工程師多年來積累的大量經驗一致認為,最好的答案是停止整體計算,并生成可以幫助某人找出問題所在的診斷資訊

除了非法訪問受保護或不存在的記憶體之外,“無意義命令”的其他示例包括告訴 CPU 將整數除以零或執行未解碼為任何有效指令的垃圾位元組。如果使用具有運行時型別檢查的編程語言,則嘗試呼叫未為所涉及的資料型別定義的任何操作是另一個示例。

但是為什么強制一個試圖除以零的程式崩潰會更好呢?沒有人希望他們的程式崩潰。難道我們不能將除以零定義為等于某個數字,例如零或 73?難道我們不能創建可以跳過無效指令而不會出錯的 CPU 嗎?也許我們的 CPU 還可以為從受保護或未映射的記憶體地址進行的任何讀取回傳一些特殊值,例如 -1。他們可以忽略對受保護地址的寫入。沒有更多的段錯誤!呼!

當然,這些事情都是可以做到的,但實際上并沒有什么收獲。重點是:雖然沒有人希望他們的程式崩潰,但不崩潰并不意味著成功。人們撰寫和運行計算機程式做的事情,而不是僅僅“不出事”。如果程式有足夠的錯誤來讀取或寫入隨機記憶體地址或嘗試除以零,那么它執行您真正想要的操作的可能性非常低,即使允許它繼續運行也是如此。另一方面,如果程式在嘗試瘋狂的事情時沒有停止,它最終可能會做一些您想要的事情,例如損壞或破壞您的資料。

從歷史上看,一些編程語言被設計為總是“只做某事”以回應無意義的命令,而不是引發致命錯誤。這樣做的目的是為了對新手程式員更加友好,但結果總是很糟糕。對于您的建議,即作業系統不應因段錯誤而使程式崩潰,這同樣適用。

uj5u.com熱心網友回復:

在機器代碼級別,許多平臺允許在某些情況下“預期”分段錯誤的程式調整記憶體配置并恢復執行。這對于實作堆疊監控之類的東西可能很有用。如果需要確定應用程式曾經使用過的最大堆疊量,可以將堆疊段設定為僅允許訪問少量堆疊,然后通過調整堆疊段的邊界和恢復代碼執行。

但是,在 C 語言級別,支持此類語意將極大地阻礙優化。如果有人要寫這樣的東西:

void test(float *p, int *q)
{
  float temp = *p;
  if (*q  = 1)
    function2(temp);
}

編譯器可能會認為讀取*p和 讀取-修改-寫入 序列*q相對于彼此是未排序的,并生成僅*p在初始值*q不是 -1 的情況下讀取的代碼如果p有效,這不會對程式行為產生任何影響,但如果p無效*p*q則即使觸發故障的訪問是在增量之前執行的,此更改也可能導致從訪問到增量后發生的段錯誤

對于有效且有意義地支持可恢復段錯誤的語言,它必須比 C 標準更詳細地記錄允許和不允許優化的范圍,而且我認為沒有理由期待 C 的未來版本包含此類細節的標準。

uj5u.com熱心網友回復:

它是可以恢復的,但這通常是一個壞主意。例如,Microsoft C 編譯器可以選擇將段錯誤轉換為例外。

您可以查看 Microsoft SEH 檔案,但即使他們也不建議使用它。

uj5u.com熱心網友回復:

老實說,如果我可以告訴計算機忽略分段錯誤。我不會選擇這個選項。

通常會發生分段錯誤,因為您正在取消參考空指標或解除分配的指標。當取消參考 null 時,行為是完全未定義的。當參考一個解除分配的指標時,你拉取的資料可能是舊值、隨機垃圾,或者在最壞的情況下來自另一個程式的值。在任何一種情況下,我都希望程式出現段錯誤,而不是繼續并報告垃圾計算。

uj5u.com熱心網友回復:

多年來,分段錯誤一直困擾著我。我主要在嵌入式平臺上作業,因為我們在裸機上運行,??所以沒有可以記錄核心轉儲的檔案系統。系統剛剛鎖定并死亡,也許是串行埠輸出了幾個分離字符。那些年最有啟發性的時刻之一是我意識到分段錯誤(以及類似的致命錯誤)是一件好事體驗一個不好,但將它們放在適當的位置,就像難以避免的故障點一樣。

像這樣的故障不是輕易產生的。硬體已經盡其所能恢復,故障是硬體警告您繼續操作是危險的方式。事實上,讓整個程序/系統崩潰實際上比繼續更安全即使在具有受保護/虛擬記憶體的系統中,在此類故障后繼續執行也會破壞系統其余部分的穩定性。

如果可以捕捉到寫入受保護記憶體的時刻

除了寫入受保護的記憶體之外,還有更多方法可以進入段錯誤。您也可以通過例如從具有無效值的指標讀取來到達那里。這要么是由于先前的記憶體損壞(損壞已經造成,因此恢復為時已晚)或由于缺少錯誤檢查代碼(您的靜態分析器和/或測驗應該已捕獲)引起的。

為什么無法恢復?

您不一定知道問題的原因或問題的嚴重程度,因此您不知道如何從中恢復。如果你的記憶已經損壞,你就不能相信任何東西可以恢復的情況是您可以提前檢測到問題的情況,因此使用例外不是解決問題的正確方法。

請注意,其中一些型別的問題可以在其他語言(如 C#)恢復。這些語言通常有一個額外的運行時層,它會提前檢查指標地址并在硬體產生故障之前拋出例外。但是,對于像 C 這樣的低級語言,您沒有任何這些。

為什么這個解決方案避免了不可恢復的狀態?甚至嗎?

該技術“有效”,但僅適用于人為的、簡單的用例。繼續執行與恢復不同。有問題的系統仍處于故障狀態,記憶體損壞未知,您只是選擇繼續前進,而不是聽從硬體的建議來認真對待問題。不知道你的程式在那個時候會做什么。在潛在記憶體損壞后繼續執行的程式將是攻擊者的早期圣誕禮物。

即使沒有任何記憶體損壞,該解決方案在許多不同的常見用例中也會中斷。當已經在一個代碼塊中時,您不能輸入第二個受保護的代碼塊(例如在輔助函式中)。在受保護的代碼塊之外發生的任何段錯誤都將導致跳轉到代碼中不可預測的點。這意味著每一行代碼都需要在一個保護塊中,你的代碼會令人討厭。您不能呼叫外部庫代碼,因為該代碼不使用此技術并且不會設定setjmp錨點。您的“處理程式”塊無法呼叫庫函式或執行任何涉及指標的操作,否則您可能會面臨需要無限嵌套塊的風險。某些東西(如自動變數)在longjmp.

關于關鍵任務系統(或任何系統),這里缺少一件事:在生產中的大型系統中,人們無法知道段錯誤在哪里,甚至不知道段錯誤是否存在,因此修復錯誤而不是癥狀的建議不成立。

我不同意這個想法。我見過的大多數分段錯誤都是由(直接或間接)取消參考指標引起的,而沒有先驗證它們。在使用之前檢查指標會告訴您段錯誤在哪里。將復雜的陳述句拆分my_array[ptr1->offsets[ptr2->index]]為多個陳述句,以便您也可以檢查中間指標。像 Coverity 這樣的靜態分析器可以很好地找到使用指標而不進行驗證的代碼路徑。這不會保護您免受由徹底記憶體損壞引起的段錯誤,但無論如何都無法從這種情況中恢復。

在短期實踐中,我認為我的錯誤只是訪問 null 而已。

好訊息!這整個討論沒有實際意義。指標和陣列索引可以(并且應該!)在使用之前進行驗證,提前檢查比等待問題發生并嘗試恢復要少得多。

uj5u.com熱心網友回復:

這可能不是一個完整的答案,它絕不是完整或準確的,但它不適合評論

因此,SIGSEGV當您嘗試以不應該的方式訪問記憶體時(例如在只讀時寫入或從未映射的地址范圍讀取),可能會發生這種情況。如果您對環境有足夠的了解,那么單獨的此類錯誤可能是可以恢復的。

但是,您首先要如何確定無效訪問發生的原因。

在對另一個答案的評論中,您說:

短期實踐,我認為我的錯誤只是訪問null而已。

沒有應用程式是無錯誤的,所以你為什么假設如果空指標訪問可能發生,你的應用程式不會例如也有這樣的情況,即釋放后使用或對“有效”記憶體位置的越界訪問發生,那不會立即導致錯誤或SIGSEGV.

釋放后使用或越界訪問也可能將指標修改為指向無效位置或成為 nullptr,但它也可能同時更改了記憶體中的其他位置。如果您現在僅假設指標未初始化并且您的錯誤處理僅考慮這一點,那么您將繼續處理處于不符合您的期望或編譯器之一在生成代碼時的狀態的應用程式。

在這種情況下,應用程式將 - 在最好的情況下 - 在“恢復”后不久崩潰,在最壞的情況下,一些變數具有錯誤值,但它會繼續運行這些變數。對于關鍵應用程式而言,這種疏忽可能比重新啟動它更有害。

但是,如果您知道某個操作在某些情況下可能會導致SIGSEGV您可以處理該錯誤,例如您知道記憶體地址有效,但該記憶體映射到的設備可能不完全可靠并且可能導致SIGSEGV因此,從 a 中恢復SIGSEGV可能是一種有效的方法。

uj5u.com熱心網友回復:

取決于你所說的恢復是什么意思。如果作業系統向您發送 SEGV 信號,唯一合理的恢復是清理您的程式并從一開始就旋轉另一個程式,希望不會遇到同樣的陷阱。

在作業系統結束混亂之前,您無法知道您的記憶體損壞了多少。如果您嘗試從下一條指令或某個任意恢復點繼續,您的程式可能會出現進一步的錯誤行為。

似乎許多贊成的回答都忘記了,有些應用程式可以在生產中發生段錯誤而不會出現編程錯誤。以及預期高可用性、數十年使用壽命和零維護的地方。在這些環境中,通常會在程式因任何原因崩潰時重新啟動,包括段錯誤。此外,看門狗功能用于確保程式不會陷入計劃外的無限回圈。

想想您所依賴的所有沒有重置按鈕的嵌入式設備。他們依賴不完美的硬體,因為沒有完美的硬體。軟體必須處理硬體缺陷。換句話說,軟體必須能夠抵御硬體不當行為。

嵌入式并不是唯一重要的領域。想想只處理 StackOverflow 的服務器數量。如果您查看地面上的任何一項操作,電離輻射導致單個事件擾亂的可能性很小,但如果您查看大量 24/7 全天候運行的計算機,這種可能性就變得非常重要。ECC 記憶體有助于解決此問題,但并非所有內容都可以得到保護。

uj5u.com熱心網友回復:

你的程式是一個欠終止狀態,因為 C 不能定義狀態。導致這些錯誤的錯誤是未定義的行為。這是最惡劣的不良行為。

從這些事情中恢復的關鍵問題是,作為未定義的行為,編譯器沒有義務以任何方式支持它們。特別是,它可能已經進行了優化,如果僅發生定義的行為,則可證明具有相同的效果。編譯器完全有權對行重新排序、跳過行以及執行各種花哨的技巧來使您的代碼運行得更快。它所要做的就是根據C 虛擬機模型證明效果是一樣的。

當發生未定義的行為時,所有這些都會消失。您可能會遇到困難的情況,即編譯器對操作進行了重新排序,但現在無法通過執行程式一段時間使您達到可以達到的狀態。請記住,賦值會洗掉舊值。如果分配在出現段錯誤的行之前上移,則無法恢復舊值以“展開”優化。

只要沒有發生未定義的行為,這個重新排序的代碼的行為確實與原始代碼相同一旦發生未定義的行為,就會暴露出重新排序發生并可能改變結果的事實。

這里的權衡是速度。因為編譯器不是在蛋殼上行走,害怕某些未指定的作業系統行為,所以它可以更好地優化您的代碼。

現在,因為未定義的行為始終未定義的行為,無論您多么希望它不是,都無法使用規范的 C 方法來處理這種情況。C 語言永遠無法引入解決此問題的方法,至少無法使其定義行為并為此付出代價。在給定的平臺和編譯器上,您可能能夠識別出這種未定義的行為實際上是由您的編譯器定義的,通常以擴展的形式。事實上,我之前鏈接的答案顯示了一種將信號轉換為例外的方法,它確實適用于至少一個平臺/編譯器對。

但它總是必須像這樣處于邊緣。C 開發人員更看重優化代碼的速度,而不是定義這種未定義的行為。

uj5u.com熱心網友回復:

當您使用術語 SIGSEGV 時,我相信您使用的是帶有作業系統的系統,并且問題發生在您的用戶應用程式中。

當應用程式獲得 SIGSEGV 時,它是記憶體訪問之前出現問題的征兆。有時它可以準確地指出事情出錯的地方,通常不會。所以出了點問題,一段時間后這個錯誤是 SIGSEGV 的原因。如果錯誤發生在“作業系統中”,我的反應是關閉系統。有一個非常特殊的例外——當作業系統具有特定的功能來檢查安裝(或可能洗掉)的存盤卡或 IO 卡時。

在用戶領域,我可能會將我的應用程式分成幾個行程。一個或多個行程將完成實際作業。另一個行程將監視作業行程并可以發現其中一個何時失敗。然后監視器行程可以發現作業行程中的 SIGSEGV,它可以重新啟動作業行程或進行故障轉移或任何在特定情況下認為合適的事情。這不會恢復實際的記憶體訪問,但可能會恢復應用程式功能。

您可以查看 Erlang 的“盡早失敗”的哲學和 OTP 庫,以獲取有關這種做事方式的更多靈感。雖然它不處理 SIGSEGV,但處理其他幾種型別的問題。

uj5u.com熱心網友回復:

您的程式無法從分段錯誤中恢復,因為它不知道任何東西處于什么狀態

考慮這個類比。

你在緬因州有一棟漂亮的房子,有一個漂亮的前花園和一條穿過它的踏腳石小路。無論出于何種原因,您都選擇用絲帶將每塊石頭連接到下一塊(也就是說,您已將它們制成單鏈表)。
一天早上,從房子里出來,你踏上第一塊石頭,然后沿著絲帶走到第二塊,然后再到第三塊,但是當你踏上第四塊石頭時,你突然發現自己在阿爾伯克基。

現在告訴我們, -如何做歇著

你的程式也有同樣的窘境。
出事壯觀錯了,但你的程式有不知道這是什么,或者什么原因造成的或如何任何事情有用。
因此:它會崩潰并燃燒。

uj5u.com熱心網友回復:

這是絕對可能的,但這會以不太穩定的方式復制現有功能。

當程式訪問尚未由物理記憶體支持的地址時,內核將已經收到頁面錯誤例外,然后將根據現有映射分配并可能初始化頁面,然后重試違規指令。

一個假設的 SEGV 處理程式會做完全相同的事情:決定應該在這個地址映射什么,創建映射并重試指令——但不同的是,如果處理程式會導致另一個 SEGV,我們可以在這里進入無限回圈,并且檢測會很困難,因為該決定需要查看代碼 - 所以我們會在這里創建一個停止問題。

內核已經懶惰地分配記憶體頁,允許映射檔案內容并支持具有寫時復制語意的共享映射,因此從這種機制中獲得的好處不多。

uj5u.com熱心網友回復:

到目前為止,答案和評論都是通過更高級別的編程模型的鏡頭做出回應的,這從根本上限制了程式員的創造力和潛力,以方便他們。所述模型定義了它們自己的語意,并且不會出于自身原因處理分段錯誤,無論是簡單性、效率還是其他任何原因。從這個角度來看,段錯誤是一種不尋常的情況,表明程式員錯誤,無論是用戶空間程式員還是語言實作的程式員。然而,問題不在于它是否是一個好主意,也不在于詢問您對此事的任何想法。

實際上,您說的是正確的:分段錯誤是可以恢復的。您可以像任何常規信號一樣,使用sigaction. 而且,是的,您的程式肯定可以以處理分段錯誤是正常功能的方式制作。

一個障礙是分段錯誤是錯誤,而不是例外,這與處理錯誤后控制流回傳到哪里不同。具體來說,故障處理程式回傳到相同的故障指令,該指令將無限期地繼續故障。不過,這不是一個真正的問題,因為它可以手動跳過,您可能會回傳到指定的位置,您可能會嘗試修補故障指令使其正確,或者如果您信任故障代碼,您可以將所述記憶體映射到存在. 憑借對機器的正確了解,沒有什么可以阻止您,即使是那些揮舞著規格的騎士。

轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/381014.html

標籤:C C 例外 分段故障

上一篇:什么是例外處理程式

下一篇:您可以更改Java例外型別的錯誤訊息嗎?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • JAVA使用 web3j 進行token轉賬

    最近新學習了下區塊鏈這方面的知識,所學不多,給大家分享下。 # 1. 關于web3j web3j是一個高度模塊化,反應性,型別安全的Java和Android庫,用于與智能合約配合并與以太坊網路上的客戶端(節點)集成。 # 2. 準備作業 jdk版本1.8 引入maven <dependency> < ......

    uj5u.com 2020-09-10 03:03:06 more
  • 以太坊智能合約開發框架Truffle

    前言 部署智能合約有多種方式,命令列的瀏覽器的渠道都有,但往往跟我們程式員的風格不太相符,因為我們習慣了在IDE里寫了代碼然后打包運行看效果。 雖然現在IDE中已經存在了Solidity插件,可以撰寫智能合約,但是部署智能合約卻要另走他路,沒辦法進行一個快捷的部署與測驗。 如果團隊管理的區塊節點多、 ......

    uj5u.com 2020-09-10 03:03:12 more
  • 谷歌二次驗證碼成為區塊鏈專用安全碼,你怎么看?

    前言 谷歌身份驗證器,前些年大家都比較陌生,但隨著國內互聯網安全的加強,它越來越多地出現在大家的視野中。 比較廣泛接觸的人群是國際3A游戲愛好者,游戲盜號現象嚴重+國外賬號安全應用廣泛,這類游戲一般都會要求用戶系結名為“兩步驗證”、“雙重驗證”等,平臺一般都推薦用谷歌身份驗證器。 后來區塊鏈業務風靡 ......

    uj5u.com 2020-09-10 03:03:17 more
  • 密碼學DAY1

    目錄 ##1.1 密碼學基本概念 密碼在我們的生活中有著重要的作用,那么密碼究竟來自何方,為何會產生呢? 密碼學是網路安全、資訊安全、區塊鏈等產品的基礎,常見的非對稱加密、對稱加密、散列函式等,都屬于密碼學范疇。 密碼學有數千年的歷史,從最開始的替換法到如今的非對稱加密演算法,經歷了古典密碼學,近代密 ......

    uj5u.com 2020-09-10 03:03:50 more
  • 密碼學DAY1_02

    目錄 ##1.1 ASCII編碼 ASCII(American Standard Code for Information Interchange,美國資訊交換標準代碼)是基于拉丁字母的一套電腦編碼系統,主要用于顯示現代英語和其他西歐語言。它是現今最通用的單位元組編碼系統,并等同于國際標準ISO/IE ......

    uj5u.com 2020-09-10 03:04:50 more
  • 密碼學DAY2

    ##1.1 加密模式 加密模式:https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html ECB ECB : Electronic codebook, 電子密碼本. 需要加密的訊息按照塊密碼的塊大小被分為數個塊,并對每個塊進 ......

    uj5u.com 2020-09-10 03:05:42 more
  • NTP時鐘服務器的特點(京準電子)

    NTP時鐘服務器的特點(京準電子) NTP時鐘服務器的特點(京準電子) 京準電子官V——ahjzsz 首先對時間同步進行了背景介紹,然后討論了不同的時間同步網路技術,最后指出了建立全球或區域時間同步網存在的問題。 一、概 述 在通信領域,“同步”概念是指頻率的同步,即網路各個節點的時鐘頻率和相位同步 ......

    uj5u.com 2020-09-10 03:05:47 more
  • 標準化考場時鐘同步系統推進智能化校園建設

    標準化考場時鐘同步系統推進智能化校園建設 標準化考場時鐘同步系統推進智能化校園建設 安徽京準電子科技官微——ahjzsz 一、背景概述隨著教育事業的快速發展,學校建設如雨后春筍,隨之而來的學校教育、管理、安全方面的問題成了學校管理人員面臨的最大的挑戰,這些問題同時也是學生家長所擔心的。為了讓學生有更 ......

    uj5u.com 2020-09-10 03:05:51 more
  • 位元幣入門

    引言 位元幣基本結構 位元幣基礎知識 1)哈希演算法 2)非對稱加密技術 3)數字簽名 4)MerkleTree 5)哪有位元幣,有的是UTXO 6)位元幣挖礦與共識 7)區塊驗證(共識) 總結 引言 上一篇我們已經知道了什么是區塊鏈,此篇說一下區塊鏈的第一個應用——位元幣。其實先有位元幣,后有的區塊 ......

    uj5u.com 2020-09-10 03:06:15 more
  • 北斗對時服務器(北斗對時設備)電力系統應用

    北斗對時服務器(北斗對時設備)電力系統應用 北斗對時服務器(北斗對時設備)電力系統應用 京準電子科技官微(ahjzsz) 中國北斗衛星導航系統(英文名稱:BeiDou Navigation Satellite System,簡稱BDS),因為是目前世界范圍內唯一可以大面積提供免費定位服務的系統,所以 ......

    uj5u.com 2020-09-10 03:06:20 more
最新发布
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:46:47 more
  • Hyperledger Fabric 使用 CouchDB 和復雜智能合約開發

    在上個實驗中,我們已經實作了簡單智能合約實作及客戶端開發,但該實驗中智能合約只有基礎的增刪改查功能,且其中的資料管理功能與傳統 MySQL 比相差甚遠。本文將在前面實驗的基礎上,將 Hyperledger Fabric 的默認資料庫支持 LevelDB 改為 CouchDB 模式,以實作更復雜的資料... ......

    uj5u.com 2023-04-16 07:28:31 more
  • .NET Core 波場鏈離線簽名、廣播交易(發送 TRX和USDT)筆記

    Get Started NuGet You can run the following command to install the Tron.Wallet.Net in your project. PM> Install-Package Tron.Wallet.Net 配置 public reco ......

    uj5u.com 2023-04-14 08:08:00 more
  • DKP 黑客分析——不正確的代幣對比率計算

    概述: 2023 年 2 月 8 日,針對 DKP 協議的閃電貸攻擊導致該協議的用戶損失了 8 萬美元,因為 execute() 函式取決于 USDT-DKP 對中兩種代幣的余額比率。 智能合約黑客概述: 攻擊者的交易:0x0c850f,0x2d31 攻擊者地址:0xF38 利用合同:0xf34ad ......

    uj5u.com 2023-04-07 07:46:09 more
  • Defi開發簡介

    Defi開發簡介 介紹 Defi是去中心化金融的縮寫, 是一項旨在利用區塊鏈技術和智能合約創建更加開放,可訪問和透明的金融體系的運動. 這與傳統金融形成鮮明對比,傳統金融通常由少數大型銀行和金融機構控制 在Defi的世界里,用戶可以直接從他們的電腦或移動設備上訪問廣泛的金融服務,而不需要像銀行或者信 ......

    uj5u.com 2023-04-05 08:01:34 more
  • solidity簡單的ERC20代幣實作

    // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; import "hardhat/console.sol"; //ERC20 同質化代幣,每個代幣的本質或性質都是相同 //ETH 是原生代幣,它不是ERC20代幣, ......

    uj5u.com 2023-03-21 07:56:29 more
  • solidity 參考型別修飾符memory、calldata與storage 常量修飾符C

    在solidity語言中 參考型別修飾符(參考型別為存盤空間不固定的數值型別) memory、calldata與storage,它們只能修飾參考型別變數,比如字串、陣列、位元組等... memory 適用于方法傳參、返參或在方法體內使用,使用完就會清除掉,釋放記憶體 calldata 僅適用于方法傳參 ......

    uj5u.com 2023-03-08 07:57:54 more
  • solidity注解標簽

    在solidity語言中 注釋符為// 注解符為/* 內容*/ 或者 是 ///內容 注解中含有這幾個標簽給予我們使用 @title 一個應該描述合約/介面的標題 contract, library, interface @author 作者的名字 contract, library, interf ......

    uj5u.com 2023-03-08 07:57:49 more
  • 評價指標:相似度、GAS消耗

    【代碼注釋自動生成方法綜述】 這些評測指標主要來自機器翻譯和文本總結等研究領域,可以評估候選文本(即基于代碼注釋自動方法而生成)和參考文本(即基于手工方式而生成)的相似度. BLEU指標^[^?88^^?^]^:其全稱是bilingual evaluation understudy.該指標是最早用于 ......

    uj5u.com 2023-02-23 07:27:39 more
  • 基于NOSTR協議的“公有制”版本的Twitter,去中心化社交軟體Damus

    最近,一個幽靈,Web3的幽靈,在網路游蕩,它叫Damus,這玩意詮釋了什么叫做病毒式營銷,滑稽的是,一個Web3產品卻在Web2的產品鏈上瘋狂傳銷,各方大佬紛紛為其背書,到底發生了什么?Damus的葫蘆里,賣的是什么藥? 注冊和簡單實用 很少有什么產品在用戶注冊環節會有什么噱頭,但Damus確實出 ......

    uj5u.com 2023-02-05 06:48:39 more