全面剖析《自己動手寫作業系統》第六章---程序
在一開始學習程序的時候,我們大概每個人都會遇到過這樣的問題,下面就讓我們帶著這些問題來認識認識程序。
1、程序是什麼?
2、什麼是多程序?
3、執行一個程序需要什麼?
4、多程序之間是如何排程的?
5、程序的上下文環境是什麼?
6、如何建立一個新的程序?
一、程序是什麼?
大家在面試時,有時候會被HR問到這麼一道題目:程式與程序有什麼區別?
如果讓我們通過生硬的概念來回答HR,往往會令HR失望。下面我想通過一個生活中的場景來回答HR這個問題。
週末,張先生想為他心愛的老婆做一道菜---牛肉排骨湯,他有做這道菜的菜譜,廚房裡也有牛肉排骨,味精,香料等,張先生就按著菜譜一步步得為他的老婆做好了這道菜---牛肉排骨湯,他的老婆很高興,感覺很幸福。
在這個比喻中,做牛肉排骨躺的菜譜就是程式,張先生應該就算是處理機(CPU),做這道菜的各種原料(牛肉排骨,大蔥等)就是輸入資料,一盤香噴噴的牛肉排骨湯則是輸出資料,程序就是張先生按著菜譜做好這道菜的整系列動作的總和。
於是我們知道,程式是一個靜態的,程序是用來描述動態的過程的。
二、什麼是多程序?
大家都知道,現在的作業系統都是支援多程序的,即一個CPU可以支援多個程序。這個又是什麼情況?讓我來修改一下上面的場景。
週末,張先生想為他心愛的老婆做一道菜---牛肉排骨湯,他有做這道菜的菜譜,廚房裡也有牛肉排骨,味精,香料等,張先生開始做牛肉排骨湯,做著做著,他的兒子的手被刀子劃破了,哭著跑到爸爸這裡來訴苦,這時候,張先生停下手裡的活,在菜譜上記下自己做到了哪裡,然後拿出一本急救手冊,按著上面的指示,把兒子的手傷包紮好之後,他回到廚房繼續做自己的牛肉排骨湯。
在這個場景中,就包含了兩個程序,程序一是做牛肉排骨湯,程序二是為兒子包紮傷口。
張先生(CPU)先是執行的程序一,在程序一還米有結束之前,暫停程序一,去執行了程序二。執行完程序二,回到繼續執行程序一,直至程序一結束。這個過程就成為程序之間的"切換"。
下面我們通過一張圖片來介紹多程序之間的關係:
圖(a):一個包含4個程序的程序表
圖(b):4個程序是完全獨立的
圖(c):4個程序進行切換,但任意時刻只有一個程序在執行
由此,我們得知,程序,從巨集觀上來說,有自己的目標,又受控於程序排程模組的控制。從微觀上來說,有自己的程式碼和資料,同時也擁有自己的堆疊。但又利用系統的資源。
三、執行一個程序需要什麼?
每個程序包含自己的程式碼,資料,和堆疊,並且都服從程序排程模組進行排程。
四、多程序之間是如何排程的?
多程序之間的排程是由程序排程模組來完成的。我們首先需要了解程序的狀態,下圖為3種狀態之間的關係:
在本書中,作者只介紹的是2、3,即就緒--執行--就緒之間的轉換關係。其中條件2是排程模組選擇其中某一個程序執行。
條件3是時鐘中斷髮生,排程模組將正在執行的程序調入到就緒佇列。
五、程序的上下文環境是什麼?
在張先生去為兒子包紮傷口時,他需要在菜譜上記錄下自己做牛肉排骨湯做到什麼地方,以便回來之後繼續做。同理,作業系統在進行系統切換時,也同樣要記錄下該程序的上下文環境。
於是我們建立了一個數據結構--程序控制塊(ProcessControl Block),它主要包括以下幾方面資訊:
1、程序識別符號 name:每個程序都必須有一個唯一的識別符號,可以是字串,也可以是一個數字。
2、程序當前狀態 status:說明程序當前所處的狀態。為了管理的方便,系統設計時會將相同的狀態的程序組成一個佇列,如就緒程序佇列,阻塞程序則要根據等待的事件組成多個等待佇列,如等待印表機佇列、等待磁碟I/O完成佇列等等。
3、程序相應的程式和資料地址,以便把PCB與其程式和資料聯絡起來。
4、程序資源清單。列出所擁有的除CPU外的資源記錄,如擁有的I/O裝置,開啟的檔案列表等。
5、程序優先順序 priority:程序的優先順序反映程序的緊迫程度,通常由使用者指定和系統設定。
6、CPU現場保護區 cpustatus:當程序因某種原因不能繼續佔用CPU時(如等待印表機),釋放CPU,這時就要將CPU的各種狀態資訊保護起來,為將來再次得到處理機恢復CPU的各種狀態,繼續執行。
7、程序同步與通訊機制 用於實現程序間互斥、同步和通訊所需的訊號量等。
8、程序所在佇列PCB的連結字 根據程序所處的現行狀態,程序相應的PCB參加到不同佇列中。PCB連結字指出該程序所在佇列中下一個程序PCB的首地址。
9、與程序有關的其他資訊。 如程序記賬資訊,程序佔用CPU的時間等。
每個程序有自己的程序控制塊,我們將這些程序控制塊組織到一起,儲存在一個叫程序表(Process Table)的結構陣列中。
在本書中,本著簡單實用的原則,我們的程序控制塊只包含了程序識別符號,CPU現場保護(即暫存器的內容),還有區域性描述符。程式內容如下:
/* 暫存器 */
typedef struct s_stackframe {
t_32 gs;
t_32 fs;
t_32 es;
t_32 ds;
t_32 edi;
t_32 esi;
t_32 ebp;
t_32 kernel_esp;
t_32 ebx;
t_32 edx;
t_32 ecx;
t_32 eax;
t_32 retaddr;
t_32 eip;
t_32 cs;
t_32 eflags;
t_32 esp;
t_32 ss;
}STACK_FRAME;
/* PCB程序控制塊 */
typedef struct s_proc {
STACK_FRAME regs; /* 暫存器 */
t_16 ldt_sel; /* LDT選擇子 */
DESCRIPTOR ldts[LDT_SIZE]; /* LDTs */
t_32 pid; /* 程序號 */
char p_name[16]; /* 名字 */
}PROCESS;
六、如何建立一個新的程序?
首先我們要申請一個程序控制塊(PCB),然後為新的程序分配資源(本程式內為堆疊),繼而初始化程序控制塊,最後將佇列加入就緒佇列(本程式即程序表)。
在本程式內,我們是這樣做到的