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

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