首先研究 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
文件 #i nclude
就可以獲得用於那個體系結構的正確文件。其他目錄中包含的是 非-體系結構-相關 的頭文件。如果在不只一個 .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
中可以找到這個函數)。
代碼的執行順序大致如下:
|
更詳細地講,發生的事情是:
- 執行體系結構相關的設置代碼:
- 如果需要,解壓縮並移動內核代碼本身
- 初始化硬件
- 這可能包括底層內存管理的設置
- 將控制權轉交給函數
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
在何處打印出來。