《ARM與Linux些許問題》第四章:ARM平臺系統(tǒng)調用原理分析
首先說明:系統(tǒng)調用不會導致進程上下文切換。
本文引用地址:http://m.butianyuan.cn/article/201611/317769.htm一、介紹系統(tǒng)調用——Linux用戶空間主動進入內核空間的唯一方法
1.系統(tǒng)調用是操作系統(tǒng)提供給用戶程序調用的一組“特殊”接口;用戶程序可以通過這組“特殊”接口來獲得操作系統(tǒng)內核提供的服務。從邏輯上來說,系統(tǒng)調用可被看成是一個內核與用戶空間程序交互的接口;把用戶進程的請求傳達給內核,待內核把請求處理完畢后再將處理結果送回給用戶空間。
2.系統(tǒng)調用按照功能邏輯大致可分為“進程控制”、“文件系統(tǒng)控制”、“系統(tǒng)控制”、“存儲管理”、“網(wǎng)絡管理”、“socket控制”、“用戶管理”和“進程間通信”幾類。
3.內核接口:kernel2.6.35.11/arch/arm/include/asm/unistd.h
二、系統(tǒng)調用的主要用途1.控制硬件——系統(tǒng)調用往往作為硬件資源和用戶空間的抽象接口,比如讀寫文件時用到的write/read調用。
2.設置系統(tǒng)狀態(tài)或讀取內核數(shù)據(jù)——因為系統(tǒng)調用是用戶空間和內核的唯一通訊手段,所以用戶設置系統(tǒng)狀態(tài)、比如開/關某項內核服務(設置某個內核變量)或讀取內核數(shù)據(jù)都必須通過系統(tǒng)調用。比如getpid、getpriority、setpriority和sethostname等。3.進程管理——系統(tǒng)調用接口用來保證系統(tǒng)中進程能以多任務在虛擬內存環(huán)境下運行。比如fork、clone、execve和exit等。
三、內核函數(shù)和系統(tǒng)調用、用戶編程接口(API)、系統(tǒng)命令的關系1.系統(tǒng)調用并非直接和程序員或系統(tǒng)管理員打交道,它僅僅是一個通過軟中斷機制向內核提交請求、獲取內核服務的接口。而在實際使用中程序員調用的多是用戶編程接口——api,而管理員使用的則多是系統(tǒng)命令。
2.用戶編程接口(API)其實是一個函數(shù)定義,說明了如何獲得一個給定的服務,比如read()、malloc()、free()、abs()等。
它有可能和系統(tǒng)調用形式一致,比如read()接口就和read系統(tǒng)調用一一對應;往往會出現(xiàn)幾種不同的API內部用到同一個系統(tǒng)調用,比如malloc()、free()內部利用brk()系統(tǒng)調用來擴大或縮小進程的堆;
或一個API利用了好幾個系統(tǒng)調用組合來完成任務;更有些API甚至不需要任何系統(tǒng)調用,因為它不需要內核服務、如計算整數(shù)絕對值的abs()接口。
Linux系統(tǒng)中這些API主要是通過C庫(libc)實現(xiàn)的;它除了定義的一些標準的C函數(shù)外,一個重要的任務是提供了一套封裝例程、將系統(tǒng)調用在用戶空間包裝后供用戶編程使用。說明:上述封裝并非必須;如果你愿意直接調用,Linux提供了一個syscall()的系統(tǒng)調用函數(shù)來實現(xiàn)調用。
3.系統(tǒng)命令相對編程接口更高了一層,它是內部引用API的可執(zhí)行程序,比如我們常用的系統(tǒng)命令ls、hostname等。三、內核函數(shù)和系統(tǒng)調用的關系
內核函數(shù)沒有想像中那么復雜;它們和普通函數(shù)很像、只不過在內核實現(xiàn),因此要滿足一些內核編程的要求。系統(tǒng)調用是一層用戶進入內核的接口,它本身并非內核函數(shù);進入內核后,不同的系統(tǒng)調用會找到對應到各自的內核函數(shù)——專業(yè)術語叫:系統(tǒng)調用服務例程。
總結:從用戶角度向內核看;依次是系統(tǒng)命令、編程接口、系統(tǒng)調用和內核函數(shù)。四、系統(tǒng)調用的實現(xiàn)
系統(tǒng)調用利用了ARM體系結構中的軟件中斷,軟件中斷和我們常說的中斷(硬件中斷)不同之處在于———它是通過軟件指令觸發(fā)而并非外設,也就是說由編程人員發(fā)出的一種異常;具體地講就是調用SWI匯編指令(x86上int $0x80),這條匯編指令將產生向量為128的編程異常,ARM從用戶模式切入管理模式、并強制R15-PC(程序計數(shù)器)為0x0000 0008,Linux從用戶態(tài)進入內核態(tài)。見:《ARM與Linux些許問題》第一章:ARM工作模式
之所以系統(tǒng)調用需要借助異常實現(xiàn);是因為當用戶態(tài)的進程調用一個系統(tǒng)調用時,CPU便被切換到內核態(tài)執(zhí)行內核函數(shù)。我們前邊分析ARM體系結構部分已經講過進入內核態(tài)——ARM高特權模式,必須經過系統(tǒng)的門機制——異常(SWI匯編指令(x86上int $0x80等);其他異常用戶空間無法利用,都是由內核使用的。)。
1.SWI匯編指令(x86上int $0x80)指令的目的是產生一個編號為128的編程異常,這個編程異常對應中斷描述符表IDT中的第128項——也就是對應的系統(tǒng)門描述符。門描述符中含有一個預設的內核空間地址,它指向了系統(tǒng)調用處理程序:vector_SWI()(x86上system_call())。注意:不是系統(tǒng)調用服務程序本身。
即:系統(tǒng)命令——>用戶編程API——>系統(tǒng)調用(調用SWI匯編指令異常)——>系統(tǒng)調用處理函數(shù)(vector_SWI)——>具體的系統(tǒng)調用服務程序。其中藍色部分是內核態(tài)函數(shù)。
2.所有的用戶空間系統(tǒng)調用函數(shù)都是通過調用SWI匯編指令(x86上int $0x80)異常、進入內核態(tài),此時、ARM默認從某一固定地址執(zhí)行程序(vector_SWI()(x86上system_call())的地址)。vector_SWI()(x86上system_call())這個內核函數(shù)又怎樣分發(fā)這些系統(tǒng)調用到各自的內核服務程序中呢?Linux為每個系統(tǒng)調用都進行了編號(0——_NR_syscall);同時在內核中保存一張系統(tǒng)調用表,該表中保存了系統(tǒng)調用編號和其對應的服務例程。因此,在系統(tǒng)調用通過門陷入內核前,需要把系統(tǒng)調用號一并傳入內核。這個傳遞工作是通過把系統(tǒng)調用號裝入相應寄存器實現(xiàn)的。
有了如上的分析:系統(tǒng)調用處理程序vector_SWI()(x86上system_call())一旦運行;就可以從相應寄存器中得到數(shù)據(jù),然后再去系統(tǒng)調用表中尋找相應的服務例程了。
注意:除了系統(tǒng)調用號之外,有的系統(tǒng)調用還需要傳遞一些參數(shù)給內核;這是Linux在vector_SWI()(x86上system_call())調用時將參數(shù)等值傳入其他寄存器。
內核系統(tǒng)服務例程結束時,system_call()從相應寄存器中獲得系統(tǒng)調用返回值,并把這個返回值放在曾保存用戶態(tài)相應寄存器棧單元的那個位置;然后跳轉到ret_from_sys_call(),終止系統(tǒng)調用處理程序的執(zhí)行。
====================================================================================================================================五、主要路徑
1.用戶空間:libc庫沒有研究代碼,大體機制如下——
調用SWI匯編指令(x86上int $0x80))軟中斷進入內核,并傳入中斷向量號相關中斷向量號arm-2010.09/arm-none-linux-gnueabi/libc/usr/include/bits/syscall.h
#define SYS_getuid __NR_getuid2.內核空間:ARM中系統(tǒng)調用號定義路徑:kernel2.6.35.11/arch/arm/include/asm/unistd.h
#define __NR_getuid (__NR_SYSCALL_BASE+ 24)
異常進入內核空間函數(shù)路徑:kernel2.6.35.11/arch/arm/kernel/entry-common.s
默認執(zhí)行vector_SWI(x86上system_call())函數(shù)
ARM中系統(tǒng)調用表定義路徑:kernel2.6.35.11/arch/arm/kernel/calls.S
CALL(sys_getuid) //第24個,要與前面unistd中對應ARM中系統(tǒng)調用服務程序的聲明路徑:kernel2.6.35.11/include/linux/syscalls.h
asmlinkage long sys_getuid(void);
====================================================================================================================================x86平臺相關路徑(內核):
系統(tǒng)調用號路徑——kernel2.6.35.11/arch/x86/include/asm/unistd.h
system_call()函數(shù)路徑——kernel2.6.35.11/arch/x86/kernel/entry.S
系統(tǒng)調用表路徑——kernel2.6.35.11/arch/x86/kernel/syscall_table.S或直接在entry.S中定義
評論