KEY:系統論 系統編程
System Programming
過去的Unix編程是沒有系統不系統之分的。
即便是開發 X Window也是在系統級(system-level)編程,看到系統的全部API。現代的操作系統編程有所謂[系統級編程],使用與[應用編程]不同的API(System programming API) 。

從編程的形式和耗費心力上。系統編程與應用編程沒有本質差別,這也意味着一個經驗豐富的應用程序猿轉向系統編程難度不大。
系統編程與應用編程的不同在於:
- 第一,系統編程更接近硬件。系統程序猿必須熟悉硬件環境和操作系統環境;相對的,應用程序猿很多其它是熟悉應用的環境;
- 第二,系統編程使用的函數庫和庫函數調用方法與應用編程有一些不同。比方,在調用系統調用(syscall)時使用所謂的陷入方式,也就是軟中斷方式。
近年來,隨計算應用深化,應用編程有遠離系統編程的趨向。
只是,這並不能說或者預言系統編程的末日的到來。由於,有人用Javascript或C#寫應用就要有人寫它的解釋器和執行時。
此外,操作系統代碼僅僅能使用系統級編程。
本書的一些核心問題:
- 系統級接口(system-level interface)究竟是什么?又怎樣編寫Linux的系統級應用?
- 內核和C庫詳細提供了什么給我們?
- 怎樣編寫優質代碼?Linux又有什么已知的陷阱(tricks )?
- Linux的系統調用是怎樣實現的?
- What neat system calls are provided in Linux compared to other Unix variants?
How does it all work? Those questions are at the center of this book.
Linux系統編程須要熟悉三大塊內容:系統調用、C庫和C編譯器。
System Calls
[系統調用]就是用戶空間與內核之間的函數接口,目的是為了給用戶空間的程序請求內核服務和資源。與其他非常多操作系統相比。Linux實現的系統調用少非常多。比方,Linux為I386體系實現了300個左右的系統調用。而 Microsoft Windows據說實現數以千計的系統調用。
Linux內核的不同平台實如今系統調用上存在差異,只是90%的系統調用是同樣的。
Invoking system calls
出於安全性等因素,應用代碼是不可以直接調用系統調用的。必須使用特殊的[陷入](trap)機制。一種“知會”內核進行工作的函數調用形式(KEMIN:證明兩“系統”的耦合度較弱,比直接調用方式要弱。以系統論的角度考究syscall也非常有意思)。陷入機制的詳細實現也是因不同的體系而有所不同的。
比方I386體系,應用代碼通過觸發軟中斷指令(int 0x80)來調用syscall。那0x80是什么呢?軟件中斷向量號嗎?回答否。
應用代碼必須通過處理器的寄存器向告訴內核向量號和調用參數。比方。假設應用代碼調用open(),它得置eax值5,然后把參數放在另外的五個寄存器:ebx, ecx, edx, esi, 和edi(所以系統調用至多使用五個參數),這些寄存器保存實用戶空間的地址,也就是參數數據所在。
作為一位系統程序猿,你一般不需干涉系統調用的過程,由於調用過程由體系定義。而且由C庫和C編譯器自己主動處理。
The C Library
C庫是全部UINX應用的核心。由於不管你使用什么語言,你的代碼終於還是調用C庫。其他高級語言的庫都是基於C庫構建的,或者說是這些庫是對C庫的包裝。
如今的Linux,使用的C庫是GNU libc。行話glibc。
glibc不不過個程序語言庫,比方C標准庫。它還是一個系統庫,並且是一個現代操作系統庫,函數涵蓋了對系統調用的包裝、線程支持、網絡支持等。
The C Compiler
Linux的C編譯器是gcc,過去gcc代表GNU C Compiler,是cc的在GNU項目的實現;如今gcc代表GNU Compiler Collection,只是gcc 仍然是C編譯器的入口。
Unix系統(包含Linux)使用的編譯器與系統編程是高度相關的。由於編譯器負責實現了C標准和系統ABI。
APIs and ABIs
無人不希望自己寫的程序具有非常好的移植性(portability)。能夠執行在不同軟件平台(如操作系統或應用框架)、硬件平台(如處理器體系及載板),甚至跨平台的開發版本號執行。
有多種因素影響着程序的可移植性。其中就有兩組不同的[系統接口]影響程序的可移植性:第一組是應用編程接口(API),還有一組是應用二進制接口(ABI)。
APIs
API是兩支軟件在源碼級的接口。通過這個標准的接口(一般以函數形式實現),客戶代碼(一般稱高級別的軟件代碼)能夠調用服務代碼(低級別的軟件代碼)。API本身是抽象的,它僅定義了一個接口,不涉入應用程序怎樣實現的細節。
系統論里的接口范疇
接口或者port是兩子系統邊界[信息交換]的規格或約定方式,用通俗的理解就是。信息是什么樣的。接口是信息的格式。
應用編程接口。就是軟件系統不同組成部分銜接的約定。因為近年來軟件的規模日益龐大,經常會須要把復雜的系統划分成小的組成部分,編程接口的設計十分重要。
程序設計的實踐中,編程接口的設計首先要使系統的職責得到合理划分。良好的接口設計能夠減少系統各部分的相互依賴。提高組成單元的內聚性,減少組成單元間的耦合程度,從而提高系統的維護性和擴展性。
因為API是抽象的,必須清晰差別接口定義與接口的實現。比方[C標准庫]是API,uclibc是一個實現;POSIX 是API,glibc是一個實現。
那么API一般涵蓋什么樣的函數呢? 這是一個非常有意思的問題。比方C標准庫是一種語言庫,它必須非常通用。所以接口函數不能依賴軟件或硬件特性;相反POSIX 是操作系統標准,它相對沒那么的通用。
ABIs
API是源碼級別接口,是邏輯約定;而ABI是二進制級別接口,定義的在特定的架構上兩個軟件模塊之間的接口的物理實現方式。這樣的[物理實現約定]保證二進制代碼兼容,也就是保證一段目標代碼可以在不論什么具有相同ABI的系統上都正常運作,不須要又一次編譯源碼。
ABI([物理實現約定]) 的內容包含調用約定(calling conventions)、字節序(byte ordering)、寄存器使用(register use)、系統調用實現方式、對象鏈接、庫行為和二進制格式。以調用規則為例,它規定了函數怎樣被調用。參數怎樣傳遞,哪些寄存器被保留和哪些會被破壞,以及調用者怎樣提取返回的結果。
雖然以前嘗試着為特定架構下不同的操作系統(特別是i386上的Unix操作系統)定義唯一的ABI,然而到眼下為止還沒有取得成效。相反,包含Linux在內的操作系統都嘗試定義各自獨立的ABI,這些ABI和架構緊密相連。
大部分的ABI涉及了機器級別的概念,如特定的寄存器或者匯編指令。
因此。在Linux系統中。每個機器架構都有自己的ABI集合,其實,我們以機器架構的名稱來稱呼這些ABI,比如alpha x86-64等。
1.4 Linux編程概念、POSIX和系統庫
全部的Unix系統,包含Linux系統。都提供了一個共同的抽象和接口集合,這個共同點定義了Unix。
如對文件和進程的抽象、管道和套接字管理的接口等等,都是Unix的核心內容。
綜上,個人理解,系統編程與應用編程的不同在於:調用的接口不同。
