《Linux核心完全註釋》學習筆記:2.7 Linux核心原始碼的目錄結構

Larcvz發表於2024-06-12

由於Linux核心是一種單核心模式的系統,因此核心中所有程式幾乎都有緊密的聯絡,它們之間的呼叫關係非常密切。所以在閱讀一個原始碼檔案時往往需要參閱其他相關的檔案。因此有必要在開始閱讀核心原始碼之前,先熟悉一下原始碼檔案的目錄結構。

這裡我們首先列出 Linux 核心完整的原始碼目錄,包括其中的子目錄。然後逐一介紹各個目錄中所含程式的主要功能,使得整個核心原始碼的安排形式能在我們的頭腦中建立起一個大概的框架,有利於下一章開始的原始碼閱讀工作。當我們使用 tar 命令將 linux-0.11.tar.gz 解開時,核心原始碼檔案被放到了 linux 目錄中。其中的目錄結構如圖 2-15 所示。

image
圖2-15 Linux核心原始碼目錄結構

該核心版本的原始碼目錄中含有 14 個子目錄,總共包括 102 個程式碼檔案。下面逐個對這些子目錄中的內容進行描述。

linux/ 目錄是原始碼的主目錄,在該主目錄中除了包括所有的 14 個子目錄以外,還含有唯一的一個 Makefile 檔案。該檔案是編譯輔助工具軟體 make 的引數配置檔案。make 工具軟體的主要用途是透過識別哪些檔案已被修改過,從而自動地決定在一個含有多個源程式檔案的程式系統中哪些檔案需要被重新編譯。因此,make 工具軟體是程式專案的管理軟體。

linux/ 目錄下的這個 Makefile 檔案還巢狀地呼叫了所有子目錄中包含的 Makefile 檔案,這樣,當 linux/ 目錄(包括子目錄)下的任何檔案被修改過時,make 都會對其進行重新編譯。因此為了編譯整個核心所有的原始碼檔案,只要在 linux 目錄下執行一次make 命令即可。

1. 引導啟動程式目錄 boot

boot 目錄中含有3個組合語言檔案,是核心原始碼檔案中最先被編譯的程式。這3個程式完成的主要功能是當計算機加電時引導核心啟動,將核心程式碼載入到記憶體中,並做一些進入32位保護執行方式前的系統初始化工作。

其中 bootsect.s 和 setup.s 程式需要使用 as86 軟體來編譯,使用的是 as86 的組合語言格式(與微軟的類似),而 head.s 需要用GNU as來編譯,使用的是AT&T格式的組合語言。

這兩種組合語言在下一章的程式碼註釋裡以及程式碼列表後面的說明中會有簡單的介紹。

  1. bootsect.s 程式是磁碟引導塊程式,編譯後會駐留在磁碟的第一個扇區中(引導扇區,0磁軌(柱面),0磁頭,第1個扇區)。在PC加電ROM BIOS自檢後,將被BIOS載入到記憶體0x7C00處執行。
  2. setup.s 程式主要用於讀取機器的硬體配置引數,並把核心模組 system 移動到適當的記憶體位置處。
  3. head.s 程式會被編譯連結在 system 模組的最前部分,主要進行硬體裝置的探測設定和記憶體管理頁面的初始設定工作。

2. 檔案系統目錄fs

fs 目錄是檔案系統實現程式的目錄,共包含 17 個 C 語言程式。這些程式之間的主要引用關係見圖2-16。圖中每個方框代表一個檔案,從上到下基本按引用關係放置。其中各檔名均略去了字尾.c,虛線框中的程式檔案不屬於檔案系統,帶箭頭的線條表示引用關係,粗線條表示有相互引用關係。

image
圖2-16 fs 目錄中各程式中函式之間的引用關係

由圖可看出,這些程式可以分成四個部分:

  1. 高速緩衝區管理
  2. 底層檔案操作
  3. 檔案資料訪問
  4. 檔案高層函式

在對本目錄中檔案進行註釋時,我們也將分成這四個部分來描述。

