1. 程式人生 > >Linux核心設計與實現 總結筆記(第二章)

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的命中率。