


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