對於檔案系統,我們可以將它看成是記憶體高速緩衝區的擴充套件部分。所有對檔案系統中資料的訪問,都需要首先讀取到高速緩衝區中。本目錄中的程式主要用來管理高速緩衝區中緩衝塊的使用分配和塊裝置上的檔案系統。管理高速緩衝區的程式是 buffer.c,而其他程式則主要用於檔案系統管理。

  1. 在 file_table.c 檔案中,目前僅定義了一個檔案控制代碼(描述符)結構陣列。
  2. ioctl.c 檔案將引用 kernel/chr_drv/tty.c 中的函式,實現字元裝置的 I/O 控制功能。
  3. exec.c 程式主要包含一個執行程式函式 do_execve(),它是所有 exec() 函式簇中的主要函式。
  4. fcntl.c 程式用於實現檔案 I/O 控制的系統呼叫函式。
  5. read_write.c 程式用於實現檔案讀/寫和定位三個系統呼叫函式。
  6. stat.c 程式中實現了兩個獲取檔案狀態的系統呼叫函式。
  7. open.c 程式主要包含實現修改檔案屬性和建立與關閉檔案的系統呼叫函式。
  8. char_dev.c 主要包含字元裝置讀寫函式 rw_char()
  9. pipe.c 程式中包含管道讀寫函式和建立管道的系統呼叫。
  10. file_dev.c 程式中包含基於i節點和描述符結構的檔案讀寫函式。
  11. namei.c 程式主要包括檔案系統中目錄名和檔名的操作函式和系統呼叫函式。
  12. block_dev.c 程式包含塊資料讀和寫函式。
  13. inode.c 程式中包含針對檔案系統 i 節點操作的函式。
  14. truncate.c 程式用於在刪除檔案時釋放檔案所佔用的裝置資料空間。
  15. bitmap.c 程式用於處理檔案系統中 i 節點和邏輯資料塊的點陣圖。
  16. super.c 程式中包含對檔案系統超級塊的處理函式。
  17. buffer.c 程式主要用於對記憶體高速緩衝區進行處理。

虛框中的 ll_rw_block 是塊裝置的底層讀函式,它並不在fs目錄中,而是 kernel/blk_drv/ll_rw_block.c 中的塊裝置讀寫驅動函式。放在這裡只是讓我們清楚地看到,檔案系統對於塊裝置中資料的讀寫,都需要透過高速緩衝區與塊裝置的驅動程式 ll_rw_block() 來進行,檔案系統程式集本身並不直接與塊裝置的驅動程式打交道。

在對程式註釋過程中,我們將另外給出這些檔案中各個主要函式之間的呼叫層次關係。

3. 標頭檔案主目錄 include

標頭檔案目錄中總共有 32 個 .h 標頭檔案。其中 include/ 主目錄下有13個標頭檔案,其他標頭檔案則存放在 asm(4個)、linux(10個)和 sys(5個)三個子目錄中。

  1. include/ 主目錄下的標頭檔案主要是供核心和使用者程式使用;
  2. asm/ 子目錄主要用於存放與計算機硬體體系結構密切相關的標頭檔案;
  3. linux/ 子目錄用於存放 Linux 核心專用的標頭檔案;
  4. sys/ 子目錄用於存放一些與檔案狀態、程序、系統資料型別等有關的標頭檔案。

4. 核心初始化程式目錄 init

該目錄中僅包含一個檔案 main.c,用於執行核心所有的初始化工作,然後移到使用者模式建立新程序,並在控制檯裝置上執行 shell 程式。

  1. 程式首先根據機器記憶體的容量對緩衝區記憶體容量進行分配,
    1. 如果還設定了虛擬盤,則在緩衝區記憶體後面也為它留下空間。
  2. 之後就進行所有硬體的初始化工作,包括人工建立第一個任務(task 0),並設定中斷允許標誌。
  3. 在執行從核心態移到使用者態之後,系統第一次呼叫建立程序函式 fork(),建立出一個用於執行 init() 的程序,在該子程序中,系統將進行控制檯環境設定,並且再生成一個子程序用來執行 shell 程式。

