本文內(nèi)容概要: 1、linux結(jié)構(gòu)圖; 2、系統(tǒng)調(diào)用和庫函數(shù)概述; 3、基于int的linux的系統(tǒng)調(diào)用的具體實(shí)現(xiàn); 4、為什么需要系統(tǒng)調(diào)用; 5、系統(tǒng)調(diào)用和庫函數(shù)的關(guān)系。
引入: 前段時(shí)間在牛客網(wǎng)站上刷題時(shí),看到這樣一道題目,當(dāng)時(shí)也是不知道該怎么做。之后去查閱了資料,才知道原來是考查系統(tǒng)調(diào)用和庫函數(shù),這里先貼出原題。
正確答案是C,因?yàn)镃選項(xiàng)是系統(tǒng)調(diào)用,其他選項(xiàng)都是庫函數(shù)。 下邊就來簡述系統(tǒng)調(diào)用和庫函數(shù),以及Linux下系統(tǒng)調(diào)用是如何實(shí)現(xiàn)的。
1.linux結(jié)構(gòu)圖:
這張圖片來自于以下文章,http://blog.csdn.net/lf_2016/article/details/54587020感謝作者~~
2.系統(tǒng)調(diào)用與庫函數(shù)概述: 系統(tǒng)調(diào)用是應(yīng)用程序(庫函數(shù)也是應(yīng)用程序的一部分)與操作系統(tǒng)內(nèi)核之間的接口(對(duì)于windows來講,它并不是與應(yīng)用程序的最終接口,API才是最終接口),它決定了應(yīng)用程序是如何與內(nèi)核打交道的。無論程序是直接進(jìn)行系統(tǒng)調(diào)用還是通過運(yùn)行庫,則最終還是會(huì)達(dá)到系統(tǒng)調(diào)用這個(gè)層面上。(下文將會(huì)具體分析) 系統(tǒng)調(diào)用既然是應(yīng)用程序與操作系統(tǒng)之間的接口,那么所有的應(yīng)用程序都必須依賴于系統(tǒng)調(diào)用,所以系統(tǒng)調(diào)用必須有嚴(yán)格而又明確的定義,并且具有穩(wěn)定性和向后兼容性(所謂的向后兼容性就是隨著系統(tǒng)的更新,之前舊的系統(tǒng)調(diào)用仍然可以使用,只是需要增加新的接口)。 庫函數(shù)是為了方便人們編寫應(yīng)用程序而引出的。比如C語言寫的第一個(gè)程序hello world,就是調(diào)用庫函數(shù)中的PRintf()函數(shù)完成輸出的。 很多操作系統(tǒng)是以系統(tǒng)調(diào)用作為應(yīng)用程序的最底層的,而windows的最底層接口是windows API。盡管windows的內(nèi)核提供了數(shù)百個(gè)系統(tǒng)調(diào)用(windows下又把系統(tǒng)調(diào)用稱為系統(tǒng)服務(wù)),但是出于種種原因,windows并沒有將系統(tǒng)調(diào)用公開,而是在系統(tǒng)調(diào)用之上,建立了這樣一個(gè)API層,讓應(yīng)用程序只能調(diào)用API層的函數(shù),而不是如Linux這樣的操作系統(tǒng)直接使用系統(tǒng)調(diào)用。
3.基于int的linux的系統(tǒng)調(diào)用的具體實(shí)現(xiàn): 系統(tǒng)調(diào)用是屬于操作系統(tǒng)內(nèi)核的一部分的,必須以某種方式提供給進(jìn)程讓它們?nèi)フ{(diào)用。CPU可以在不同的特權(quán)級(jí)別下運(yùn)行,而相應(yīng)的操作系統(tǒng)也有不同的運(yùn)行級(jí)別,用戶態(tài)和內(nèi)核態(tài)。 首先,我們得知道用戶態(tài)和內(nèi)核態(tài)的區(qū)別,參考文(http://blog.csdn.net/xieyutian1990/article/details/38413413)。
運(yùn)行在內(nèi)核態(tài)的進(jìn)程可以毫無限制的訪問各種資源,而在用戶態(tài)下的用戶進(jìn)程的各種操作都有著限制,比如不能隨意的訪問內(nèi)存、不能開閉中斷以及切換運(yùn)行的特權(quán)級(jí)別。顯然,屬于內(nèi)核的系統(tǒng)調(diào)用一定是運(yùn)行在內(nèi)核態(tài)下,但是如何切換到內(nèi)核態(tài)呢? 操作系統(tǒng)一般是通過中斷從用戶態(tài)切換到內(nèi)核態(tài)。 學(xué)習(xí)過計(jì)算機(jī)組成原理這類課程之后,我們都知道中斷是有兩個(gè)屬性,一個(gè)是中斷號(hào)(從0開始編號(hào)),一個(gè)是中斷處理程序。通常,中斷是有兩種類型,一種是硬件中斷(一般來源于硬件的異常或者其他事件的發(fā)生,比如鍵盤被按下),一種是軟件中斷(通常是一條指令,i386下是int指令),通常有一個(gè)參數(shù),記錄中斷號(hào)。在i386下,windows的絕大多數(shù)的系統(tǒng)調(diào)用都是由int 0x2e來觸發(fā),而Linux則是使用int 0x80來觸發(fā)所有的系統(tǒng)調(diào)用。 在Linux內(nèi)核版本2.6.19版本一共提供了319個(gè)系統(tǒng)調(diào)用。EAX寄存器是用來存儲(chǔ)系統(tǒng)調(diào)用的接口號(hào)。 EAX = 1,表示退出進(jìn)程exit;EAX=2,表示創(chuàng)建進(jìn)程fork,等等。 Linux的系統(tǒng)中斷流程:

細(xì)節(jié)實(shí)現(xiàn): 1>觸發(fā)中斷: Linux下的系統(tǒng)調(diào)用是通過 宏函數(shù)來定義的。比如_syscall0是用于定義一個(gè)沒有參數(shù)的系統(tǒng)調(diào)用的封裝,定義在include /asm-i386/unistd.h中,總共有 7個(gè)宏函數(shù)。 Linux下支持的系統(tǒng)調(diào)用的參數(shù)至多有6個(gè),用6個(gè)寄存器來傳遞,分別是EBX,ECX,EDX,ESI,EDI,EBP。使用的時(shí)候是依次使用,也就是說系統(tǒng)調(diào)用有一個(gè)參數(shù)的時(shí)候,使用寄存器EBX,而不是其他。 以上是系統(tǒng)調(diào)用的一種方式,那么另一種方式是什么呢?應(yīng)用程序調(diào)用C庫函數(shù),庫函數(shù)底層調(diào)用的是系統(tǒng)調(diào)用,就如下圖所示(引自http://blog.csdn.net/skyflying2012/article/details/10044343) 
2>切換堆棧: 在學(xué)習(xí)C語言的中后期,我們大概掌握了一些基本知識(shí),就開始想:為什么點(diǎn)擊運(yùn)行按鈕程序就會(huì)跑起來?編譯器在背后都干了哪些事,函數(shù)調(diào)用是怎么實(shí)現(xiàn)的等等問題。我們知道,函數(shù)的調(diào)用其實(shí)也就是利用中斷的,借助于堆棧來保存函數(shù)的當(dāng)前信息,當(dāng)調(diào)用函數(shù)執(zhí)行完成之后,就會(huì)繼續(xù)執(zhí)行當(dāng)前函數(shù)。同樣,系統(tǒng)調(diào)用也是有自己的調(diào)用堆棧,內(nèi)核態(tài)有內(nèi)核棧,用戶態(tài)有用戶棧,當(dāng)要觸發(fā)系統(tǒng)調(diào)用的時(shí)候,程序的執(zhí)行流是從用戶態(tài)到內(nèi)核態(tài),這時(shí),程序的當(dāng)前棧也是從用戶棧切換到內(nèi)核棧,當(dāng)系統(tǒng)調(diào)用執(zhí)行完成之后,又會(huì)從內(nèi)核棧返回到用戶棧。 總結(jié)堆棧的切換: 當(dāng)系統(tǒng)調(diào)用的中斷被觸發(fā)的時(shí)候,CPU會(huì)切換到內(nèi)核態(tài),找到當(dāng)前進(jìn)程的內(nèi)核棧,并在當(dāng)前進(jìn)程的內(nèi)核棧中寫入用戶棧的信息,包括,SS(當(dāng)前棧所在的頁),ESP等信息;當(dāng)系統(tǒng)調(diào)用執(zhí)行完成之后,CPU會(huì)調(diào)用iret指令切換到用戶態(tài) ,iret指令會(huì)從內(nèi)核棧中讀取用戶棧的信息,進(jìn)行返回。
3>中斷處理程序: 從上邊的一個(gè)圖(Linux系統(tǒng)中斷流程),我們可以看出,當(dāng)觸發(fā)中斷之后,系統(tǒng)會(huì)查詢中斷向量表,得知,0x80觸發(fā)的是系統(tǒng)調(diào)用,然后從EAX寄存器中得到系統(tǒng)調(diào)用編號(hào),去執(zhí)行相應(yīng)的系統(tǒng)調(diào)用。這里也有點(diǎn)類似于函數(shù)的調(diào)用過程。
4.為什么需要系統(tǒng)調(diào)用? (1)系統(tǒng)調(diào)用可以為用戶控件提供訪問硬件資源的統(tǒng)一接口,以至于應(yīng)用程序不必去關(guān)注具體的硬件訪問的操作。比如:fopen函數(shù),使用的時(shí)候用戶就不需要去管底層的尋道找道得到原理等等。再說,就是計(jì)算機(jī)的硬件資源是有限的,不能做到多個(gè)進(jìn)程同時(shí)訪問硬件資源,所以需要系統(tǒng)調(diào)用來控制。 (2)可以對(duì)系統(tǒng)進(jìn)行保護(hù),保證系統(tǒng)的穩(wěn)定和安全。也就是說,用戶訪問內(nèi)核的路徑事先是已經(jīng)被規(guī)定好的。
5.系統(tǒng)調(diào)用和C庫函數(shù)的關(guān)系: 并不是一一對(duì)應(yīng),幾個(gè)庫函數(shù)對(duì)應(yīng)一個(gè)系統(tǒng)調(diào)用(malloc()和free()對(duì)應(yīng)的是brk系統(tǒng)調(diào)用)。也有一個(gè)庫函數(shù)對(duì)應(yīng)一個(gè)系統(tǒng)調(diào)用(open()函數(shù)對(duì)應(yīng)open系統(tǒng)調(diào)用)。也有些庫函數(shù)不需要系統(tǒng)調(diào)用(比如strcpy函數(shù),atoi函數(shù))。
【總結(jié) 】 (1)系統(tǒng)調(diào)用是應(yīng)用程序與操作系統(tǒng)內(nèi)核的接口,但是不是最終接口。windows下API才是最終接口,因?yàn)閣indows下的系統(tǒng)調(diào)用不公開。 (2)系統(tǒng)調(diào)用是通過中斷來觸發(fā)的,Linux下是0x80來觸發(fā)。 (3)系統(tǒng)調(diào)用最多可以有6個(gè)參數(shù),放在6個(gè)寄存器EBX,ECX,EDX,ESI,EDI,EBP中,但是系統(tǒng)調(diào)用號(hào)是放在EAX中。 (4)觸發(fā)系統(tǒng)調(diào)用的時(shí)候,CPU會(huì)從用戶態(tài)切換到內(nèi)核態(tài),程序的當(dāng)前棧也會(huì)從用戶棧切換到內(nèi)核棧,系統(tǒng)調(diào)用執(zhí)行完成之后執(zhí)行相反的操作。 (5)操作系統(tǒng)包括內(nèi)核和其他程序,內(nèi)核中包括進(jìn)程管理,進(jìn)程調(diào)度等等,其他程序包括函數(shù)庫等等。所以我們可以認(rèn)為內(nèi)核約等于操作系統(tǒng)。
參考書籍: 《程序員的自我修養(yǎng)—-鏈接、裝載、庫》俞甲子 石凡 潘愛民 著
本人小白,如有問題,請(qǐng)不吝指出~~如果感興趣的讀者,可以自行去閱讀《程序員的自我修養(yǎng)》一書的第12章節(jié)。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注