什么是系統(tǒng)調(diào)用?

在探討系統(tǒng)調(diào)用(system call)時,我們首先想到的可能是軟中斷、內(nèi)核態(tài)和用戶態(tài)。讓我們從頭開始,重新理解“系統(tǒng)調(diào)用”這個概念。

實際上,系統(tǒng)調(diào)用這個術語有兩種解釋。一些資料將open、read、write、fork等man手冊第二頁的函數(shù)稱為系統(tǒng)調(diào)用,但實際上這些只是真正系統(tǒng)調(diào)用的封裝函數(shù)(wrapper functions),我稱之為廣義上的系統(tǒng)調(diào)用。這些封裝函數(shù)通過軟中斷陷入內(nèi)核態(tài),然后調(diào)用對應的真正的系統(tǒng)調(diào)用,通常是一一對應的。例如,fork函數(shù)內(nèi)部會調(diào)用sys_fork,而后者才是嚴格意義上的系統(tǒng)調(diào)用,由內(nèi)核提供的服務,我稱之為狹義的系統(tǒng)調(diào)用。

系統(tǒng)調(diào)用的封裝函數(shù)是由glibc實現(xiàn)的,而真正的系統(tǒng)調(diào)用是由內(nèi)核實現(xiàn)的。你可能覺得這有點復雜,讓我們看一下epoll的man手冊,其末尾有一個版本聲明:

翻譯過來就是,linux內(nèi)核的2.5.44版本引入了epoll,而glibc的2.3.2版本開始支持epoll。

這里特別提到glibc的版本,是為了說明即使你的Linux內(nèi)核版本支持epoll系統(tǒng)調(diào)用,但如果你的glibc版本不夠,你仍然無法直接使用以epoll開頭的那些函數(shù)。為什么需要glibc封裝一層呢?主要是因為系統(tǒng)調(diào)用在實際調(diào)用時涉及到一些匯編指令(后文會詳細介紹)。

盡管我已經(jīng)解釋了系統(tǒng)調(diào)用的兩種理解,但還有一點需要注意:(廣義上的)系統(tǒng)調(diào)用的具體實現(xiàn)與內(nèi)核架構有關。我上述描述的過程主要基于Linux,但縱觀整個unix-like操作系統(tǒng)家族,實際上還有不同的聲音。這就引出了單內(nèi)核與微內(nèi)核之爭。

單內(nèi)核與微內(nèi)核的爭論始于1992年,由國外的譚寧邦教授(Tanenbaum)和Linux的發(fā)明者林納斯(Linus)展開。當時林納斯還是一個剛嶄露頭角的小伙子,一年前他曾在校園網(wǎng)上發(fā)布了Linux內(nèi)核。

單內(nèi)核(Monolithic kernel),也稱為宏內(nèi)核,但我認為宏內(nèi)核這個詞可能會引起誤解,所以我將一直使用單內(nèi)核這個術語。Linux就是基于單內(nèi)核的,這是較為傳統(tǒng)的內(nèi)核架構。內(nèi)核提供多種服務,我們常用的文件IO、內(nèi)存管理、網(wǎng)絡相關的系統(tǒng)調(diào)用都在內(nèi)核態(tài)運行,且都在同一地址空間內(nèi)。

譚寧邦教授直言Linux采用單內(nèi)核是在開歷史倒車,是操作系統(tǒng)技術的倒退,回到七十年代。此前他也曾開源一個類Unix的操作系統(tǒng),名為MINIX,而MINIX基于微內(nèi)核。

微內(nèi)核(Microkernel)提出時間比單內(nèi)核晚,在學術界被視為新興的技術。微內(nèi)核采用模塊化設計,將內(nèi)核功能簡化到最少,只提供基本功能,更多的功能在用戶態(tài)運行,不同服務在不同地址空間運行,常用服務(如IO、內(nèi)存管理)通過IPC調(diào)用組合提供。從這個角度看,微內(nèi)核的擴展性更強,添加新功能無需重新編譯內(nèi)核,且由于內(nèi)核服務間的隔離,使得操作系統(tǒng)更安全,一個服務崩潰不會影響其他服務。但問題也很明顯,大量的IPC會影響性能。

微內(nèi)核的理念與后來大型分布式系統(tǒng)中的SOA、微服務概念不謀而合。然而,歷史并未如期發(fā)展,站在二十一世紀的第三個十年回望,Linux取得了空前的成功。時至今日,無論是MINIX還是其他,鮮有廣泛使用的微內(nèi)核操作系統(tǒng)(去年華為鴻蒙高調(diào)宣布采用單微內(nèi)核架構,我們拭目以待)。當然,Linux的成功與其采用單內(nèi)核架構并無直接關聯(lián),主要歸功于其出色的開源社區(qū)運營模式(觀點出自《大教堂與集市》)。