順序按描述實際上是可以再細分的。

5. 核心程式主目錄 kernel

linux/kernel 目錄中共包含12個程式碼檔案和一個 Makefile 檔案,另外還有3個子目錄。由於這些檔案中程式碼之間呼叫關係複雜,因此這裡就不詳細列出各檔案之間的引用關係,但仍然可以進行大概分類,如圖2-17所示。

image
圖2-17 各檔案的呼叫層次關係

  1. asm.s 程式用於處理系統硬體異常所引起的中斷
  2. 對各硬體異常的實際處理程式則是在 traps.c 檔案中,在各個中斷處理過程中,將分別呼叫 traps.c 中相應的C語言處理函式。
  3. exit.c 程式主要包括用於處理程序終止的系統呼叫。包含:
    1. 程序釋放、
    2. 會話(程序組)終止
    3. 程式退出處理函式
    4. 殺死程序
    5. 終止程序
    6. 掛起程序等系統呼叫函式。
  4. fork.c 程式給出了程序建立系統呼叫 sys_fork() 相關的兩個C語言函式。
  5. mktime.c 程式包含一個核心使用的時間函式 mktime(),用於計算從1970年1月1日0時起到開機當日的時間,作為開機時間(秒)。僅在 init/main.c 中被呼叫一次。
  6. panic.c 程式包含一個顯示核心出錯資訊並停機的函式 panic()。
  7. printk.c 包含一個核心專用資訊顯示函式 printk()。
  8. vsprintf.c 實現了現已歸入標準庫中的字串格式化函式。
  9. sched.c 程式中包括有關排程的基本函式(sleep_on、wakeup、schedule 等)以及一些簡單的系統呼叫函式。另外還有幾個與定時相關的軟盤操作函式。
  10. signal.c 程式中包括了有關訊號處理的 4 個系統呼叫以及一個在對應的中斷處理程式中處理訊號的函式 do_signal()。
  11. sys.c 程式包括很多系統呼叫函式,其中有些還沒有實現。
  12. system_call.s 程式實現了系統呼叫(int 0x80)的介面處理過程,實際的處理過程則包含在各系統呼叫相應的C語言處理函式中,這些處理函式分佈在整個Linux核心程式碼中。

5.1 塊裝置驅動程式子目錄 kernel/blk_drv

通常情況下,使用者是透過檔案系統來訪問裝置的,因此裝置驅動程式為檔案系統實現了呼叫介面。

在使用塊裝置時,由於其資料吞吐量大,為了能夠高效地使用塊裝置上的資料,在使用者程序與塊裝置之間使用了高速緩衝機制。在訪問塊裝置上的資料時,系統首先以資料塊的形式把塊裝置上的資料讀入到高速緩衝區中,然後再提供給使用者。blk_drv 子目錄共包含4個 C 檔案和1個標頭檔案。標頭檔案 blk.h 由於是塊裝置程式專用的,所以與C檔案放在一起。這幾個檔案之間的大致關係見圖2-18。

image
圖2-18 blk_drv子目錄中檔案的層次關係

  1. blk.h 中定義了3個C程式中共用的塊裝置結構和資料塊請求結構。
  2. hd.c 程式主要實現對硬碟資料塊進行讀/寫的底層驅動函式 do_hd_request() 等;
  3. floppy.c 程式主要實現了對軟盤資料塊的讀/寫驅動函式。
  4. ll_rw_blk.c程式實現了底層塊裝置資料讀/寫函式 ll_rw_block(),核心中所有其他程式都是透過該函式對塊裝置進行資料讀寫操作。你將看到該函式在許多訪問塊裝置資料的地方被呼叫,尤其是在高速緩衝區處理檔案fs/buffer.c 中。

5.2 字元裝置驅動程式子目錄 kernel/chr_drv

字元裝置程式子目錄共含有4個C語言程式和2個彙編程式檔案。這些檔案實現了對串列埠rs-232、序列終端、鍵盤和控制檯終端裝置的驅動。圖2-19顯示了這些檔案之間的大致呼叫層次關係。

