程序(二)--- 程序切換與排程
在程序一中,我們知道程式啟動後是被載入進記憶體,同時將第一條指令地址寫入pc暫存器,接下來由cpu來處理各種指令。但是cpu並不是一直在執行這個程序的,在遇到IO或者一些其他事件的時候,就會讓出cpu,讓給其他的程序來使用cpu.
那麼,到底是哪些情況下會導致程序被切換?
系統呼叫
作業系統向普通使用者提供的介面被稱作系統呼叫,系統呼叫提供了訪問和使用作業系統提供的服務的介面。
- 系統呼叫的實現程式碼是作業系統級的
- 這個介面通常是面向程式設計師的
但是程式設計師一般不是直接使用系統呼叫的,程式設計師使用API
間接的呼叫了系統呼叫
再linux 系統中提供的api是POSIX API
( Portable Operating System Interface
e.g :printf
這個api 被呼叫執行的時候,其實引發的系統呼叫是write
以下執行一段shell
for i in {1..100}; do printf 0;sleep 1 ;done
使用strace 來檢視系統呼叫strace -p 553467
通過上圖可以看到有一個write
系統呼叫,還有一個sleep
系統呼叫
再說程序切換的時候我們需要了解一點就是作業系統其實是分為兩個模式,如下:
雙模式
上面瞭解了系統呼叫之後,我們需要知道為什麼需要系統呼叫。
因為再現代作業系統上有一個特殊的硬體,用於劃分系統的執行狀態,至少需要兩種單獨執行模式:使用者模式和核心模式
使用者模式
當計算機系統執行使用者應用程式(如檔案建立或使用任何其他應用程式)時,系統處於使用者模式。 此模式不能直接訪問計算機的硬體。 為了執行與硬體相關的任務,例如當用戶應用程式從作業系統請求服務或發生某些中斷時,在這些情況下,系統必須切換到核心模式。 使用者模式的模式位為1。這意味著,如果系統處理器的模式位為1,則系統將處於使用者模式。
核心模式
執行作業系統程式碼,作業系統的所有底層任務均在核心模式下執行。 由於核心空間可以直接訪問系統的硬體,因此核心模式可以處理所有需要硬體支援的程序。 除此之外,核心模式的主要功能是執行特權指令。 這些特權指令沒有提供給使用者訪問許可權,這就是為什麼這些指令無法在使用者模式下進行處理。因此,所有限制使用者干預的程序和指令都在作業系統的核心模式下執行。 核心模式的模式位為0。因此,要使系統在核心模式下執行,處理器的模式位必須等於0
目的
確保作業系統正確執行,因為對於硬體的操作以及一些cpu 的特權指令如果由使用者來執行處理,很可能會對硬體造成不可挽回的損傷導致計算機被破壞等危害。
上圖中就是兩種模式的切換,其中從使用者模式切換到核心模式就叫做trap
,trap 就是將作業系統的模式為由 1變為0,在將控制權交給使用者程式之前,系統總是切換到使用者模式
系統呼叫的實現
每個系統呼叫都有一個唯一的數字編號,被稱為系統呼叫號
。
當用戶程式碼呼叫API 時,API會向系統呼叫介面
指明起所要用的系統呼叫號
,作業系統核心中維護了一張索引表,依據這個呼叫號可以檢索到訪系統呼叫程式碼再核心中的位置。
系統呼叫號和程式碼
系統呼叫號 | 入口點 | 函式名 | 原始碼 |
---|---|---|---|
0 | read | sys_read | fs/read_write.c |
1 | write | sys_write | fs/read_write.c |
2 | open | sys_open | fs/open.c |
3 | close | sys_close | fs/open.c |
4 | stat | sys_newstat | fs/stat.c |
中斷技術
什麼是中斷?
中斷是指程式執行過程中,當發生某個事件的時候,中止cpu上現行程式的執行,從而執行該事件的處理程式,等到事件執行完畢後返回源程式中斷的地方繼續執行。
這句話中的事件其實可以理解為一個系統呼叫或者一個硬體響應,比如滑鼠移動,鍵盤輸入等。
然後事件的處理程式其實就是指的是,作業系統針對系統呼叫或者硬體進行響應而做的事情
中斷源
上面介紹了上面是中斷,從上面介紹的中斷可以看出中斷源其實應該可以分為兩類,外中斷和內中斷
外中斷(interrupt)
來自處理器之外的硬體中斷訊號,比如
-
時鐘中斷:一個程序最長使用cpu的時間,當時間到達後,就會觸發時鐘中斷由作業系統接管cpu的執行
-
鍵盤中斷: 鍵盤打字也是一箇中斷訊號
-
外圍裝置中斷: 比如麥克風,耳機,音響等
這些外中斷都是非同步中斷(隨機中斷),因為這些外部裝置發生的時機都是隨機的
內中斷(異常 exception)
來自於處理器內部,指令執行過程中發生的中斷,比如
- 硬體異常: 掉電,奇偶校驗錯誤
- 程式異常:非法操作,地址越界,斷電,除數為0
- 系統呼叫
這些都是同步中斷
中斷處理過程
- 程序執行過程中觸發了異常或者遇到了外部事件,作業系統就會儲存這個程序的上下文資訊(context)
程序上下文資訊:當一個程序在執行時,CPU的所有暫存器中的值、程序的狀態以及記憶體堆疊中的內容被稱為該程序的上下文,當核心需要切換到另一個程序時,它需要儲存當前程序的所有狀態,即儲存當前程序的上下文,以便在再次執行該程序時,能夠回到切換時的狀態執行下去
- 判斷中斷來源來選擇對應的中斷處理程式
- 選擇一個程序恢復
- 恢復程序的上下文
- 恢復程序到剛剛中斷的那個地方繼續執行
程序切換
通過以上資訊,可以大致瞭解程序進行切換所需要的資訊。
總結
程序切換的時候,程序需要進入等待狀態或者由於被強佔cpu 進入就緒狀態,
切換過程 (user mode ---> kernel mode)
- 需要儲存中斷程序的上下文資訊
- 修改中斷程序的控制資訊(比如程序狀態)
- 將被中斷的程序加入相應的狀態佇列
- 從就緒佇列排程一個新的程序並恢復它的上下文資訊
程序排程
程序控制塊(PCB)
PCB (process control Block) 包含了指定程序的一系列的資訊
- process state : 程序狀態
- process number: pid
- program counter: pc 值
- registers: 暫存器的值
- memory limits: 記憶體資訊
- list of open files: 程序所開啟的檔案列表
之前有介紹過程序的記憶體空間資訊是如下圖:
但是其實真實的程序再記憶體中的資訊其實還要加上PCB
的資訊,所以如下圖:
上圖就是程序的上下文資訊
程序佇列
再上面程序切換的概念裡面有描述過,一個程序的切換就是將執行時的進行放入就緒或者等待佇列,然後再從就緒佇列裡面獲取一個程序來再cpu上執行。那麼程序佇列又是怎麼個形式呢?
如下圖
程序佇列中只存在PCB
資訊,因為 PCB
資訊裡面以及包含了程序的元資訊沒必要包含所有程序的資訊,因為程序是一直在切換的,那就回一直入隊出隊,如果資訊過多就會造成不必要的資源浪費。這裡沒有執行時佇列是因為,執行時的程序只有一個(單個cpu) 因此沒必要設定一個執行時佇列。
就緒佇列
就緒佇列一個
根據上圖中 PCB2
要開始進入就緒佇列,那麼只需要將PCB7
的頭指標置空。同時將就緒佇列的尾指標指向 PCB7
等待佇列
等待佇列針對不同的等待IO事件分為不同的佇列,如果某些程序等待相同的事件發生,那麼就將這些程序防盜一個佇列
如上圖中 分別是
- 等待第0號磁帶機
- 等待第1號磁帶機
- 等待第0號磁碟事件
- 等待第0號終端事件
程序排程總結
程序再整個生命週期中會再各個排程佇列中遷移,由作業系統的一個排程器來執行
上圖中i,每個矩形框代表一個佇列。存在兩種型別的佇列:就緒佇列和一組裝置佇列。圓圈表示為佇列服務的資源,箭頭表示系統中的程序流。
一個新程序最初被放入就緒佇列。它在那裡等待,直到它被選中執行或分派。一旦程序被分配了 CPU 並正在執行,可能會發生以下幾個事件之一:
- 該程序可以發出 I/O 請求,然後放入 I/O 佇列。
- 程序可以建立一個新的子程序並等待子程序終止。
- 由於中斷,程序可以從 CPU 中強制移除,並放回就緒佇列中
在前兩種情況下,程序最終會從等待狀態切換到就緒狀態,然後放回就緒佇列中。一個程序繼續這個迴圈,直到它終止,此時它從所有佇列中刪除,並釋放其 PCB 和資源。