譚寧邦教授和林納斯的這場論戰(zhàn)在上世紀90年代引起了巨大反響,雙方陣營都有多位技術大牛加入討論。這場論戰(zhàn)的記錄至今仍常見諸報端。論戰(zhàn)持續(xù)時間很長,從技術本身的爭辯到后來雙方的互相嗆聲,多年后兩人也曾公開表達和解,表示雙方只是技術之爭,不涉及任何私人攻擊。

在這場論戰(zhàn)之前,這位老教授和這位年輕的發(fā)明者其實也有交集。譚寧邦教授曾出版過講解Unix和操作系統(tǒng)的書籍,并隨書附贈了MINIX的源碼。林納斯在發(fā)明Linux之前,確實通過這本書和MINIX的代碼學習了操作系統(tǒng)知識。所以從某種意義上說,譚寧邦教授可以說是林納斯的半個老師。

這場論戰(zhàn)距今已近三十年,是爭是吵,已無需糾結。這里不再展開具體細節(jié),有興趣的讀者可以在互聯(lián)網(wǎng)上找到當年的蛛絲馬跡。

內(nèi)核態(tài)、用戶態(tài)與CPU的特權等級intel x86 CPU的架構將所運行的指令劃分為4個不同特權等級:ring 0、ring 1、ring 2、ring 3,通常稱為保護環(huán)(protection ring)。從ring 0到ring 3,特權級別依次降低。Linux使用了ring 0和ring 3兩個特權等級。運行在ring 0的程序被稱為內(nèi)核態(tài)(Kernel Space)程序,運行在ring 3的程序被稱為用戶態(tài)(User Space)程序。

什么是系統(tǒng)調(diào)用?圖片來自網(wǎng)絡

系統(tǒng)調(diào)用與軟中斷我們已經(jīng)大致了解了什么是用戶態(tài),什么是內(nèi)核態(tài)。那么,這與系統(tǒng)調(diào)用有什么關系呢?

請看下一張圖:

什么是系統(tǒng)調(diào)用?圖片來自網(wǎng)絡

可以看到,在用戶態(tài)和內(nèi)核態(tài)的邊界上畫著線,這就是系統(tǒng)調(diào)用!也就是說,無論是單內(nèi)核還是微內(nèi)核,運行在用戶態(tài)的應用程序想要使用某些只能在內(nèi)核態(tài)執(zhí)行的功能,必須通過系統(tǒng)調(diào)用來實現(xiàn)。所以你需要明白:進程從用戶態(tài)陷入內(nèi)核態(tài)是目的,而使用系統(tǒng)調(diào)用僅僅是達到該目的的手段。因果關系要理清楚。

接下來解釋一下什么是軟中斷。要說軟中斷,先說一下中斷(interrupt),如果你大學時學過《計算機組成與體系結構》這門課,你應該會記得。中斷本身是一個硬件概念,就是打斷CPU,讓其執(zhí)行其他任務,比如鍵盤中斷、打印機中斷、定時器中斷等。軟中斷則是從軟件層面模擬了這一中斷操作。

網(wǎng)上許多資料會提到使用128號軟中斷指令(int 0x80)來使進程從用戶態(tài)陷入內(nèi)核態(tài),執(zhí)行完畢后使用iret指令返回用戶態(tài)。但這是較為傳統(tǒng)的老方法。隨著CPU(Intel和AMD)的升級,Linux內(nèi)核通常使用快速系統(tǒng)調(diào)用(Fast system calls)的sysenter/syscall指令代替int 0x80,使用sysexit/sysret代替iret。在運行軟中斷指令時,會使用一個寄存器來存儲具體的系統(tǒng)調(diào)用號,例如在Linux上,read和write的系統(tǒng)調(diào)用號分別為0和1。

單內(nèi)核與微內(nèi)核上的系統(tǒng)調(diào)用有什么不同呢?就系統(tǒng)調(diào)用的實現(xiàn)原理而言,沒有不同。所謂的差異實際上體現(xiàn)在系統(tǒng)調(diào)用的封裝函數(shù)的種類上!前面提到,單內(nèi)核內(nèi)核提供許多服務。以Linux為代表,其系統(tǒng)調(diào)用種類繁多,有三百多種。可以查看:filippo.io/linux-syscal

什么是系統(tǒng)調(diào)用?而微內(nèi)核操作系統(tǒng),則沒有這么多系統(tǒng)調(diào)用。例如,MINIX實際上只有三個系統(tǒng)調(diào)用(的封裝函數(shù)):

發(fā)送:_send()接收:_receive()發(fā)收一體:_sendrec()(基于老版本MINIX,最新的MINIX是否擴展了尚不清楚)

這當然不是說MINIX不支持open()、read()、write()和fork()。只是在MINIX系統(tǒng)上,這些功能本質(zhì)上都是通過_send()、_receive()、_sendrec()實現(xiàn)的。所以準確來說,MINIX上與Linux的open()、read()、write()、fork()這些系統(tǒng)調(diào)用封裝函數(shù)對應的,是_send()、_receive()、_sendrec()。

? 版權聲明
THE END
喜歡就支持一下吧
點贊12 分享