首先研究 Linux 原始碼樹的頂層目錄,它通常但不總是)位於 /usr/src/linux-。我們不會研究得過於詳細,因為 Linux 原始碼經常會發生變化,但是,我們將嘗試讓給出的資訊足以找出特定驅動程式或函數的位置。
Makefile:這個檔案是整個原始碼樹的頂層 makefile。它定義了很多實用的變數和規則,比如預設的 gcc 編譯標記。
Documentation/:這個目錄中包含很多關於配置核心、運行 ramdisk 等任務的實用資訊但通常是過時的)。不過,與不同配置選項相應的協助條目並不在這裡 —— 它們在每個原始碼目錄的 Kconfig 檔案中。
arch/:所有與體繫結構相關的代碼都在這個目錄以及 include/asm- 目錄中。在此目錄中,每種體繫結構都有自己的目錄。例如,用於基於 PowerPC 的電腦的代碼位於 arch/ppc 目錄中。在這些目錄裡,可以找到底層記憶體管理、中斷處理、早期初始化、彙編常式,等等。
crypto/:這是核心本身所用的加密 API。
drivers/:按照慣例,在此目錄的子目錄中可以找到運行外圍裝置的代碼。包括視頻驅動程式、網路卡驅動程式、底層 SCSI 驅動程式,以及其他類似的驅動程式。例如,在 drivers/net 中可以找到大部分網路卡驅動程式。將一類驅動程式組合在一起的某些更高層代碼,可能會也可能不會)像底層驅動程式本身那些包含在同一目錄中。
fs/:通用檔案系統的代碼稱做 VFS,即 Virtual File System)和各個不同檔案系統的代碼都可以在這個目錄中找到。ext2 檔案系統是在 Linux 中最常廣泛使用的檔案系統之一;在 fs/ext2 中可以找到讀取 ext2 格式的代碼。並不是所有檔案系統都會編譯或運行;對某些尋找核心項目的人而言,更生僻的檔案系統永遠都是理想的候選者。
include/:在 .c 檔案的開頭所包含的大部分標頭檔都可以在這個目錄中找到。 asm- 目錄下是與體繫結構相關的包含include )檔案。部分核心構建過程建立從 asm 指定 asm- 的符號連結。這樣,無需將其固定編碼到 .c 檔案 #include 就可以獲得用於那個體繫結構的正確檔案。其他目錄中包含的是 非-體繫結構-相關 的標頭檔。如果在不只一個 .c 檔案中使用了某個結構體、常量或者變數,那麼它可能應該放入其中一個標頭檔中。
init/:這個目錄中的檔案包括 main.c、建立 早期使用者空間early userspace) 的代碼,以及其他初始化代碼。可以認為 main.c 是核心“粘合劑glue)”。在下一部分將深入討論 main.c。早期使用者空間提供了 Linux 核心引導起來時所需要的功能,而這些功能並不需要在核心本身運行。
ipc/:IPC 的意思是 處理序間通訊interprocess communication)。它包含了共用記憶體、訊號量以及其他形式 IPC 的代碼。
kernel/:不適合放在任何其他位置的通用核心級代碼位於此處。這裡有高層系統調用代碼,以及 printk() 代碼、發送器、訊號處理代碼,等等。檔案名稱包含很多資訊,所以可以使用 ls kernel/,並非能常準確地猜到每個檔案的功能。
lib/:這裡是對所有核心代碼都通用的實用常式。常見的字串操作、調試常式,以及命令列解析代碼都位於此處。
mm/:這個目錄中是高層次核心管理代碼。聯合使用這些常式以及底層的與體繫結構相關的常式通常位於 arch//mm/ 目錄中)來實現虛擬記憶體Virtual memory,VM)。在這裡會完成早期記憶體管理在記憶體子系統完全建立起來之前需要它),以及檔案的記憶體映射、頁快取管理、記憶體配置、RAM 中頁的清除還有很多其他事情)。
net/:這裡是高層網路代碼。底層網路驅動程式與此層次代碼交換資料包,這個層次的代碼可以根據資料包將資料傳遞給使用者層應用程式,或者丟棄資料,或者在核心中使用它。net/core 包含大部分不同的網路通訊協定都可以使用的代碼,和某些位於 net/ 目錄本身中的檔案一樣。特定的網路通訊協定在 net/ 的子目錄下實現。例如,在 net/ipv4 目錄中可以找到 IP版本 4)代碼。
scripts/:這個目錄中包含的指令碼可用於核心的構建,但並不將任何代碼加入到核心本身之中。例如,各種組態工具可以將它們的檔案放在這裡。
security/:在這裡可以找到不同 Linux 安全模型的代碼,比如 NSA Security-Enhanced Linux 以及通訊端和網路安全鉤子函數hooks),以及其他安全選項。
sound/:這裡放置的是音效卡驅動程式和其他與聲音相關的代碼。
usr/:此目錄中的代碼用於構建包含 root 檔案系統映像的 cpio-格式 的歸檔檔案,用於早期使用者空間。
所有這些彙集在哪裡?
init/main.c 檔案是整個 Linux 核心的中央連接點。每種體繫結構都會執行一些底層設定函數,然後執行名為 start_kernel 的函數在 init/main.c 中可以找到這個函數)。
代碼的執行順序大致如下:
Architecture-specific set-up code (in arch//*) | vThe function start_kernel() (in init/main.c) | vThe function init() (in init/main.c) | vThe user level "init" program
|
關於執行順序的更多細節
更詳細地講,發生的事情是:
- 執行體繫結構相關的設定代碼:
- 如果需要,解壓縮並移動核心代碼本身
- 初始化硬體
- 將控制權轉交給函數
start_kernel()
start_kernel() 去執行以下事情以及其他事情):
- 列印核心版本和命令列
- 啟動控制台輸出
- 啟用中斷
- 校準延遲迴圈
- 調用
rest_init(),這個函數會:
- 啟動一個核心線程來運行
init() 函數
- 進入空閑迴圈
init():
- 啟動其他處理器在 SMP 機器上)
- 啟動裝置子系統
- 掛載 root 檔案系統
- 釋放不使用的核心記憶體
- 運行
/sbin/init或者 /etc/init,或者...)
此時,使用者級 init 程式正在運行;它將完成啟動網路裝置並在控制台上運行 getty 登入程式)等任務。
加入自己的 printk,並觀察那個子系統的 printk 相對於自己的 printk 何時出現,就可以指出那個子系統是在 start_kernel() 中還是在 init() 中初始化的。例如,如果想要知道 ALSA 聲音系統何時被初始化,那麼將 printk 加入到 start_kernel() 和 init() 的起始處,然後找到“Advanced Linux Sound Architecture [...]” 相對於您的 printk 在何處列印出來。