1什么是系統調用
???? 系統調用,顧名思義,說的是操作系統提供給用戶程序調用的一組“特殊”接口。用戶程序可以通過這組“特殊”接口來獲得操作系統內核提供的服務,比如用戶可以通過文件系統相關的調用請求系統打開文件、關閉文件或讀寫文件,可以通過時鐘相關的系統調用獲得系統時間或設置定時器等。
從邏輯上來說,系統調用可被看成是一個內核與用戶空間程序交互的接口——它好比一個中間人,把用戶進程的請求傳達給內核,待內核把請求處理完畢后再將處理結果送回給用戶空間。
系統服務之所以需要通過系統調用來提供給用戶空間的根本原因是為了對系統進行“保護”,因為我們知道Linux的運行空間分為內核空間與用戶空間,它們各自運行在不同的級別中,邏輯上相互隔離。所以用戶進程在通常情況下不允許訪問內核數據,也無法使用內核函數,它們只能在用戶空間操作用戶數據,調用用戶空間函數。比如我們熟悉的“hello world”程序(執行時)就是標準的用戶空間進程,它使用的打印函數printf就屬于用戶空間函數,打印的字符“hello word”字符串也屬于用戶空間數據。
但是很多情況下,用戶進程需要獲得系統服務(調用系統程序),這時就必須利用系統提供給用戶的“特殊接口”——系統調用了,它的特殊性主要在于規定了用戶進程進入內核的具體位置;換句話說,用戶訪問內核的路徑是事先規定好的,只能從規定位置進入內核,而不準許肆意跳入內核。有了這樣的陷入內核的統一訪問路徑限制才能保證內核安全無虞。我們可以形象地描述這種機制:作為一個游客,你可以買票要求進入野生動物園,但你必須老老實實地坐在觀光車上,按照規定的路線觀光游覽。當然,不準下車,因為那樣太危險,不是讓你丟掉小命,就是讓你嚇壞了野生動物。
2 Linux 的系統調用 ?
????? ?對于現代操作系統,系統調用是一種內核與用戶空間通訊的普遍手段,Linux系統也不例外。但是Linux系統的系統調用相比很多Unix和windows等系統具有一些獨特之處,無處不體現出Linux的設計精髓——簡潔和高效。
??? ?? Linux系統調用很多地方繼承了Unix的系統調用(但不是全部),但Linux相比傳統Unix的系統調用做了很多揚棄,它省去了許多Unix系統冗余的系統調用,僅僅保留了最基本和最有用的系統調用,所以Linux全部系統調用只有250個左右(而有些操作系統系統調用多達1000個以上)。
??????? 要彌補這個鴻溝,第一,你必須明白系統調用在內核里的主要用途。雖然上面給出了數種分類,不過,總的概括來講,系統調用在系統中的主要用途無非以下幾類:
l ??????? 控制硬件——系統調用往往作為硬件資源和用戶空間的抽象接口,比如讀寫文件時用到的write/read調用。
l ??????? 設置系統狀態或讀取內核數據——因為系統調用是用戶空間和內核的唯一通訊手段,所以用戶設置系統狀態,比如開/關某項內核服務(設置某個內核變量),或讀取內核數據都必須通過系統調用。比如getpgid、getpriority、setpriority、sethostname
l ??????? 進程管理——一系統調用接口是用來保證系統中進程能以多任務在虛擬內存環境下得以運行。比如 fork、clone、execve、exit等
第二,什么服務應該存在于內核;或者說什么功能應該實現在內核而不是在用戶空間。這個問題并沒有明確的答案,有些服務你可以選擇在內核完成,也可以在用戶空間完成。選擇在內核完成通常基于以下考慮:
l ??????? 服務必須獲得內核數據,比如一些服務必須獲得中斷或系統時間等內核數據。
l ??????? 從安全角度考慮,在內核中提供的服務相比用戶空間提供的毫無疑問更安全,很難被非法訪問到。
l ??????? 從效率考慮,在內核實現服務避免了和用戶空間來回傳遞數據以及保護現場等步驟,因此效率往往要比在用戶空間實現高許多。比如,httpd等服務。
l ??????? 如果內核和用戶空間都需要使用該服務,那么最好實現在內核空間,比如隨機數產生。
?? 理解上述道理對掌握系統調用的本質意義很大,希望網友們能從使用中多總結,多思考。
3 系統調用、用戶編程接口(API)、系統命令和內核函數的關系 ?
系統調用并非直接和程序員或系統管理員打交道,它僅僅是一個通過軟中斷機制向內核提交請求,獲取內核服務的接口。而在實際使用中程序員調用的多是用戶編程接口——API,而管理員使用的則多是系統命令。
用戶編程接口其實是一個函數定義,說明了如何獲得一個給定的服務,比如read()、malloc()、free()、abs()等。它有可能和系統調用形式上一致,比如read()接口就和read系統調用對應,但這種對應并非一一對應,往往會出現幾種不同的API內部用到同一個系統調用,比如malloc()、free()內部利用brk( )系統調用來擴大或縮小進程的堆;或一個API利用了好幾個系統調用組合完成服務。更有些API甚至不需要任何系統調用——因為它并不是必需要使用內核服務,如計算整數絕對值的abs()接口。
另外要補充的是Linux的用戶編程接口遵循了在Unix世界中最流行的應用編程界面標準——POSIX標準,這套標準定義了一系列API。在Linux中(Unix也如此),這些API主要是通過C庫(libc)實現的,它除了定義的一些標準的C函數外,一個很重要的任務就是提供了一套封裝例程(wrapper routine)將系統調用在用戶空間包裝后供用戶編程使用。
?
下一個需要解釋一下的問題是內核函數和系統調用的關系。大家不要把內核函數想像的過于復雜,其實它們和普通函數很像,只不過在內核實現,因此要滿足一些內核編程的要求。系統調用是一層用戶進入內核的接口,它本身并非內核函數,進入內核后,不同的系統調用會找到對應到各自的內核函數——換個專業說法就叫:系統調用服務例程。實際上針對請求提供服務的是內核函數而非調用接口。
??? 比如系統調用 getpid實際上就是調用內核函數sys_getpid。
asmlinkage long sys_getpid(void)
{
?????? return current->tpid;
}
Linux系統中存在許多內核函數,有些是內核文件中自己使用的,有些則是可以export出來供內核其他部分共同使用的,具體情況自己決定。
內核公開的內核函數——export出來的——可以使用命令ksyms 或 cat /proc/ksyms來查看。另外,網上還有一本歸納分類內核函數的書叫作《The Linux Kernel API Book》,有興趣的讀者可以去看看。
??? 總而言之,從用戶角度向內核看,依次是系統命令、編程接口、系統調用和內核函數。在講述了系統調用實現后,我們會回過頭來看看整個執行路徑。
4系統調用的實現 Linux 中實現系統調用利用了0x86體系結構中的軟件中斷[4]。軟件中斷和我們常說的中斷(硬件中斷)不同之處在于——它是通過軟件指令觸發而并非外設引發的中斷,也就是說,又是編程人員開發出的一種異常,具體的講就是調用int $0x80匯編指令,這條匯編指令將產生向量為128的編程異常。
之所以系統調用需要借助異常來實現,是因為當用戶態的進程調用一個系統調用時,CPU便被切換到內核態執行內核函數[5],而我們在i386體系結構部分已經講述過了進入內核——進入高特權級別——必須經過系統的門機制,這里的異常實際上就是通過系統門陷入內核(除了int 0x80外用戶空間還可以通過int3——向量3、into——向量4 、bound——向量5等異常指令進入內核,而其他異常無法被用戶空間程序利用,都是由系統使用的)。
我們更詳細地解釋一下這個過程。int $0x80指令的目的是產生一個編號為128的編程異常,這個編程異常對應的是中斷描述符表IDT中的第128項——也就是對應的系統門描述符。門描述符中含有一個預設的內核空間地址,它指向了系統調用處理程序:system_call()(別和系統調用服務程序混淆,這個程序在entry.S文件中用匯編語言編寫)。
很顯然,所有的系統調用都會統一地轉到這個地址,但Linux一共有2、3百個系統調用都從這里進入內核后又該如何派發到它們到各自的服務程序去呢?別發昏,解決這個問題的方法非常簡單:首先Linux為每個系統調用都進行了編號(0—NR_syscall),同時在內核中保存了一張系統調用表,該表中保存了系統調用編號和其對應的服務例程,因此在系統調入通過系統門陷入內核前,需要把系統調用號一并傳入內核,在x86上,這個傳遞動作是通過在執行int0x80前把調用號裝入eax寄存器實現的。這樣系統調用處理程序一旦運行,就可以從eax中得到數據,然后再去系統調用表中尋找相應服務例程了。
除了需要傳遞系統調用號以外,許多系統調用還需要傳遞一些參數到內核,比如sys_write(unsigned int fd, const char * buf, size_t count)調用就需要傳遞文件描述符fd、要寫入的內容buf、以及寫入字節數count等幾個內容到內核。碰到這種情況,Linux會有6個寄存器可被用來傳遞這些參數:eax (存放系統調用號)、 ebx、ecx、edx、esi及edi來存放這些額外的參數(以字母遞增的順序)。具體做法是在system_call( )中使用SAVE_ALL宏把這些寄存器的值保存在內核態堆棧中.
轉自: http://hi.baidu.com/ahunspun/blog/item/4620954be9e483f783025c06.html
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
