一 使用者空間和核心空間
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的格言:“提供機制而不是策略”。
添加系統調用要謹慎!