一 用戶空間和內核空間
Linux內核將這4G字節虛擬地址空間的空間分爲兩部分:
l 將最高的1G字節(從虛擬地址0xC0000000到0xFFFFFFFF),供內核使用,稱爲“內核空間”。
l 將較低的3G字節(從虛擬地址 0x00000000到0xBFFFFFFF),供各個進程使用,稱爲“用戶空間)。
因爲每個進程可以通過系統調用進入內核,因此Linux內核由系統內的所有進程共享。於是從具體進程的角度來看,
每個進程可以擁有4G字節的虛擬空間。如此劃分提供對系統內核安全保護機制。
二 系統調用
用戶空間的進程和內核空間程序如何進行交互?——系統調用
l 爲用戶空間提供統一的抽象接口;
l 保證系統的安全訪問和穩定;
l 控制進程用戶空間與內核空間的切換;
1 系統調用的層次關係
Linux內部體系結構:
圖片來自:http://blog.chinaunix.net/uid-26838492-id-3162146.html
系統調用過程如下:
Unix系統設計理念:提供機制而不是策略
將編程問題分成兩個部分:機制(Mechanism)和策略(Policy)。對外應用程序提供接口(系統調用API),
而不用去關心如何實現——機制;真正的實現在系統內部,系統提供實現接口算法而不關心如何使用——策略。
2 系統調用程序執行
通知內核的機制是靠軟中斷實現的:
通過引發一個異常來促使系統切換到內核態去執行異常處理程序。此時的異常處理程序實際上就是系統調用處理程序。
通過異常陷入到內核中,如何執行相應的系統調用:
在x86上, 系統調用號是通過eax寄存器傳遞給內核的。在陷入內核之前,用戶空間就把相應系統調用所對應的號放入eax中了。
這樣系統調用處理程序一旦運行,就可以從eax中得到數據。
call *sys_call_table(, %eax, 4)
由於系統調用表中的表項是以32位(4字節)類型存放的,所以內核需要將給定的系統調用號乘以4,然後用所得的結果在該表中查詢其位置。
通過異常陷入到內核中,如何傳遞參數給系統調用以及回傳給用戶空間:
把這些參數也存放在寄存器裏。在x86系統上,ebx、ecx、edx、esi和edi按照順序存放前五個參數。需要六個或六個以上參數的情況不多見,
此時,應該用一個單獨的寄存器存放指向所有這些參數在用戶空間地址的指針。給用戶空間的返回值也通過寄存器傳遞。在x86系統上,它存放在
eax寄存器中。
3 系統調用的實現
一個Linux的系統調用在實現時並不需要太關心它和系統調用處理程序之間的關係。給Linux添加一個新的系統調用是件相對容易的工作。
怎樣設計和實現一個系統調用是難題所在,而把它加到內核裏卻無須太多周折。
實現一個新的系統調用的第一步是決定它的用途。它要做些什麼:
每個系統調用都應該有一個明確的用途。在Linux中不提倡採用多用途的系統調用(一個系統調用通過傳遞不同的參數值來選擇完成不同的工作)。
ioctl()就應該被視爲一個反例。
新系統調用的參數、返回值和錯誤碼又該是什麼:
系統調用的接口應該力求簡潔,參數儘可能少。系統調用的語義和行爲非常關鍵;因爲應用程序依賴於它們,所以它們應力求穩定,不做改動。
設計接口的時候要儘量爲將來多做考慮。你是不是對函數做了不必要的限制:
系統調用設計得越通用越好。不要假設這個系統調用現在怎麼用將來也一定就是這麼用。系統調用的目的可能不變,
但它的用法卻可能改變。這個系統調用可移植嗎?別對機器的字節長度和字節序做假設。記住Unix的格言:“提供機制而不是策略”。
添加系統調用要謹慎!