1、獲取內核源碼
1.1 下載內核
登錄Linux內核官方網站,獲取Linux源代碼。
1.2 Git
使用Git來下載和管理Linux內核源代碼。
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
使用Git獲取最新提交到Linux版本樹的一個副本。
$ git pull
更新自己的分支到Linux的最新分支。
1.3 使用補丁
可以以補丁的形式發佈對代碼的修改,也可以以補丁的形式接收其他人所做的修改。
$ patch -pl < ../patch-x.y.z
從內核源碼樹開始運行命令,應用增量補丁,進行版本轉移。
2、內核源碼樹
目錄 | 描述 |
---|---|
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 | 虛擬化基礎結構 |
Linux源碼樹中的其他文件:COPYING文件是內核許可證(GNU GPL v2)。CREDITS是開發了很多內核代碼的開發者列表。MAINTAINERS是維護者列表,負責維護內核子系統和驅動程序。Makefile是基本內核的Makefile。
3、編譯內核
3.1 配置內核
$ make config
逐一遍歷所有配置項,要求用戶進行選擇,耗費時間很長。
$ make menuconfig
$ make gconfig
前者是基於ncurse庫編制的圖形界面工具,後者是基於gtk+的圖形工具。將所有配置項分門別類放置。
$ make defconfig
基於默認的配置爲你的體系結構創建一個配置。
配置項會被存放在內核代碼樹根目錄下的.config文件中,可以從中查找和修改內核選項。
$ make oldconfig
在修改過配置文件後,或者用已有的配置文件配置新的代碼樹時,應該進行驗證和更新。
$ zcat /proc/config.gz > .config
$ make oldconfig
配置選項CONFIG_IKCONFIG_PROC把完整的壓縮過的內核配置文件存放在/proc/config.gz下,編譯一個新內核時可以方便地克隆當前的配置。
內核配置好後,使用make
命令進行編譯。
3.2 減少編譯的垃圾信息
$ make > ../detritus
對輸出進行重定向。
$ make > /dev/null
將輸出信息重定向到永無返回值的黑洞/dev/null。
3.3 衍生多個編譯作業
$ make -jn
默認情況make只衍生一個作業,因爲Makefile常會出現不正確的依賴信息,對於不正確的依賴,多個作業可能會互相踩踏,導致編譯過程出錯。但是內核的Makefile沒有這樣的編碼錯誤。
make程序能把編譯過程拆分成多個並行的作業,每個作業獨立併發地運行,極大地加快多處理器系統上的編譯過程,改善處理器的利用率。
3.4 安裝內核
內核的安裝和體系結以及啓動引導工具(bootloader)相關。查閱啓動引導工具的說明,按照他的指導將內核映像拷貝到合適的位置。
模塊的安裝是自動的,獨立於體系結構。以root身份運行命令,將所有已編譯的模塊安裝到目錄/lib/modules下。
$ make modules_install
編譯時會在內核代碼樹的根目錄下一個符號對照表System.map文件。
4、內核開發特點
- 內核編程時既不能訪問C庫也不能訪問標準的C頭文件。
- 內核編程時必須使用GNU C。
- 內核編程時缺乏像用戶空間那樣的內存保護機制。
- 內核編程時難以執行浮點運算。
- 內核給每個進程只有一個很小的定長堆棧。
- 由於內核支持異步中斷、搶佔和SMP(對稱多處理器),必須時刻注意同步和併發。
- 要考慮可移植性的重要性。
4.1 無libc庫或無標準頭文件
與用戶空間的應用程序不同,內核不能鏈接使用標準C函數庫或者其他的庫。主要原因是速度和大小。完整的C庫對內核來說都太大且低效。不過大部分常用的C庫函數在內核中都已經得到了實現。
頭文件指的是組成內核源代碼樹的內核頭文件。內核源代碼文件不能包含外部頭文件,就想他們不能用外部庫一樣。
printk(KERN_ERR "this is an error\n");
內核代碼使用的printk函數與printf函數相似,負責把格式化好的字符串拷貝到內核日誌緩衝區,syslog程序通過讀取該緩衝區來獲取內核信息。此外,printk可以指定一個標誌來設置優先級,syslogd根據這個優先級標誌決定在什麼地方顯示這條系統消息。
4.2 GNU C
內核開發使用的C語言涵蓋了ISO C99標準和GNU C擴展特性。
內聯函數
static inline void wolf(unsigned long tail_size)
內聯函數會在它所調用的位置上展開,消除函數調用和返回帶來的開銷(寄存器存儲和恢復),可以使編譯器進一步優化代碼。但是,代碼會變長,佔用更多的內存空間或指令緩存。
內聯函數需要使用static作爲關鍵字,並用inline限定它。通常把對時間要求比較高且本身長度較短的函數定義成內聯函數。
內嵌彙編
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
/* low和high分別包含64位時間戳的低32位和高32位 */
內核編程時,知道對應的體系結構,可以使用內聯彙編功能,使用asm()指令嵌入彙編代碼。
分支聲明
if (unlikely(error))
if (likely(error))
unlikely()宏表示絕少發生的分支。
likely()宏表示通常爲真的分支。
4.3 沒有內存保護機制
在內核中不應該訪問非法的內存地址,否則內核可能會死掉卻沒有通知。
內核中的內存不分頁,每用掉一個字節,物理內存就減少一個字節。
4.4 不要輕易在內核中使用浮點數
內核不能完美地支持浮點操作,因爲內核本身不能陷入。使用浮點數時,處理要人工保存和恢復浮點寄存器,還有其他瑣碎的事情要做。
4.5 容積小而固定的棧
用戶空間的程序可以從棧上分配大量的空間存放變量。內核棧的準確大小隨體系結構體而變。
在x86上,棧的大小在編譯時配置,可以使4KB也可以是8KB。內核棧的大小是兩頁,所以32位機的內核棧是8KB,64位機是16KB,每個處理器都有自己的棧。
4.6 同步和併發
內核的許多特性都要求能夠併發地訪問共享數據,要有同步機制保證不出現競爭條件。
4.7 可移植性
大部分C代碼應與體系結構無關,在不同體系結構和計算機上都能夠編譯和執行。必須把與體系結構相關的代碼從內核代碼樹的特定目錄中適當地分離出來。
保持字節序、64位對其、不假定字長和頁面長度等準則有助於移植性。