斷言是非常常見的,其語義就是判斷某個條件,如果不為真,就執行一段非常規的動作,一般為程式立馬結束執行。
斷言分為動態和靜態的。動態斷言也叫執行時斷言,即在程式執行的使用,由比較指令來判斷條件;而靜態斷言是在**的編譯過程中,通過編譯器對常量表示式的計算來判斷條件成立與否的,即一般不會生成額外的**。
一般我們在沒有編譯器特性支撐的情況下,可以使用c語言的語法語義的正確性來實現,最常規的是根據常量表示式的真假來定義乙個負數大小的陣列。比如:
#define static_assert(cond) \
do while(0)
cond
必須要為常量表示式,即非執行時就可以確定結果的表示式。
當斷言失敗時將出現下面的資訊:
../../include/utils/compiler.h:247:20: error: size of unnamed array is negative
在有編譯器特性支援的的情況下,使用__attribuite__((error(message)))
來描述乙個不存在的函式,當編譯時發現呼叫了此函式就列印message
並終止編譯,一般來說,函式的存在與否必須要等到鏈結時才可知的,但gnu的編譯器支援這個特性。比如:
同樣#define static_assert(cond) \
do } while(0)
cond
必須要為常量表示式,即非執行時就可以確定結果的表示式。
當斷言失敗時將出現下面的資訊:
../../include/utils/compiler.h:247:20: error: call to 『__static_assert_func』 declared with attribute error: static assert failed
核心使用build_bug_on_***()
來作為字首定義一系列的靜態斷言,比如在定義乙個可以配置的雜湊表時,為了加快雜湊桶的定位,一般都是用2^n
大小的桶,並用雜湊值與其掩碼來計算桶下標,即:hash_value & (bucket_size -1)
。為了防止非2^n
大小的雜湊表出現,定義build_bug_on_not_power_of_2()
來快速判斷大小。
這些巨集都最終都是由下列巨集實現 :
上面的定義做了一些改動,你可以直接用於自己的應用層程式。#define gcc_version (__gnuc__ * 10000 \
+ __gnuc_minor__ * 100 \
+ __gnuc_patchlevel__)
#if gcc_version >= 40800
# define __compiletime_error(message) __attribute__((error(message)))
#endif
#ifndef __compiletime_error
# define __compiletime_error(message)
#endif
# define __compiletime_error_fallback(condition) \
do while (0)
#if defined(__optimize__)
# define __compiletime_assert(condition, msg, prefix, suffix) \
do while (0)
#else
# define __compiletime_assert(condition, msg, prefix, suffix) \
__compiletime_error_fallback(!(condition))
#endif
#define _compiletime_assert(condition, msg, prefix, suffix) \
__compiletime_assert(condition, msg, prefix, suffix)
#define compiletime_assert(condition, msg) \
_compiletime_assert(condition, msg, __compiletime_assert_, __line__)
#define build_bug_on(condition) \
build_bug_on_msg(condition, "build_bug_on failed: " #condition)
#define build_bug_on_not_power_of_2(n) \
build_bug_on((n) == 0 || (((n) & ((n) - 1)) != 0))
__optimize__
表示在使用-o
並且n
大於0時,編譯器會定義此巨集,並且要gcc編譯器特性來斷言時,必須要使用較新的版本,我在macos
上測試時,gcc就不支援__attribuite__((error(message)))
這個擴充套件。
剛才我們提示過,動態的斷言將使用指令,完成斷言,那麼聰明的核心在條件不成立時執行乙個cpu不認識的指令來觸發乙個指令異常,並被異常子系統捕獲,最終會執行宕機處理。
#define bug() __asm__ __volatile__("ud2\n")
我們在應用層程式設計時,直接使用assert.h
標頭檔案中的assert(cond)
就可以了。
核心的斷言有許多的**技巧,你在掌握了基本原理後,可以在所有的bug.h
中學習動態斷言,以及在compiler.h
和build_bug.h
中學習靜態斷言。
《Windows核心程式設計》 防禦性程式設計 斷言
防禦性程式設計是提高軟體質量技術的有益輔助手段,它的主要思想是 子程式應該不因傳入錯誤資料而被破壞,哪怕是由其他子程式產生的錯誤資料。防禦性程式設計關鍵在於嚴格的輸入檢查 預期的錯誤處理方法。下面介紹使用斷言來進行防禦性程式設計。斷言通常是乙個例程 routine 或乙個巨集 macros 斷言通常...
《Windows核心程式設計》 防禦性程式設計 斷言
防禦性程式設計是提高軟體質量技術的有益輔助手段,它的主要思想是 子程式應該不因傳入錯誤資料而被破壞,哪怕是由其他子程式產生的錯誤資料。防禦性程式設計關鍵在於嚴格的輸入檢查 預期的錯誤處理方法。下面介紹使用斷言來進行防禦性程式設計。斷言通常是乙個例程 routine 或乙個巨集 macros 斷言通常...
Linux 核心程式設計總結
linux 核心程式設計總結 從事了幾年的核心程式設計,對核心程式設計有一定的經驗,現總結 吐槽下,作為標記。任何程序都有有程序的入口點,使用者態的程序,其入口點是,main函式。那麼核心的入口點是什麼?個人理解整個os,執行起來就是乙個程序,核心的入口點是init程序,在這個程序中負責 1 子程序...