1. 程式人生 > >全面剖析《自己動手寫作業系統》第六章---程序

全面剖析《自己動手寫作業系統》第六章---程序

在一開始學習程序的時候,我們大概每個人都會遇到過這樣的問題,下面就讓我們帶著這些問題來認識認識程序。

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),然後為新的程序分配資源(本程式內為堆疊),繼而初始化程序控制塊,最後將佇列加入就緒佇列(本程式即程序表)。

    在本程式內,我們是這樣做到的