linux核心解讀入門

2023-02-09 14:30:02 字數 6024 閱讀 9370

很多linux 愛好者對核心很感興趣卻無從下手,本文旨在介紹一種解讀linux核心原始碼的入門方法,而不是講解linux複雜的核心機制。

1.核心源程式的檔案組織

(1)linux核心源程式通常都安裝在/usr/src/linux下,而且它有乙個非常簡單的編號約定:任何偶數的核心(中間數字)如:2.

0.30都是乙個穩定的發行的核心,而任何奇數的核心如:2.

1.42都是乙個開發中的核心。

本文基於穩定的2.2.5源**,第二部分的實現平台為redhat linux 6.0。

(2)核心源程式的檔案按樹形結構進行組織,在源程式樹的最上層你會看到這樣一些目錄:

arch:arch子目錄包括了所有和體系結構相關的核心**。它的每乙個子目錄都代表一種支援的體系結構,例如i386就是關於intel cpu及與之相相容體系結構的子目錄。

pc機一般都基於此目錄;

include:include子目錄包括編譯核心所需要的大部分標頭檔案。與平台無關的標頭檔案在include/linux子目錄下,與intel cpu相關的標頭檔案在include/asm-i386子目錄下,而include/scsi目錄則是有關scsi裝置的頭檔案目錄;

init:這個目錄包含核心的初始化**(注:不是系統的引導**),包含兩個檔案和這是研究核心如何工作的乙個非常好的起點;

mm:這個目錄包括所有獨立於cpu 體系結構的記憶體管理**,如頁式儲存管理記憶體的分配和釋放等,而和體系結構相關的記憶體管理**則位於arch/*/mm/,例如arch/i386/mm/

kernel:主要的核心**,此目錄下的檔案實現了大多數linux系統的核心函式,其中最重要的檔案當屬同樣,和體系結構相關的**在arch/*/kernel中;

