第二章 從內核出發
1. 內核源碼樹的根目錄描述
目錄 | 描述 |
arch | 特定體繫結構的代碼 |
block | 塊設備IO層 |
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. 在修改過內核配置文件.config後,或者用已有的配置文件配置新的代碼樹的時候,使用下面的命令驗證和更新配置
make oldconfig
3. 如果配置了內核選項CONFIG_IKCONFIG和CONFIG_IKCONFIG_PROC,那麼就會把完整的壓縮過的內核配置文件存放在/proc/config.gz下。具體配置:
General setup
---> <*> Kernel .config support
---> [*] Enable access to .config through /proc/config.gz
可以使用下面的命令將config.gz取出,並用它來編譯一個新內核:
zcat /proc/config.gz >.config
make oldconfig
此外,也可以利用內核提供的腳本scripts/extract-ikconfig將內核配置文件從內核鏡像裏解析出來,用法如下:
./scripts/extract-ikconfig arch/arm/boot/zImage | tee config_file
或者
./scripts/extract-ikconfig arch/arm/boot/uImage | tee config_file
或者
./scripts/extract-ikconfig arch/arm/boot/Image| tee config_file
或者
./scripts/extract-ikconfig ./vmlinux | tee config_file
4. 在編譯完Linux內核後,會在內核源碼樹的根目錄下創建一個System.map文件。
這是一份符號對照表,用於將內核符號和它們的起始地址對應起來。在調試的時候,如果需要把內存地址翻譯成容易理解的函數名以及變量名,這很有用。
5. 內核編程的特點
-
不能訪問C庫也不能訪問標準的C頭文件。(原因是速度和大小,因爲C庫太低效)
基本的內核頭文件位於內核源代碼樹頂級目錄下的include下,如頭文件<linux/inotify.h>對應的是include/linux/inotify.h
跟體繫結構相關的頭文件集位於內核源碼樹的arch/<architecture>/include/asm目錄下,內核代碼通過以asm/爲前綴的方式包含這些頭文件,如<asm/ioctl.h>
printk的用法範例:
printk(KERN_ERR "this is an error.\n")
-
必須使用GNU C
-
缺乏像用戶空間那樣的內存保護機制
-
難以執行浮點運算
除一些極少的情況,不要在內核中使用浮點操作
-
內核給每個進程只有一個很小的定長內核堆棧
-
由於內核支持異步中斷、搶佔和SMP,故必須時刻注意同步和併發
-
要考慮可移植性的重要性
6. 內聯函數:函數會在他所調用的位置展開
-
使用內聯函數的目的是這麼做可以消除函數調用和返回帶來的開銷(寄存器存儲和恢復)。編譯器會把調用函數的代碼和函數本身放在一起進行優化,代價是代碼會變長,會佔用更多的內存空間或者佔用更多的指令緩存
-
把對時間要求比價高,而本身長度又比較短的函數定義成內斂函數
-
實際使用時,內斂函數一般定義在頭文件中,如果只在某個源文件中使用,也可以定義在該文件開始的地方
-
定義內斂函數的範例
static inline void wolf (unsigned long tail_size)
7. 內聯彙編
Linux的內核混合使用了C語言和彙編語言,在偏近體繫結構的底層或者對執行時間要求嚴格的地方,一般使用彙編語言。
8. unlikely()和likely()
在一個條件經常出現,或者很少出現時,編譯器可以根據gcc內建的一條指令對條件分支選擇進行優化。內核將這條指令封裝成了上面的兩個宏。
對於很少發生的分支,用unlikely()宏,對於經常發生的分支,用likely()宏。
9. 內核中的內存都不分頁,你用掉一個字節,物理內存就減少一個字節。在內核中不應該去訪問非法的內存地址,否則死掉。
10. 用戶空間的棧本身比較大,而且還能動態地增長。但是內核棧的大小隨體繫結構變,大小爲兩頁,即在32位機器上,內核棧爲8KB,在64位機器上爲16KB。每個處理器都有自己的棧。
11. 內核產生競爭的原因:
-
Linux是搶佔多任務操作系統
內核的進程調度程序即興對進程進行調度和重新調度,內核必須和這些任務同步。
-
Linux內核支持對稱多處理系統(SMP)
同時在兩個或兩個以上的處理器上執行的內核代碼很可能同時訪問共享的資源。
-
中斷是異步到來的,完全不顧及當前正在執行的代碼
中斷處理程序有可能訪問同一資源
-
Linux內核可以搶佔
內核中一段正在執行的代碼可能會被另一段代碼搶佔,可能導致幾段代碼同時訪問相同資源
12. 常用的解決競爭的辦法是自旋鎖和信號量
完。
本文来自博客园,作者:摩斯电码,未经同意,禁止转载