一、前言
為什么用戶程序不能直接訪問系統內核模式提供的服務?
在linux中,將程序的運行空間分為內核空間與用戶空間(內核態和用戶態),在邏輯上它們之間是相互隔離的,因此用戶程序不能訪問內核數據,也無法使用內核函數。當用戶進程必須訪問內核或使用某個內核函數時,就得使用系統調用(System Call)。在Linux中,系統調用是用戶空間訪問內核空間的唯一途徑。
什么是系統調用?
答:系統調用就是一種特殊的接口。通過這個接口,用戶可以訪問內核空間。系統調用規定了用戶進程進入內核的具體位置。系統調用是用戶進程進入內核的接口層,它本身并非內核函數,但它是由內核函數實現,進入內核后,不同的系統調用會找到各自對應的內核函數,這些內核函數被稱為系統調用的“服務例程”。比如系統調用getpid實際調用了服務例程為sys_getpid(),或者說系統調用getpid是服務例程sys_getpid()的“封裝例程”。
具體步驟:用戶進程-->系統調用-->內核-->返回用戶空間。
系統調用就是為了解決上述問題而引入的,是提供給用戶的“特殊接口”,系統調用規定用戶進程進入內核空間的具體位置。
1.程序運行空間從用戶空間進入內核空間。
2.處理完后再返回用戶空間。
什么是API
答:應用程序接口API(Application Programming Interface) ,是程序員在用戶空間下可以直接使用的函數接口。是一些預定義的函數,比如常用的read()、malloc()、free()、abs()函數等,這些函數都具有一定功能,說明了如何獲得一個給定的服務,跟內核沒有必然的聯系。提供應用程序與開發人員基于某軟件或硬件的以訪問一組例程的能力,而又無需訪問源碼,或理解內部工作機制的細節。
兩者的區別
答:區別:api是函數的定義,規定了這個函數的功能,跟內核無直接關系。而系統調用是通過中斷向內核發請求,實現內核提供的某些服務。
聯系:程序員調用的是API(API函數),然后通過與系統調用共同完成函數的功能。 因此,API是一個提供給應用程序的接口,一組函數,是與程序員進行直接交互的。系統調用則不與程序員進行交互的,它是根據API函數,通過一個軟中斷機制向內核提交請求,以獲取內核服務的接口。
有時候,某些API所提供的功能會涉及到與內核空間進行交互。那么,這類API內部會封裝系統調用。而不涉及與內核進行交互的API則不會封裝系統調用。也就是說,API和系統調用并沒有嚴格的一一對應關系,一個API可能恰好只對應一個系統調用,比如read()系統調用和read();一個API也可能由多個系統調用實現;有時候,一個API的功能可能并不需要內核提供的服務,那么此時這個API也就不需要任何的系統調用,比如abs()。另外,一個系統調用可能還被多個API內部調用。
對于編程者來說,系統調用和API都是一組函數,并無什么兩樣,二者關注的都是函數名、參數類型及返回值的含義;但是事實上,系統調用的實現是在內核完成的,API則是在函數庫中實現的。
例如: 在內核中實現了write系統調用。
AI寫代碼
在庫函數中通過宏定義的方式實現write API。可以在unistd.h中看到write到系統調用的轉換。
API是用戶程序直接可以使用的函數接口,但如果每個操作系統都擁有只屬于自己的API,那么應用程序的移植性將會很差。基于POSIX(Portable Operating System Interface)標準的API擁有很好的可移植性,它定義了一套POSIX兼容標準,這使得按這個標準實現的API可以在各種版本的UNIX中使用。現如今,它也可以在除UNIX之外的操作系統中使用,比如Linux,Windows NT等。
二、系統調用
操作系統負責管理和分配所有的計算機資源。為了更好地服務于應用程序,操作系統提供了一組特殊接口———系統調用。用戶程序可以通過這組特殊的接口來獲取操作系統內核提供的各種功能,如分配內存、創建進程、實現進程之間的通信等。
系統調用(system call) 其實是 Linux 內核提供給應用層的應用編程接口(API) , 是 Linux 應用層進入內核的入口。不止 Linux 系統,所有的操作系統都會向應用層提供系統調用,應用程序通過系統調用來使用操作系統提供的各種服務。
通過系統調用, Linux 應用程序可以請求內核以自己的名義執行某些事情,譬如打開磁盤中的文件、讀寫文件、關閉文件以及控制其它硬件外設。
通過系統調用 API,應用層可以實現與內核的交互,其關系可通過下圖簡單描述:
內核提供了一系列的服務、資源、支持一系列功能,應用程序通過調用系統調用 API 函數來使用內核提供的服務、資源以及各種各樣的功能, 如果大家接觸過其它操作系統編程,想必對此并不陌生,譬如Windows 應用編程,操作系統內核一般都會向應用程序提供應用編程接口 API,否則我們將我無法使用操作系統。
為什么用戶程序不能直接訪問系統內核提供的服務呢?答案是不安全。
單片機開發中,由于不需要操作系統,只需要知道寄存器的地址,代碼通過指針直接操作硬件,所以開發人員可以編寫代碼直接訪問硬件。硬件>>>>>>代碼直接在內存中運行操作硬件。
而在嵌入式系統中通常都要運行操作系統,程序訪問資源的方式就發生了改變。操作系統基本上都支持多任務,即同時可以運行多個程序。如果允許程序直接訪問系統資源,肯定會帶來很多問題。因此,所有軟硬件資源的管理和分配都由操作系統負責。程序要獲取資源(如分配內存、讀寫串口)必須通過操作系統來完成,即用戶程序向操作系統發出服務請求,操作系統收到請求后執行相關的代碼來處理。硬件>>>>>>>操作系統>>>>>>>代碼
還有更重要的一點是,在Linux中,為了更好地保護內核空間,將程序的運行空間分為用戶空間和內核空間(也就是常稱的用戶態和內核態),它們分別運行在不同的級別上,邏輯上是相互分離的。因此,用戶進程通常情況下不允許訪問內核數據,也無法使用內核函數,它們只能在用戶空間操作用戶數據,調用用戶空間的函數。但是,在有些情況下,用戶空間的進程需要獲得內核的系統服務(調用內核空間程序),這時操作系統就必須利用系統提供用戶的特殊接口———”系統調用”規定用戶進程進入內核空間的具體位置。在進行系統調用時,程序運行空間需要從用戶空間進入內核空間,處理完成后再返回用戶空間。
用戶程序向操作系統提出請求的接口就是系統調用。所有的操作系統都會提供系統調用接口,只不過不同的操作系統提供的系統調用接口各不相同。Linux系統調用接口非常精簡(只有250個左右),它繼承了UNIX系統調用中最基本和最有用的部分。這些系統調用按照功能大致可分為進程控制、進程間通信、文件系統控制、存儲管理、網絡管理、套接字控制、用戶管理等。
應用編程與裸機編程、驅動編程有什么區別?
在學習應用編程之前,相信大家都有過軟件開發經驗,譬如 51、 STM32 等單片機軟件開發、 以及嵌入式 Linux 硬件平臺下的驅動開發等, 51、 STM32 這類單片機的軟件開發通常是裸機程序開發,并不會涉及到操作系統的概念,那應用編程與裸機編程以及驅動開發有什么區別呢?
就拿嵌入式 Linux 硬件平臺下的軟件開發來說,我們大可將編程分為三種,分別為裸機編程、 Linux 驅動編程以及 Linux 應用編程。首先對于裸機編程這個概念來說很好理解,一般把沒有操作系統支持的編程環境稱為裸機編程環境,譬如單片機上的編程開發,編寫直接在硬件上運行的程序,沒有操作系統支持; 狹義上 Linux 驅動編程指的是基于內核驅動框架開發驅動程序, 驅動開發工程師通過調用 Linux 內核提供的接口完成設備驅動的注冊, 驅動程序負責底層硬件操作相關邏輯, 如果學習過 Linux 驅動開發的讀者,想必對此并不陌生; 而 Linux 應用編程(系統編程)則指的是基于 Linux 操作系統的應用編程,在應用程序中通過調用系統調用 API 完成應用程序的功能和邏輯, 應用程序運行于操作系統之上。通常在操作系統下有兩種不同的狀態:內核態和用戶態,應用程序運行在用戶態、而內核則運行在內核態。
三、用戶編程接口(API)---C庫
前面提到利用系統調用接口程序可以訪問各種資源,但并不直接與程序員進行交互,它僅僅是一個通過軟中斷機制向內核提交請求以獲取內核服務的接口。但在實際開發中程序并不直接使用系統調用接口,而是使用用戶程序編程接口(API)。例如,創建進程的API函數fork()函數對應于內核空間的sys_fork()系統調用,但并不是所有的函數都對應一個系統調用。有時,一個API函數會需要幾個系統調用來共同完成函數的功能,甚至還有一些API函數不需要調用相應的系統調用(因此它所完成的不是內核提供的服務)。
前面給大家介紹了系統調用,系統調用是內核直接向應用層提供的應用編程接口, 譬如 open、 write、read、 close 等。編寫應用程序除了使用系統調用之外,我們還可以使用庫函數。
庫函數也就是 C 語言庫函數, C 語言庫是應用層使用的一套函數庫, 在 Linux 下,通常以動態(.so)庫文件的形式提供,存放在根文件系統/lib 目錄下, C 語言庫函數構建于系統調用之上,也就是說庫函數其實是由系統調用封裝而來的,當然也不能完全這么說, 原因在于有些庫函數并不調用任何系統調用,譬如一些字符串處理函數 strlen()、 strcat()、 memcpy()、 memset()、 strchr()等等; 而有些庫函數則會使用系統調用來幫它完成實際的操作,譬如庫函數 fopen 內部調用了系統調用 open()來幫它打開文件、庫函數 fread()就利用了系統調用 read()來完成讀文件操作、 fwrite()就利用了系統調用 write()來完成寫文件操作。
庫函數可以理解為是對系統調用的一層封裝。系統調用作為內核提供給用戶程序的接口,它的執行效率是比較高效而精簡的,但有時我們需要對獲取的信息進行更復雜的處理,或更人性化的需要,我們把這些處理過程封裝成一個函數再提供給程序員,更方便于程序猿編碼。庫函數有可能包含有一個系統調用,有可能有好幾個系統調用,當然也有可能沒有系統調用,比如有些操作不需要涉及內核的功能。可以參考下圖來理解庫函數與系統調用的關系。
Linux 系統內核提供了一系列的系統調用供應用層使用, 我們直接使用系統調用就可以了呀,那為何還要設計出庫函數呢?事實上, 有些系統調用使用起來并不是很方便, 于是就出現了 C 語言庫,這些 C 語言庫函數的設計是為了提供比底層系統調用更為方便、更為好用、 且更具有可移植性的調用接口。
來看一看它們之間的區別:
庫函數是屬于應用層,而系統調用是內核提供給應用層的編程接口,屬于系統內核的一部分;
庫函數運行在用戶空間,調用系統調用會由用戶空間(用戶態)陷入到內核空間(內核態);
庫函數通常是有緩存的,而系統調用是無緩存的,所以在性能、效率上,庫函數通常要優于系統調用;
可移植性:庫函數相比于系統調用具有更好的可移植性,通常對于不同的操作系統,其內核向應用層提供的系統調用往往都是不同,譬如系統調用的定義、功能、參數列表、返回值等往往都是不一樣的;而對于 C 語言庫函數來說,由于很多操作系統都實現了 C 語言庫, C 語言庫在不同的操作系統之間其接口定義幾乎是一樣的,所以庫函數在不同操作系統之間相比于系統調用具有更好的可移植性。
以上便上它們之間一個大致的區別, 從實現者的角度來看,系統調用與庫函數之間有根本的區別,但從用戶使用角度來看,其區別并不重要,它們都是 C 語言函數。 在實際應用編程中,庫函數和系統調用都會使用到,所以對于我們來說,直接把它們當做是 C 函數即可,知道你自己調用的函數是系統調用還是庫函數即可,不用太過于區分它們之間的差別,所以應用編程簡單點來說就是:開發 Linux 應用程序,通過調用內核提供的系統調用或使用 C 庫函數來開發具有相應功能的應用程序。
為什么不直接使用系統調用接口了。原因如下:
系統調用接口功能非常簡單,無法滿足程序的要求
不同操作系統的系統調用接口不兼容,程序移植時工作量大。
用戶程序編程接口通俗的解釋就是各種庫(最重要的就是C庫)中的函數。為了提高開發效率,C庫中實現了很多函數。這些函數實現了常用的功能,供程序員調用。這樣一來,程序員就不需要自己編寫這些代碼。直接調用庫函數就可以實現基本功能,提高了代碼的復用率。使用用戶程序編程接口好友一個好處就是:程序具有良好的可移植性。幾乎所有的操作系統上都實現了C庫,所以程序通常只需要重新編譯一下就可以在其它操作系統下運行。
在Linux中,用戶程序編程接口(API)遵循了UNIX中最流行的應用編程界面標準———POSIX標準。POSIX標準是由IEEE和ISO/IEC共同開發的標準系統,該標準基于當時的UNIX實踐和經驗,描述了操作系統的系統調用編程接口(實際上就是API),用于保證應用程序可以在源代碼一級上在多種操作系統上移植運行。這些系統調用編程接口主要是通過C庫(libc)實現的。
3.1系統命令
每一個系統命令其實就是一個可執行的程序,這些可執行程序的實現調用了某些系統調用。并且,這些可執行程序又分為普通用戶可使用的命令和管理員可使用的命令。根據上述分類,普通用戶可用的命令和管理可用的命令分別被存放于/bin和/sbin目錄下。
系統命令相對于API更高了一層,它實際上是一個可執行程序,它的內部引用了用戶編程接口(API)來實現相應的功能,它們之間的關系如下圖:
3.2內核函數
內核函數和用戶空間中函數并無兩樣,只不過內核函數是在內核中實現。雖然系統調用是用戶進程進入內核的唯一途徑,但是系統調用函數內部并不真正實現其功能,而是通過對內核函數的封裝。也就是說,用戶程序通過某個系統調用進入內核后,會接著去執行這個系統調用對應的內核函數。這個內核函數也稱為系統調用的服務例程。
由于內核函數是在內核中實現的,因此它必須符合內核編程的規則,比如函數名以sys_開始,函數定義時候需加asmlinkage標識符等。
四、標準C程序的實例
首先,C程序調用API函數printf;
printf 函數的調用引發了對應的系統調用write的執行;
write執行結束時的返回值傳遞回用戶程序;
4.1操作系統的模式
(1)雙重模式(DUAL MODE)
現代計算機系統有一個特殊的硬件,用于劃分系統的運行狀態,至少需要兩種單獨運行模式:
用戶模式(user mode):執行用戶代碼
內核模式(kernel mode):執行操作系統代碼
目的:確保操作系統正確的運行
實現方式:用一個硬件模式位來表示當前模式:0表示內核模式,1表示用戶模式
(2)運行模式的切換
程序執行流剛開始在“用戶模式”運行,當發生系統調用的時候,需要從“用戶模式”切換到“內核模式”運行。
這種模式切換的機制稱為:陷阱(Trap)機制。
4.2系統調用的實現機制
如遇任何疑問或有進一步的需求,請隨時與我私信或者評論聯系。
版權聲明:本文為CSDN博主「xie13307907056」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/xie13307907056/article/details/154171834
-
Linux
+關注
關注
88文章
11758瀏覽量
219009 -
操作系統
+關注
關注
37文章
7401瀏覽量
129282
發布評論請先 登錄
國產工業操作系統選型指南:硬實時、功能安全與生態怎么選
操作系統體系結構
操作系統運行機制
什么是嵌入式操作系統?
EV10AS180A模數轉換器支持哪些操作系統
單片機的操作系統
深入了解系統調用API:探索操作系統底層的關鍵接口
樹莓派操作系統:版本、特性及設置完整指南!
深度智能 基座躍遷 鴻道Intewell,面向“AI+智造”的新型工業操作系統
揭秘LuatOS:實時操作系統RTOS核心庫的關鍵技術剖析!
鴻道Intewell操作系統:人形機器人底層操作系統
基于LuatOS核心庫的實時操作系統開發:從理論到實踐~
理想汽車智駕操作系統解讀
KaihongOS操作系統:導入接口模塊介紹
模型原生操作系統:機遇、挑戰與展望 CCCF精選
探索操作系統底層的關鍵接口
評論