drivers:放置系統所有的裝置驅動程式;每種驅動程式又各占用乙個子目錄,如/block下為塊裝置驅動程式,比如ide(如果你希望檢視所有可能包含檔案系統的裝置是如何初始化的,你可以看drivers/block/中的device_setup()函式。它不僅初始化硬碟,也初始化網路,因為安裝nfs檔案系統的時候需要使用網路。

其他目錄如lib:放置核心的庫**;net:核心與網路相關的**;ipc:

包含核心的程序間通訊的**;fs:所有的檔案系統**和各種型別的檔案操作**,它的每乙個子目錄支援乙個檔案系統,例如fat和ext2、scripts,此目錄包含用於配置核心的指令碼檔案等。

一般在每個目錄下都有乙個.depend 檔案和乙個makefile 檔案,這兩個檔案都是編譯時使用的輔助檔案,仔細閱讀這兩個檔案對弄清各個檔案之間的聯絡和依託關係很有幫助,而且在有的目錄下還有readme 檔案,它是對該目錄下的檔案的一些說明,同樣有利於我們對核心原始碼的理解。

2.解讀實戰:為你的核心增加乙個系統呼叫

雖然linux 的核心原始碼用樹形結構組織得非常合理、科學,把與功能相關聯的檔案都放在同乙個子目錄下,這樣使得程式更具可讀性。然而,linux 的核心原始碼實在是太大而且非常複雜,即便採用了很合理的檔案組織方法,在不同目錄下的檔案之間還是有很多的關聯,分析核心的一部分**通常要檢視其他的幾個相關的檔案,而且可能這些檔案還不在同乙個子目錄下。

下面舉乙個具體的核心分析例項,希望能通過這個例項,使讀者對linux 的核心組織有些具體的認識,讀者從中也可以學到一些對核心的分析方法。

以下即為分析例項:

(1)操作平台

硬體:cpu intel pentium ii;

軟體:redhat linux 6.0,核心版本2.2.5

(2)相關核心源**分析

①系統的引導和初始化:linux 系統的引導有好幾種方式,常見的有lilo、loadin引導和linux的自舉引導(bootsect-loader),而後者所對應源程式為arch/i386/boot/它為實模式的匯程式設計序,限於篇幅在此不做分析。無論是哪種引導方式,最後都要跳轉到arch/i386/kernel/主要是進行實模式下的初始化,為系統進入保護模式做準備。

此後,系統執行arch/i386/kernel/ (對經壓縮後存放的核心要先執行arch/i386/boot/compressed/ 中定義的一段匯程式設計序setup_idt,它負責建立一張256項的idt表(interrupt descriptor table),此表儲存著所有自陷和中斷的入口位址,其中包括系統呼叫總控程式system_call 的入口位址。當然,除此之外,還要做一些其他的初始化工作。

②系統初始化後執行的第乙個核心程式asmlinkage void __init start_kernel(void) 定義在/usr/src/linux/init/中,它通過呼叫usr/src/linux/arch/i386/kernel/ 中的乙個函式void __init trap_init(void) 把各個自陷和中斷服務程式的入口位址設定到idt表中,其中系統呼叫總控程式system_cal就是中斷服務程式之一;void __init trap_init(void)函式則通過呼叫乙個巨集set_system_gate(syscall_vector,&system_call),把系統呼叫總控程式的入口掛在中斷0x80上。

其中syscall_vectr是定義在/usr/src/linux/arch/i386/kernel/中的乙個常量0x80,而system_call 即為中斷總控程式的入口位址,中斷總控程式用組合語言定義在/usr/src/linux/arch/i386/kernel/中。

③中斷總控程式主要負責儲存處理機執行系統呼叫前的狀態,檢驗當前呼叫是否合法,並根據系統呼叫向量,使處理機跳轉到儲存在sys_call_table 表中的相應系統服務例程的入口,從系統服務例程返回後恢復處理機狀態退回使用者程式。

而系統呼叫向量則定義在/usr/src/linux/include/asm-386/ 中,sys_call_table 表定義在/usr/src/linux/arch/i386/kernel/ 中,同時在/usr/src/linux/include/asm-386/ 中也定義了系統呼叫的使用者程式設計介面。

④由此可見,linux的系統呼叫也像dos系統的int 21h中斷服務,大把0x80中斷作為總的入口,然後轉到儲存在sys_call_table表中的各種中斷服務例程的入口位址,提供各種不同的中斷服務。

提供上源**分析可知,要增加乙個系統呼叫就必須在sys_call_table表中增加一項,並在其中儲存好自己的系統服務例程的入口位址,然後重新編譯核心,當然,系統服務例程是必不可少的。

由此可知,在此版linux核心源程式<2.2.5>中,與系統呼叫相關的源程式檔案就包括以下這些:

* arch/i386/boot/

* rch/i386/kernel/

* rch/i386/boot/compressed/

* rch/i386/kernel/

* nit/

* rch/i386/kernel/

* rch/i386/kernel/

* rch/i386/kernel/

* nclude/asm-386/

當然,這只是涉及到的幾個主要檔案。而事實上,增加系統呼叫真正要修改的檔案只有include/asm-386/ 和arch/i386/kernel/兩個。

(3)原始碼的修改

①kernel/中增加系統服務例程如下:

asmlinkage int sys_addtotal(int numdata)

該函式有乙個int 型入口引數numdata , 並返回從0 到numdata 的累加值,然而也可以把系統服務例程放在乙個自己定義的檔案或其他檔案中,只是要在相應檔案中作必要的說明。

②把smlinkage int sys_addtotal( int) 的入口位址加到sys_call_table表中。

arch/i386/kernel/ 中的最後幾行源**修改前為:

.long symbol_name(sys_sendfile)

.long symbol_name(sys_ni_syscall) /* streams1 */

.long symbol_name(sys_ni_syscall) /* streams2 */

.long symbol_name(sys_vfork) /* 190 */

.rept nr_syscalls-190

.long symbol_name(sys_ni_syscall)

.endr

修改後為:

.long symbol_name(sys_sendfile)

.long symbol_name(sys_ni_syscall) /* streams1 */

.long symbol_name(sys_ni_syscall) /* streams2 */

.long symbol_name(sys_vfork) /* 190 */

/* add by i */

.long symbol_name(sys_addtotal)

.rept nr_syscalls-191

.long symbol_name(sys_ni_syscall)

.endr

③把增加的sys_call_table 表項所對應的向量,在include/asm-386/ 中進行必要申明,以供使用者程序和其他系統程序查詢或呼叫。

增加後的部分/usr/src/linux/include/asm-386/ 檔案如下:

#define __nr_sendfile 187

#define __nr_getpmsg 188

#define __nr_putpmsg 189

#define __nr_vfork 190

/* add by i */

#define __nr_addtotal 191

④測試程式(如下:

#include

#include

_syscall1(int,addtotal,int, num)

main()

對修改後的新的核心進行編譯,並引導它作為新的作業系統,執行幾個程式後可以發現一切正常;在新的系統下對測試程式進行編譯(注:由於原核心並未提供此系統呼叫,所以只有在編譯後的新核心下,此測試程式才可能被編譯通過),運**況如下:

$gcc .test

$./test

please input a number

36total from 0 to 36 is 666

修改成功後對相關原始碼進一步分析可知,在此版本的核心中,從/usr/src/linux/arch/i386/kernel/ 檔案中對sys_call_table 表的設定可以看出,有好幾個系統呼叫的服務例程都是定義在/usr/src/linux/kernel/ 中的同乙個函式:

asmlinkage int sys_ni_syscall(void)

例如第188項和第189項就是如此:

.long symbol_name(sys_sendfile)

.long symbol_name(sys_ni_syscall) /* streams1 */

.long symbol_name(sys_ni_syscall) /* streams2 */

.long symbol_name(sys_vfork) /* 190 */

而這兩項在檔案/usr/src/linux/include/asm-386/ 中卻申明如下:

#define __nr_sendfile 187

#define __nr_getpmsg 188 /* some people actually want streams */

#define __nr_putpmsg 189 /* some people actually want streams */

#define __nr_vfork 190

由此可見,在此版本的核心源**中,由於asmlinkage int sys_ni_syscall(void) 函式並不進行任何操作,所以包括getpmsg, putpmsg 在內的好幾個系統呼叫都是不進行任何操作的,即有待擴充的空調用;但它們卻仍然占用著sys_call_table表項,估計這是設計者們為了方便擴充系統呼叫而安排的,所以只需增加相應服務例程(如增加服務例程getmsg或putpmsg),就可以達到增加系統呼叫的作用。

3.結束語

要完全解讀龐大複雜的linux核心,一篇文章遠遠不能介紹清楚,而且與系統呼叫相關的**也只是核心中極其微小的一部分,重要的是方法,掌握好的分析方法,所以上述分析只是起個引導作用,而真正的分析還有待讀者自己的努力。

寫景狀物類文章閱讀入門

海晏小學楊海燕 在語文課本和課外閱讀中,我們常常會見到一些介紹 描述類文章。這些文章有些是描寫自然現象 如風 雨 雪 地理環境 如森林 高山 名勝古蹟 如故宮 大雁塔 的,我們稱作寫景類文章 有些是描述事物的,如動物 植物 靜物,我們稱做狀物類文章。小學低年級學生對這類描述性文章首先有個初步概念,從...

linux核心及版本介紹

linux的版本分為兩類 核心版本和發行版本。核心版本是指linux的傳世人linus領導的開發小組所開發的作業系統核心的版本號,如2.4.20.通常在核心版本號之後還會附加乙個數字,如2.4.20 8,最後的數字用來表示該版本核心是第幾次被修訂的。linux的核心版本號是由3部分組成 主版本號,次...

Linux核心與驅動面試要點

嵌入式linux核心與驅動面試要點 1.實際經驗 所開發驅動程式或核心模組的來龍去脈 需求 設計思想 實現方法 要點難點,特別是硬體除錯過程中所遇到的特殊情況 所修復bug的現象 除錯手段 原因分析和解決方案。2.驅動除錯 核心的除錯手段 應用程式的除錯手段 核心panic所dump的資訊以及pan...