淺談C語言函數調用與系統調用

1. 函數調用和系統調用概述

1.1 定性的去區分函數調用和系統調用

很多初學C語言的同學,亦或者開發中很少接觸系統底層的同學可能會認爲函數調用以及系統調用是一回事。因爲在應用程序,兩者都被抽象成接口去給應用程序調用。其實函數調用和系統調用還是有區別,我們通過下圖先有個全局的瞭解!

圖1-1 系統功能模塊關聯圖

 

從圖1-1 我們可以知道應用程序訪問內核,主要通過兩種方式:中斷和系統調用接口。

其一,中斷是置於程序流程之外的,所以這種方式並不是我們訪問內核的普遍方式,更多的是因爲程序異常而引起的中斷;

其二,就是系統調用接口,例如使用 open、read 以及 write 去操作文件數據(相當於去訪問硬盤)。

而函數調用是無法直接訪問內核的,函數調用需要訪問內核,也只能通過系統調用的方式。

所以,我們可以通過這種方式定性地去區分函數調用和系統調用: 接口直接訪問內核的屬於系統調用,其他則爲函數調用

1.2 爲什麼要封裝好系統調用

我們知道,32位 linux系統爲了保證程序獨立性,給每一個進程分配獨立的4G地址空間(虛擬內存空間),但是4G地址空間又被劃分爲兩個空間:用戶空間以及內核空間,如下圖:

圖1-2 進程地址空間劃分圖

用戶空間,存儲的是一些進程常見的數據,例如代碼、變量數據、堆區和棧區等,需要注意的是內核空間,雖然說系統給每一個進程都分配1G大小的內核空間,但是實際上,內核空間是操作系統中進程公用的。公用就會帶來一個現實性的問題,就比如我們可以在自己的房間幹任何事情(用戶空間),去不能在公共場合(內核空間)爲所欲爲,活動在公共場合(內核空間),我們的行爲會受到限制,對於操作系統也一樣道理,內核空間屬於進程公用,進程去訪問內核必須保證不會給其他進程造成影響。

如何去保證進程操作系統中公用的內核空間不會出現非法操作,打個比方說:如何保證銀行內部數據不被非法操作,銀行是提供一個ATM給你,你只能選擇上面的接口操作。操作系統也“學習”了這樣的模式,封裝接口,保證數據操作正確性!

不僅僅應用程序無法直接訪問內核空間,只能通過系統調用訪問內核空間,而且內核對用戶空間的數據的不信任,對於通過系統調用的傳遞過來的數據也會做相應的檢查,確保內核安全,確保內核安全是必須的,內核屬於系統的軟件核心,內核崩潰會導致整個軟件系統的崩潰。爲了保證應用程序傳遞的數據合法性以及內核數據的絕密性,通常會使用copy_from_user去檢驗和拷貝傳遞過來的數據,使用copy_to_user()檢驗和拷貝數據給應用程序。

2.函數調用

2.1 什麼叫做函數調用

int add(int a, int b){

    return(a + b);
}

int main(){

    int sum;
    
    sum = add(1 + 2);
    
    return 0;
}

如上是一個最簡單的函數調用過程,瞭解什麼叫函數調用,我們可以通過了解什麼是函數?

函數(function)是完成特定任務的獨立程序代碼單元。

                                                                                                                                                     ----《C Primer Plus》

再來了解爲啥需要使用函數?

首先,使用函數可以省去編寫重複代碼的苦差。如果程序多次完成某一項。如果程序需要多次完成某項任務,那麼只需編寫一個適合的函數,就可以在需要時使用這個函數,或者在不同的程序中使用該函數,就像許多程序使用putchar() 一樣。

其次,即使程序只完成某項任務一次,也值得使用函數。因爲函數使程序更加模塊化,從而提高了程序代碼的可讀性,更方便後期修改、完善。

                                                                                                                                                     ----《C Primer Plus》

函數調用是很好理解的,提高代碼重用性以及模塊性,調用相對應函數,得到某種結果。

2.1 函數調用的過程

函數調用會發生函數壓棧以及出棧過程,具體可以參考如下博文和視頻(博主對彙編不熟悉,所以就不具體分析了,以免誤人子弟!)。

淺析函數的調用過程

彙編中的函數調用中棧的工作過程

3.系統調用

3.1 什麼叫做系統調用

爲了和用戶空間上運行的進程進行交互,內核提供了一組接口。透過該接口,應用程序可以訪問硬件設備和其他操作系統資源。這組接口在應用程序和內核之間扮演了使者的角色,應用程序發送各種請求,而內核負責滿足這些請求(或者讓應用程序暫時擱置)

                                                                                                                                         ----《Linux內核設計與實現》

3.2 系統調用的過程

系統調用和函數調用在應用程使用幾乎沒有差別,但是內部實現過程是完全不同的,函數調用是通過函數入口地址直接跳轉函數服務程序,而系統調用並非直接跳轉系統調用服務程序,因爲內核地址對於應用程序是不可用的,需要通過中斷的方式進入內核態,再通過一系列的操作找到對應的服務程序。

我們可以通過下圖有一個瞭解:

圖3-1 系統調用過程流程圖

希望更詳細的瞭解系統調用過程可以看看下面這篇博文:

ioctl系統調用過程(深入Linux(ARM)內核源碼)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章