Linux核心設計與實現 總結筆記(第二章)
一、Linux核心中的一些基本概念
核心空間:核心可獨立於普通應用程式,它一般處於系統態,擁有受保護的記憶體空間和訪問硬體裝置的所有許可權。這種系統態和被保護起來的記憶體空間,稱為核心空間。
程序上下文:當應用程式執行一條系統呼叫,通過系統呼叫執行在核心空間,而核心被稱為執行在程序上下文中。
當你開發核心程式碼時,有一個重要的論壇是linux kernel mailing list(常縮寫為lkml),你可以在http://vger.kernel.org上訂閱郵件。
二、核心的一些常用的準備和操作
2.1 準備一個核心程式碼
- 下載地址:http://www.kernel.org
- 使用git獲取最新的linux版本樹:$git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
- 更新linus的最新分支:$git pull
2.2 安裝核心原始碼
- 解壓壓縮包
tar xvjf linux-x.y.z.tar.bz2
tar xvzf linux-x.y.z.tar.gz
安裝原始碼的位置注意:核心原始碼通常安裝在/usr/src/linux目錄下。不能把這個原始碼樹用作開發,因為編譯工具C庫就是連結到這裡的。
並且不能用root對核心進行修改,哪怕是root安裝核心,它都應該原封不動。
- 使用補丁的方法
patch -p1 < ../patch-x.y.z
- 核心配置
make config 老舊的方法,低效 make menuconfig 最常用的方法 make gconfig 基於個gtk+的圖形工具
make defconfig 建立一個預設的配置
make oldconfig .config不存在,執行make config/menuconfig,設定是子目錄中的Kconfig的設定
.config存在,執行make config/menuconfig時的預設設定即當前的.config設定
備份當前.config檔案為.config.old,如果make config/menuconfig設定不當可用於恢復
- 當核心執行時則可以將/proc/config.gz檔案複製出來,並且解壓得到此核心的.config配置檔案
zcat /proc/config.gz > .config #解壓命令
make oldconfig
- 核心編譯
make -jn
make -j32 > /dev/null
- 安裝新核心
一定要保證隨時有一個或兩個可以啟動的核心,以防新編譯的核心出現問題。
例如,在使用grub的x86系統上,可能需要把arch/i386/boot/bzImage拷貝到/boot目錄下,像vmlinuz-version這樣命名它。並且編輯/etc/grub/grub.conf檔案,為新核心建立一個新的啟動項。使用LILO啟動的系統應當編輯etc/lilo.conf,然後執行lilo
make moudles_install
三、核心編譯之外的資訊
- 核心和應用程式的差別包括以下幾種:
- 核心程式設計時既不能訪問C庫也不能訪問標準的C標頭檔案
- 核心程式設計時必須使用GNU C
- 核心程式設計時缺乏像使用者空間那樣的呢村保護機制
- 核心程式設計時難以執行浮點運算
- 核心給每個程序只有一個很小的定長堆疊
- 由於核心支援非同步中斷、搶佔和SMP,因此必須時刻注意同步和併發
- 要考慮可移植性的重要性
3.1 沒有libc庫抑或無標準標頭檔案
核心不能連線和使用標準C函式庫,主要原因是使用庫非常低效。
而且大部分庫中的函式,在核心中都有對應的函式實現了。
3.2 標頭檔案
體系結構相關的標頭檔案集位於核心原始碼樹的arch/<architecture>/include/asm目錄下
- 行內函數
核心開發者通常把那些對時間要求比較高,而本身長度有比較短的函式定義成行內函數。
定義行內函數需要使用static作為關鍵字,並且用inline限定它。
static inline void wolf(unsinged long tail_size);
- 分支宣告
核心把gcc優化指令封裝成巨集,likely()和unlikely()。
也就是說,使用likely(),執行if後面的語句的機會更大,使用unlikely(),執行else後面的語句機會更大一些。
通過這種方式,編譯器在編譯過程中,會將可能性更大的程式碼緊跟著後面的程式碼,從而減少指令跳轉帶來的效能上的下降。
比如 :
if (likely(a>b)) {
fun1();
}
if (unlikely(a>b)){
fun2();
}
這裡就是程式設計師可以確定 a>b 在程式執行流程中出現的可能相比較大,因此運用了likely()告訴編譯器將fun1()函式的二進位制程式碼緊跟在前面程式的後面,這樣就cache在預取資料時就可以將fun1()函式的二進位制程式碼拿到cache中。這樣,也就添加了cache的命中率。