訪問 NULL 指針錯誤背后的原理

前言

說到 NULL 指針大家都是談之色變,第一印象就是 NullPointerException, Segmentation fault 之類的錯誤。NULL 指針大部分情況下會導(dǎo)致程序被終止。但是其實嚴(yán)格來說,訪問空指針會產(chǎn)生不可預(yù)料的結(jié)果。只不過大部分情況是程序被終止。為什么呢?接下來讓我們來探討訪問 NULL 指針錯誤背后的原理。

NULL 在編譯器中的實現(xiàn)

首先,我們來看看 NULL 指針到底是什么?

Null 是一個特殊指針值(或是一種對象引用)表示這個指針并不指向任何的對象。

舉一些例子,C/C++ 中的 NULL,Python 中的 None 等等。大部分 NULL 實現(xiàn)是用 0 代表 NULL,例如說 C/C++ 。實際上,NULL 的值并不重要,重要的是它代表的含義。例如說,JVM 規(guī)范并沒有規(guī)定 NULL 的值,不同虛擬機實現(xiàn)可以自己定義 NULL 的值。

總之, NULL 的值取決編譯器實現(xiàn)。

訪問 NULL 指針的過程

C 語言中,NULL 的值是 0,即 NULL == 0 是成立的。我們前面說訪問 NULL 指針的行為會產(chǎn)生不可預(yù)料的后果。但是在 Linux 系統(tǒng)中后果是確定的:訪問空指針會產(chǎn)生 Segmentation fault 的錯誤。因此這里的“不可預(yù)料”指的是在不同系統(tǒng)產(chǎn)生的后果不一樣。

讓我們假設(shè)現(xiàn)在使用的是 C 語言,運行在 Linux 系統(tǒng)上,以此來分析訪問 NULL 指針的過程。

  1. Linux 中,每個進程空間的 0x0 虛擬地址開始的線性區(qū)(memory region)都會被映射到一個用戶態(tài)沒有訪問權(quán)限的頁上。通過這樣的映射,內(nèi)核可以保證沒有別的頁會映射到這個區(qū)域。
  2. 編譯器把空指針當(dāng)做 0 對待,開心地讓你去訪問空指針。
  3. 缺頁異常處理程序被調(diào)用,因為在 0x0 的頁沒有在物理內(nèi)存里面。
  4. 缺頁異常處理程序發(fā)現(xiàn)你沒有訪問的權(quán)限。
  5. 內(nèi)核發(fā)送 SIGSEGV 信號給進程,該信號默認(rèn)是讓進程自殺。

可以看到:不需要特定的編譯器實現(xiàn)或者內(nèi)核的支持,只需要讓一個頁映射到 0x0 的虛擬地址上,就完美的實現(xiàn)了檢測空指針的錯誤。

總結(jié)

為了研究這個問題,我查了很多資料??罩羔樀膯栴}涉及 Linux 內(nèi)存管理的知識,主要參考了 Robert Love 大神對該 問題 的回答和《深入理解Linux內(nèi)核》。最大的感悟是帶著問題去看內(nèi)核的書,你會理解內(nèi)核為什么要這么做,同時可以加深理解和記憶。

總之,空指針的實現(xiàn)取決于編譯器的實現(xiàn),訪問空指針的后果取決于操作系統(tǒng)的實現(xiàn)。大部分系統(tǒng)類似于 Linux,會產(chǎn)生 Segmentation fault 的錯誤,至于內(nèi)部實現(xiàn)就要看各個系統(tǒng)的代碼了。

參考資料
What actually happens when dereferencing a NULL pointer?
《深入理解Linux內(nèi)核》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容