前言
之前弄個一段時間內核,而最近在應用程序特別是C++ 方面開發多一些。當前日常工作中碰到一些性能分析、不同鎖API選擇的問題,發現由於對用戶態程序API背後的工作原理,特別是它和內核的調用關係、在內核中具體實現過程不甚清楚,導致前期的預期無法進行分析。爲此,打算結合工作中碰到的問題,比如:
- 用戶態程序如何執行系統調用;
- 用戶態鎖的底層實現及其和內核態鎖的關係如何;
- 用戶態不同鎖(自旋鎖,讀寫鎖,條件鎖)和無鎖機制的對比該如何做理論分析;
- Libaio 異步IO在內核中的具體使怎樣實現的;
- 系統調用、線程調度、中斷分別對系統性能、CPU消耗的影響如何;
希望通過理清上面的這些基礎問題,打通腦子中相互孤立的知識點,幫助他們建立起比較緊密的聯繫。
系統調用ABC
系統調用的功能
從操作系統的角度看,系統調用是內核提供用戶態程序操作最底層軟件或硬件的接口。Linux 系統中每種硬件系統架構都有自己的系統調用表,比如下面mips的系統調用表:
EXPORT(sys_call_table)
PTR sys_read /* 5000 */
PTR sys_write
PTR sys_open
PTR sys_close
....
PTR sys_statx
PTR sys_rseq
PTR sys_io_pgetevents
.size sys_call_table,.-sys_call_table
...
NESTED(handle_sys64, PT_SIZE, sp)
#if !defined(CONFIG_MIPS32_O32) && !defined(CONFIG_MIPS32_N32)
/*
* When 32-bit compatibility is configured scall_o32.S
* already did this.
*/
.set noat
SAVE_SOME
TRACE_IRQS_ON_RELOAD
STI
.set at
.....
syscall_common:
dsubu t2, v0, __NR_64_Linux
sltiu t0, t2, __NR_64_Linux_syscalls + 1
beqz t0, illegal_syscall
dsll t0, t2, 3 # offset into table
dla t2, sys_call_table
daddu t0, t2, t0
ld t2, (t0) # syscall routine
beqz t2, illegal_syscall
jalr t2 # Do The Real Thing (TM)
系統調用的工作過程
從調用者的角度看:
(1)參考系統調用的操作規範,準備參數和數據;
(2)通過系統調用的標準接口模式,調用系統調用的接口;
從被調用者的角度看:
(1)將處理機狀態由用戶態轉爲系統態之後,由硬件和內核程序進行系統調用的一般性處理,即首先保護被中斷進程的CPU環境;
(2)分析系統調用類型,轉入相應的系統調用處理子程序;
(3)在系統調用處理子程序執行完後,恢復被中斷的或設置新進程的CPU現場,然後返冋被中斷進程或新進程,繼續往下執行。
用戶態程序快速調用系統調用
當前爲了快速開發,也可以通過syscall 快速調用,比如下面Libaio的兩個快速調用的實現:
/* Actual syscalls */
int io_setup(int maxevents, io_context_t *ctxp) {
return syscall(_io_setup, maxevents, ctxp);
}
int io_destroy(io_context_t ctx) {
return syscall(__io_destroy, ctx);
}
讀者可以根據上面的示例,調用自己的系統調用。