首先在最一開始,將藉由 Hello World 範例程式,引入一些作業系統的基本部件。以及作業系統的概念與目標,抽象性的實現,最後引入 System call 的概念,作業系統分層與總類。
以下為 C 語言的 Hello World
#include <stdio.h>
int main(void)
{
printf("Hello World\n");
return 0;
}
接著我們會通過 gcc 工具鍊,經過了前處理的巨集展開,經過編譯器 (c compiler, cc) 產生出組合語言,接著經過組譯器 (assembiler, as) 產生出目的碼檔案,最後鏈結器 (linker, ld) 鏈接其他檔案與函式庫後,產生出了可執行檔 hello.exe。
gcc hello.c -o hello
exe 包含了許多機械碼指令提供給 CPU,以及一些資料,而這些對於 CPU的機械指令,是由 CPU 的指令集架構 (Instruction Set Architecture, ISA) 所實現,而 CPU 還會包含一些暫存器,像是Program Counter (PC), 或是一些指令的運算元 (Operands), 記憶體地址等等資訊。
當執行一個可執行檔時,CPU 會進行以下
(最近執行過的指令或是資料會放入 CPU 的快取中用來加速整個程式的執行)
而在上面這一些過程中,作業系統會進行以下工作。
電腦通常由一個或是多個 CPU 組成,包含 RAM,硬碟,鍵盤,滑鼠,螢幕等等,可見電腦是一個複雜的系統,而如果開發一隻程式需要掌握這全部的知識,會讓程式的開發變得十足的困難,因此,電腦上面有一個軟體,稱之為作業系統,提供了一個更為抽象,方便理解的電腦模型。
作業系統為一種可以管理電腦硬體的軟體,而這個軟體有幾個目標需要達成。
抽象 (Abstraction) :
將硬體抽象化,一部電腦中,會包含CPU, GPU, RAM等等,但這是站在看待硬體的角度去看待,也就是以相較low-level的角度看待,而在作業系統中,會提供相較high-level的角度去看待這一些硬體,諸如process(處理程序),file system(檔案系統)等等,可以方便軟體開發,以及提供軟體更好的可移植性。
多工 (Multiplex) :
現代作業系統需要在多個 process 之間共享資源,再不互相干擾的前提之下,同時執行這一些程式。例如三個程式需要在同一台影印機上輸出結果,可能會發生三個程式的輸出混雜在一起的情況發生,而這時候我們會需要一些處理的方式,像是將輸出輸出到緩衝區之後的方法。
獨立性 (Isolation)
如果應用程式發生了當機,崩潰等等,不會影響到其他應用程式,程式的活動之間必須不能互相干擾。
共享 (Sharing)
許多時候不同程式之間需要交換資料,傳送資料,共同完成任務等等,例如使用檔案系統建立的一個檔案,經由文字編輯器讀取與編輯,之後寫入並儲存,這時候便會需要共享。
存取控制 (Access Control System)
有時我們不希望使用者之間共享資源,或是其他使用者讀取我們的檔案,而這時候我們會希望有存取控制的手段給予限制。
效率 (Efficiency)
作業系統作為資源的協調以及管理者,期望能夠更有效(並非公平)的運用資源。
假設我到麥當勞想要點一個大麥克,我會走到櫃台和服務人員點一個大麥克,而經過了3分鐘後,大麥克就會噴出來了,我不需要管大麥克是如何製作的,只要和服務人員說我要一個大麥克,服務人員就會噴一個大麥克,服務人員就是 API 的概念。
而假設我要搜尋一個檔案,我只需要通過檔案系統就可以找到目標檔案,不需要管應的讀寫頭該如何運動,或是需要運動到磁盤中的那一個位置,檔案系統就相當於一個 API,讓我們可以方便的直接得到目標檔案,API 為一個抽象思考的概念,可將複雜的系統簡化,方便程式開發。
以下為 C 語言 API 概念示範 :
//source : https://www.bottomupcs.com/abstration.xhtml
#include <stdio.h>
//greet_api class
struct greet_api
{
int (*say_hello)(char *name);
int (*say_goodbye)(void);
};
//member function implementation
int say_hello_func(char *name)
{
printf("Hello %s\n", name);
return 0;
}
//member function implementation
int say_goodbye_func(void)
{
printf("Goodbye\n");
return 0;
}
//object implementation
struct greet_api greet_api =
{
.say_hello = say_hello_func,
.say_goodbye = say_goodbye_func
};
int main(int argc, char *argv[])
{
greet_api.say_hello(argv[1]);
greet_api.say_goodbye();
return 0;
}
execute:
./hello alice
output:
hello alice
goodbye
整個作業系統包含了幾個部份,分別為 User Interface 的部份,像是 GUI, Shell 等等,和底層硬體進行互動的 kernel 部份。
而從硬體層,如 CPU,硬碟,到 process, file system 較高抽象層次,中間需要經過一個界面提供給使用者進行使用,類似於 API 的概念,而這個中間層就稱為 kernel。
我們可以對這一張圖進行分層:
每一個在 User space 中執行的程式,稱為一個 process,每一個 process 有自己的記憶體空間,包含一些 instruction, data, stack 等等資訊,instruction 用來實現程式的計算部份,data 為一個變數記錄計算時所發生的動作,stack 會紀錄程式對其他函數或是其他程式的呼叫。電腦中通常會有許多 process 正在執行,但皆是通過一個 kernel 進行管理。
在一個現代完整的作業系統中,kernel 中還會有許多的服務,像是處理 TCP/IP 協定,支援許多不同的網卡等等,所以一個 kernel 通常程式碼的數量是十分龐大的。
檔案系統為 kernel 提供和硬體層進行互動的 API,而對於 user space 和 kernel 層的互動,kernel 也提供了一些 API 進行互動,當 user space 中的 process 需要呼叫 kernel 所提供的服務時,稱為一個系統呼叫 (system call)。
在作業系統中,我們主要關注的部分是 kernel 的部分,正如前面所言,作業系統的角色是扮演使用者到硬體層面的溝通者,而在作業系統中主要負責這個部分的是 kernel,因此在作業系統的討論中會集中在 kernel 中各個功能的實踐,諸如記憶體管理等等。
如果有一個程式需要打開一個檔案,則會需要進行open
的系統呼叫,將文件名稱以及一個"額外"參數 (Argument) 傳入open
中,這個額外參數表示我想要對 "file" 這個檔案進行寫入的操作。
fd = open("file", 1);
這看起來像是一個普通的函式呼叫 (function call),但實際上這是一個系統呼叫,會跳轉到 kernel 中,執行 kernel 中實現open
的程式碼,最後回傳一個 file descriptor,由 fd 變數所接受,fd 的全名為 file descriptor,之後,其他程式就可以通過 fd 來得知該如何對這個檔案進行操作,這個概念也被稱為 file handle。process 使用open
的系統呼叫,user space 和 kernle space 會進行互動。
< from book-riscv >
事實上 C 語言的函式庫 (lib c) 的printf
函式就隱藏了系統呼叫的部分,如果我們將printf
展開,可以發現裡面存在 System call 的部分。
write(stdout, text);
< How Does the CPU Output “Hello, World!” To the Command Line >
System call 定義在user.h
中,在 xv6 中支援21種 System call,System call 只能在 supervisor mode (kernel mode) 底下執行。
POSIX (Portable Operating System Interface of UNIX) 為一套 UNIX 的標準。
UNIX 為作業系統,在早期 AT&T 將 UNIX 作業系統以低廉的收費甚至免費授權給許多學術以及商業機構,許多機構在基於 UNIX 的基礎上進行一些改進而產生出新的作業系統,這一些作業系統被稱為 UNIX Like 的作業系統,而為了提高程式在這一些 UNIX Like 之間的可移植性,IEEE 制定了一套標準,稱為 POSIX。
POSIX 制定了關於 System call 的標準,符合 POSIX 標準的作業系統需要支援哪一些 System call,以及 C 語言函式庫,shell 等等一系列的標準。
Shell 為一隻程式,接受使用者輸入的指令並且執行他們,Shell 為 user space 的程式,而非 Kernel 的一部分,可以從上圖中得知。在UNIX 中有許多的 Shell,諸如 bash, zsh,擁有自己的界面和一些腳本功能,之所以被稱作為 Shell 可以從上面這一張圖得知,他隱藏了Kernel (從殼到核心) 細節的部份,通過 Shell 去和 Kernel 互動。
定義 : 允許多個 Process 同時執行的 System。
目的 : 提高 CPU 使用率 (utilization),避免 CPU 處於閒置 (idel),等待使用者命令的狀態。
作法 : 利用 CPU 排班技術 (Schedualing) 來讓 CPU 可以在多個 Process 之間切換。
Process 同時執行使用兩種方法:
Concurrency,可以將一隻程式拆分成多個可獨立執行的工作,可以讓很多事情一起做,但是不一定真得要一起做,讓多個工作在單一個 CPU (或是單核 CPU) 上面執行。而可將程式拆分成多個部分,是屬於程式架構的部分。
< Introduction to OpenMP: 02 part 2 Module 1 >
Parallelism,是指可以同時執行多隻程式,把程式分配給不同的核心(多核處理器)(執行緒 thread, 在 RISC-V 中也會稱為 hart)
< Introduction to OpenMP: 02 part 2 Module 1 >
定義 : 在某一些地方(如: 恐龍本)會稱之為 Multitasking system,為 Mutiprogramming system 的一種,特色為 CPU 會很頻繁的進行 jobs switching,也就是在多個 Process 之間不斷的切換,這樣的設計適合與使用者進行交互,因為響應時間較短。作業系統會通過資源分享,讓每一個使用者有專屬的系統。
定義 : 又被稱為 Multiprocessing, Parallel, Tighty-couple system 主要功能如字面上的意思,有多顆 CPU,或是多核 CPU,每一個核心 (core) 之間共享記憶體,I/O 裝置,匯流排等等。
定義 : 又稱為 Loosely coupled system,特色為每一台電腦都是獨立的,有各自的 CPU, I/O 裝置,但是在軟體上會看作是一台電腦,像是Peer-to-peer (BT 的那個 P2P)
定義 : 對於每一個工作完成的時間有嚴格的要求,如果工作不能在規定的時間內完成,則視為失敗。
riscv riscv-fast-interrupt
chromitem-soc.readthedocs
RISC-V异常与中断机制概述
RISC-V spec references the word 'hart'
Serial communication
Linux 核心設計: Timer 及其管理機制