《linux內核設計與實現》筆記:第二章從內核出發

2020-08-12 14:11:46

1、關於內核原始碼樹

目錄 描述
arch 特定體系結構的原始碼
block 塊裝置I/O層
crypto 加密API
Documentation 內核原始碼文件
drivers 裝置驅動程式
firmware 使用某些驅動程式而需要的裝置韌體
fs VFS和各種檔案系統
include 內核標頭檔案
init 內核引導和初始化
ipc 進程間通訊程式碼
kernel 像排程程式這樣的核心子系統
lib 通用內核函數
mm 記憶體管理子系統和VM
net 網路子系統
samples 範例,示範程式碼
scripts 編譯內核所需的指令碼
security Linux安全模組
sound 語音子系統
usr 早期使用者空間程式碼(所謂的initramfs)
tools 在Linux開發中有用的工具
virt 虛擬化基礎結構

2、關於內核開發的特點

1)內核程式設計時既不用存取C庫,也不能存取標準的C檔案;
2)內核程式設計時必須使用GNU C;
3)內核程式設計時缺乏像使用者空間那樣的內核保護機制 機製;
4)內核程式設計時難以執行浮點運算;
5)內核給每個進程只有一個很小的定長堆疊;
6)由於內核支援非同步中斷、搶佔和SMP,因此必須時刻注意同步和併發。

3、關於標準C函數庫

  • 與使用者空間的應用程式不同,內核不能鏈接使用標準C函數庫。最主要的原因時速度和大小。對內核來說,完整的C庫——哪怕是它的一個子集,都太大且太低效了。
  • 不過不用擔心,大部分常用的C庫函數在內核中都已經得到了實現。
  • 內核程式碼雖然無法呼叫printf(),但它提供了printfk()函數,幾乎與printf()相同。

4、關於GNU C

  • gcc是多種GNU編譯器的集合,它包含的C編譯器既可以編譯內核,也可以編譯Linux系統上用C語言寫的其它程式碼。
  • 內核開發者使用的C語言涵蓋了ISO C99標準和GNU C擴張特性。Linux內核使用gcc的各種特性。
  • 當我定義一個內函數時,需要使用static作爲關鍵字,並且用inline限定它。比如:static inline void wolf(unsigned long tail_size)。由於使用了static作爲關鍵字進行限制,所以編譯時不會爲行內函式單獨建立一個函數體。在內核中,爲了型別安全和易讀性,優先選用行內函式,而不是複雜的宏。
  • 對於內聯彙編,我們通常使用asm()指令嵌入彙編程式碼。在偏近體系結構的底層或對執行時間要求嚴格的地方,一般使用的是彙編語言。而內核其它部分的大部分程式碼是用C語言編寫的。
  • 對於條件選擇語句,gcc內建了一條指令用於優化,在一條條件經常出現,或者該條件很少出現的時候,編譯器可以根據這條指令對條件分支選擇進行優化。內核把這條指令封裝成了宏,比如likely()unlikely()。還有,必須明確的一點是,如果你判斷正確,確實是這條佔壓倒性的地位,那麼效能會得到提升;如何搞錯,效能反而下降。

5、關於記憶體保護機制 機製

  • 如果一個使用者程式試圖進行一次非法的記憶體存取,內核就會發現這個錯誤,發送SIGSEGV信號,並結束整個進程。
  • 對於內核,自己非法訪問了記憶體,則後果很難控制(使用者空間的非法存取,可以由內核來處理,而內核的存取,由誰來管呢?)。內核發生的記憶體錯誤會導致oops,這是內核中出現的最常見的一型別錯誤。
  • 在內核中,不應該做存取非法的記憶體地址,參照空指針之類的事情,否則它可能會死掉卻根本不告訴你一聲——在內核裡,風險常常比外面更大一些。

6、關於浮點數

  • 與使用者空間進程不同,內核並不能完美地支援浮點操作,因爲它本身不能陷入。陷入指觸發中斷。
  • 在內核中使用浮點數是,除了要人工儲存和恢復浮點暫存器,還有其它一些瑣碎的事情要做。
  • 在內核中,不要輕易使用浮點數。

7、關於內核棧

  • 內核棧的準確大小隨體系結構而變。在X86上,棧的大小在編譯時設定,可以是4KB,也可以是8KB。
  • 從歷史上說,內核棧的大小是兩頁,這意味着,32位元機的內核棧是8KB,而64位元機是16KB,這是固定不變的。每個處理器都有自己的棧。

8、關於同步和併發

  • 內核很容易產生競爭條件。和單執行緒的使用者空間程式不同,內核的許多特性都要能夠併發地存取共用數據,這就要求有同步機制 機製以保證不出現競爭條件。
  • 常用的解決競爭的辦法是自旋鎖和號志。