image
圖2-19 字元裝置程式之間的關係示意圖

  1. tty_io.c 程式中包含 tty 字元裝置讀函式 tty_read() 和寫函式 tty_write(),為檔案系統提供了上層訪問介面。另外還包括在序列中斷處理過程中呼叫的C函式 do_tty_interrupt(),該函式將會在中斷型別為讀字元的處理中被呼叫。
  2. console.c 檔案主要包含控制檯初始化程式和控制檯寫函式 con_write(),用於被tty裝置呼叫,還包含對顯示器和鍵盤中斷的初始化設定函式 con_init()
  3. rs_io.s 彙編程式用於實現兩個序列介面的中斷處理程式。該中斷處理程式會根據從中斷標識暫存器(埠0x3fa或0x2fa)中取得的4種中斷型別分別進行處理,並在處理中斷型別為讀字元的程式碼中呼叫 do_tty_interrupt()
  4. serial.c 用於對非同步序列通訊晶片UART進行初始化操作,並設定兩個通訊埠的中斷向量。另外還包括tty用於向串列埠輸出的 rs_write() 函式。
  5. keyboard.s 程式主要實現了鍵盤中斷處理過程 keyboard_interrupt
  6. tty_ioctl.c 程式實現了tty的I/O控制介面函式 tty_ioctl() 以及對termio[s]終端I/O結構的讀寫函式,並會在實現系統呼叫 sys_ioctl() 的 fs/ioctl.c 程式中被呼叫。

5.3 協處理器模擬和操作程式子目錄 kernel/math

該子目錄中目前僅有一個C程式 math_emulate.c。其中的 math_emulate() 函式是中斷int7的中斷處理程式呼叫的C函式。當機器中沒有數學協處理器,而CPU執行了協處理器的指令時,就會引發該中斷。因此,使用該中斷就可以用軟體來模擬協處理器的功能。本書所討論的核心版本還沒有包含有關協處理器的模擬程式碼。本程式只是列印一條出錯資訊,並向使用者程式傳送一個協處理器錯誤訊號 SIGFP E

6. 核心庫函式目錄 lib

核心庫函式用於為初始化程式 init/main.c 執行在使用者態的程序提供呼叫支援。它與普通靜態函式庫的實現方法完全一樣。在該目錄中共有12個C語言檔案,除了一個由 Tytso 編制的 malloc.c 程式較長以外,其他的程式都很短,有的只有一兩行程式碼。這些檔案主要包括:

  1. 退出函式 _exit()
  2. 關閉檔案函式 close()
  3. 複製檔案描述符函式 dup()
  4. 檔案開啟函式 open()
  5. 寫檔案函式 write()
  6. 執行程式函式 execve()
  7. 記憶體分配函式 malloc()
  8. 等待子程序狀態函式 wait()
  9. 建立會話系統呼叫 setsid()
  10. 以及在 include/string.h 中實現的所有字串操作函式。

7. 記憶體管理程式目錄 mm

該目錄包括2個程式碼檔案。主要用於管理程式對主記憶體區的使用,實現了程序邏輯地址到線性地址以及線性地址到主記憶體區中實體記憶體地址的對映,透過記憶體的分頁管理機制,在程序的虛擬記憶體頁與主記憶體區的實體記憶體頁之間建立了對應關係。

  1. page.s檔案包括記憶體頁面異常中斷(int 14)處理程式,主要用於處理程式由於缺頁而引起的頁異常中斷和訪問非法地址而引起的頁保護。
  2. memory.c程式包括對記憶體進行初始化的函式 mem_init(),由 page.s 的記憶體處理中斷過程呼叫的 do_no_page()do_wp_page() 函式。在建立新程序而執行復制程序操作時,使用該檔案中的記憶體處理函式來分配管理記憶體空間。

8. 編譯核心工具程式目錄tools

該目錄下的 build.c 程式用於將 linux 各個目錄中被分別編譯生成的目的碼連結合併成一個可執行的核心映像檔案 image。其具體的功能可參見第3章內容。

相關文章