理解Linux的系統(tǒng)調(diào)用
現(xiàn)在,您或許正在查看設(shè)備驅(qū)動程序,并感到奇怪:“函數(shù) foo_read()
是如何被調(diào)用的?”或者可能疑惑: “當我輸入 cat /proc/cpuinfo
時,cpuinfo()
函數(shù)是如何被調(diào)用的?”內(nèi)核完成引導后,控制流就從相對直觀的“接下來調(diào)用哪個函數(shù)?”改變?yōu)槿Q于系統(tǒng)調(diào)用、異常和中斷。
字面上講,系統(tǒng)調(diào)用(也稱為“syscall”)就是一條類似于“add”或者“jump”的指令。從更高的層面上講,系統(tǒng)調(diào)用是用戶級程序要求操 作系統(tǒng)為它做某些事情的途徑。如果您正在編寫程序,需要讀取某個文件,那么要使用一個系統(tǒng)調(diào)用來要求操作系統(tǒng)為您讀取那個文件。
這里是系統(tǒng)調(diào)用的工作原理。首先,用戶程序為系統(tǒng)調(diào)用設(shè)置參數(shù)。其中一個參數(shù)是系統(tǒng)調(diào)用編號(稍后對此進行詳述)。注意,所有這些都是由庫函數(shù)自動 完成的,除非您是使用匯編編程。參數(shù)設(shè)置完成后,程序執(zhí)行“系統(tǒng)調(diào)用”指令。這個指令會導致一個異常:產(chǎn)生一個事件,這個事件會致使處理器跳轉(zhuǎn)到一個新的 地址,并開始執(zhí)行那里的代碼。
新地址的指令會保存程序的狀態(tài),計算出應該調(diào)用哪個系統(tǒng)調(diào)用,調(diào)用內(nèi)核中實現(xiàn)那個系統(tǒng)調(diào)用的函數(shù),恢復用戶程序狀態(tài),然后將控制權(quán)返還給用戶程序。系統(tǒng)調(diào)用是設(shè)備驅(qū)動程序中定義的函數(shù)最終被調(diào)用的一種方式。
這就是系統(tǒng)調(diào)用如何工作的一個簡短說明。接下來,我們將為那些對內(nèi)核事實上如何完成感到好奇的這些人提供詳盡的細節(jié)。不要擔心您是否完全理解所有細 節(jié) —— 只需要記住這是內(nèi)核中的函數(shù)最終被調(diào)用的一個途徑 —— 沒有任何神秘之處。您可以追蹤控制流在內(nèi)核中的全部歷程 —— 有時會有些困難,但是您可以做得到。
這里非常適合于開始根據(jù)理論展示一些代碼。我們將研究 read()
系統(tǒng)調(diào)用的過程,首先從系統(tǒng)調(diào)用指令被執(zhí)行的時候開始。使用 PowerPC 體系結(jié)構(gòu)作為代碼體系結(jié)構(gòu)相關(guān)部分的示例。在 PowerPC 上,當執(zhí)行一個系統(tǒng)調(diào)用時,處理器跳轉(zhuǎn)到地址 0xc00
。那個位置的代碼是在文件 arch/ppc/kernel/head.S
中定義的。類似如下:
|
這段代碼所做的事情是,保存一些狀態(tài),然后調(diào)用另一個名為
DoSyscall
的函數(shù)。
EXCEPTION_PROLOG
是一個宏,負責從用戶空間到內(nèi)核空間的切換,這需要保存用戶進程的寄存器狀態(tài)。使用此例程的地址和函數(shù) DoSyscall
的地址來調(diào)用 EXC_XFER_EE_LITE
。最后,某些狀態(tài)將會被保存,DoSyscall
將會被調(diào)用。后面的兩行在地址 0xd00
和 0xe00
保存兩個異常向量。
EXC_XFER_EE_LITE
類似如下:
|
EXC_XFER_TEMPLATE
是另一個宏,代碼類似如下:
|