前言
這個系列是《操作系統(tǒng)導(dǎo)論》的讀書筆記诚亚。力求簡潔赖草、清晰、易懂乡翅。
1.OS
Operating System,位于硬件與上層應(yīng)用之間一種特殊的軟件鳞疲,它負責(zé)提供硬件的抽象,與上層應(yīng)用直接溝通蠕蚜,確保整個系統(tǒng)維持在一個高效穩(wěn)定的狀態(tài)尚洽。構(gòu)建它要考慮多個方面的因素,分別是抽象靶累,高性能腺毫,可靠性 癣疟,安全,能耗潮酒。在移動設(shè)備盛行的今天争舞,還需要考慮操作系統(tǒng)的可移植性。
抽象:作為操作系統(tǒng)中最基本的一個目標(biāo)澈灼,它的功能是讓操作系統(tǒng)便于理解和使用,因此操作系統(tǒng)引入諸如“進程”店溢,“文件”叁熔,“虛擬存儲器”的抽象概念,使得用戶編寫應(yīng)用程序時只考慮相應(yīng)的抽象接口即可床牧,無須了解硬件設(shè)備的底層原理荣回。如圖1-11所示。
高性能:在其他要求滿足的情況下戈咳,操作系統(tǒng)在運行時應(yīng)盡可能地減少開銷心软,包括時間上(更少的指令)和空間上(更少的內(nèi)存和磁盤)。
可靠性:所有的應(yīng)用程序都運行于操作系統(tǒng)之上著蛙,這要求OS必須足夠可靠以保證應(yīng)用程序的順利運行删铃。
安全:應(yīng)用程序之間并發(fā)運行,要求OS能控制它們的邊界和行為踏堡。當(dāng)有應(yīng)用程序突破了OS的"封鎖"猎唁,對其他應(yīng)用程序或者對OS造成了惡意影響時,OS能將影響降低到最低顷蟆。
OS的功能可分為三個部分诫隅,虛擬化(virtualization),并發(fā)(concurrency)帐偎,持久性(persistence)逐纬。
2.虛擬化
CPU虛擬化:為滿足不同程序?qū)PU資源的要求崩瓤,操作系統(tǒng)將CPU虛擬化成了多個虛擬CPU侮繁,以進程的視角來看酬屉,就好像是自己一直在占用一個物理CPU一樣须喂。
上述OS使用的技術(shù)被稱為“時分共享”信粮,它給每個進程分配一個獨占CPU的時間片搀菩,計時結(jié)束后CPU切換到下一個進程運行缕题,這能讓OS運行多個并發(fā)進程摄狱。但它也不可避免的帶來了一些額外開銷绕辖,因為進程切換需要額外的時間和空間以保存進程的相關(guān)信息摇肌,以便此進程后續(xù)運行時能夠恢復(fù)。這個過程稱為上下文切換(context switch)仪际。
實現(xiàn)虛擬化需要低級的機制(mechanism)和高級的策略(policy)相互協(xié)作围小。機制是低層的協(xié)議或者方法昵骤,它屬于戰(zhàn)術(shù)層次,表示虛擬化的步驟具體應(yīng)該怎么實現(xiàn)肯适,上文中的時分共享就是一個具體的實現(xiàn)方法变秦。策略是OS作出決策的算法,屬于戰(zhàn)略層次框舔,它聚焦于某一時間段應(yīng)該對哪個進程提供虛擬CPU才能使得系統(tǒng)運行狀態(tài)較好蹦玫。操作系統(tǒng)使用進程的調(diào)度策略充當(dāng)這一角色。分離機制與策略的模塊化做法可以提高系統(tǒng)的魯棒性刘绣,需要修改策略時可以不用改變機制的實現(xiàn)樱溉。
具體闡述虛擬化的過程離不開操作進程。首先對進程的相關(guān)知識進行盤點纬凤。
3.進程
顧名思義福贞,進程即“進行中的程序”,它是CPU停士、內(nèi)存挖帘、I/O設(shè)備的抽象表示,是操作系統(tǒng)對正在運行的程序的一種抽象恋技∧匆ǎ“進程”概念的出現(xiàn)使得操作系統(tǒng)能夠?qū)⒊绦虻臓顟B(tài)信息和行為信息具體化,并追蹤系統(tǒng)內(nèi)部動態(tài)變化的規(guī)律蜻底,為多道程序處理打下了基礎(chǔ)你稚。
進程:一個具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合的一次運行活動。它是操作系統(tǒng)進行資源分配和動態(tài)執(zhí)行的基本單元朱躺。
操作系統(tǒng)引入進程的概念的原因:
從理論角度看刁赖,是對正在運行的程序過程的抽象;
從實現(xiàn)角度看长搀,是一種數(shù)據(jù)結(jié)構(gòu)宇弛,目的在于清晰地刻畫動態(tài)系統(tǒng)的內(nèi)在規(guī)律,有效管理和調(diào)度進入計算機內(nèi)存運行的程序源请。
進程是一個實體枪芒。每一個進程都有它自己的地址空間,一般情況下谁尸,包括文本和堆棧(stack region)舅踪。文本區(qū)域存儲處理器執(zhí)行的代碼;數(shù)據(jù)區(qū)域存儲變量和進程執(zhí)行期間使用的動態(tài)分配的內(nèi)存良蛮;堆棧區(qū)域存儲著活動過程調(diào)用的指令和本地變量抽碌。第二,進程是一個“執(zhí)行中的程序”决瞳。程序是一個沒有生命的實體货徙,只有處理器賦予程序生命時(操作系統(tǒng)執(zhí)行之)左权,它才能成為一個活動的實體,我們稱其為進程痴颊。
3.1 特征
3.2 構(gòu)成
進程由程序赏迟、數(shù)據(jù)、進程控制塊(PCB,processing control block)三部分組成蠢棱。其中進程控制塊(PCB)是系統(tǒng)為了管理進程設(shè)置的一個專門的數(shù)據(jù)結(jié)構(gòu)锌杀。系統(tǒng)用它來記錄進程的外部特征,描述進程的運動變化過程泻仙。同時糕再,系統(tǒng)可以利用PCB來控制和管理進程,創(chuàng)建進程即創(chuàng)建進程的PCB饰豺,撤銷進程即撤銷進程的PCB,所以說允蜈,PCB(進程控制塊)是系統(tǒng)感知進程存在的唯一標(biāo)志冤吨。
3.3 API
上文說過進程是對運行程序的抽象妓蛮,那么它必然對外提供了接口怠李,讓用戶在無需了解其內(nèi)部具體實現(xiàn)的情況下實現(xiàn)對進程的相關(guān)操作。
- 創(chuàng)建 (create)
通過fork()和exec()這樣一對系統(tǒng)調(diào)用來創(chuàng)建新進程蛤克,其中fork()用于在一個進程中創(chuàng)建子進程(child)捺癞。原來的進程稱為父進程。int process_child=fork();
子進程創(chuàng)建完畢构挤,但并不是創(chuàng)建完畢后就立即運行子進程髓介,這依賴于OS的調(diào)度策略。
由于在子進程復(fù)制了父進程的堆棧段筋现,所以兩個進程都停留在fork函數(shù)中唐础,等待返回。因此fork函數(shù)會返回兩次矾飞,一次是在父進程中返回一膨,另一次是在子進程中返回,這兩次的返回值是不一樣的洒沦。如果運行父進程豹绪,此時fork()返回的值是新創(chuàng)建的子進程的進程描述符PID(process identifier)。如果運行的是子進程申眼,它地址空間中的代碼不會包含fork()之前的代碼森篷,因此子進程從fork()之后的代碼開始運行输钩,此時fork()返回的值是0,就好像是子進程調(diào)用了自己一樣仲智。其實就相當(dāng)于鏈表买乃,進程形成了鏈表,父進程的fork函數(shù)返回的值指向子進程的進程id, 因為子進程沒有子進程钓辆,所以其fork函數(shù)返回的值為0剪验。fork()根據(jù)父或子進程返回不同值,這樣很容易編寫出考慮兩種不同條件的代碼——使用if else
前联。
大多數(shù)時候我們運行的子進程都需要完成與父進程不同的任務(wù)功戚,這就需要在fork()的基礎(chǔ)上利用系統(tǒng)調(diào)用exec()。雖然理論上我們也能在if(process_child)==0
的代碼下自己手動實現(xiàn)各種功能似嗤,但exec()的出現(xiàn)極大降低了重復(fù)造輪子的成本啸臀,大部分的程序我們拿來就能用。這是通過給exec()傳遞可執(zhí)行程序的名稱及相應(yīng)的參數(shù)(如文件名烁落,數(shù)據(jù)乘粒,環(huán)境變量等)實現(xiàn)的。exec()是execute的縮寫伤塌,linux的shell中可使用它來執(zhí)行其他命令灯萍。
注意exec()是一個函數(shù)簇,它有多種函數(shù)每聪。調(diào)用exec()并沒有創(chuàng)建一個新進程旦棉,而是將執(zhí)行程序的代碼和靜態(tài)數(shù)據(jù)以及堆、棧和內(nèi)存空間覆蓋到原進程中药薯,但進程的標(biāo)識符pid不變绑洛。一旦調(diào)用exec()之后,原代碼中exec()之后的代碼不會被執(zhí)行童本,因為此時進程已經(jīng)徹底改頭換面诊笤,原進程中的代碼段都已被覆蓋了。
進程創(chuàng)建的具體過程
通過系統(tǒng)調(diào)用我們能直接創(chuàng)建進程巾陕,這是在利用進程的API讨跟。進程創(chuàng)建的具體過程如下:OS首先找到磁盤上程序的位置,將其代碼段和靜態(tài)數(shù)據(jù)加載到內(nèi)存中鄙煤,這個加載過程被稱為惰性加載晾匠,即程序執(zhí)行時需要加載的代碼才會加載。同時為程序運行棧和堆分配內(nèi)存梯刚,后者需要通過顯示請求如malloc()來完成凉馆,并執(zhí)行與I/O設(shè)置相關(guān)的操作。這一切完成之后,OS將從main()函數(shù)開始執(zhí)行澜共,進程獲得CPU的獨占權(quán)向叉。
等待
因為無法判斷父進程和子進程誰先返回,但有時候父進程需要等待子進程先完成嗦董,這時父進程就用wait()調(diào)用來控制母谎,OS將CPU的使用權(quán)移交給子進程。銷毀
其他控制
狀態(tài)
3.4進程狀態(tài)
既然進程是動態(tài)運行的京革,它肯定也逃不過“生老病死”奇唤。具體來說,進程主要有以下幾種狀態(tài)匹摇。
狀態(tài) | 說明 |
---|---|
運行 running | 進程占用CPU咬扇,執(zhí)行指令 |
就緒 ready | 進程已準(zhǔn)備好占用CPU,由于某些原因OS暫時沒有給予它使用權(quán) |
阻塞 blocked | 進程在進行另外的操作廊勃,無需使用CPU的一種狀態(tài)懈贺,此時CPU的使用權(quán)不能分配給它,以免造成資源浪費坡垫。常見于進程進行I/O操作時梭灿,在完成此操作后,進程才會轉(zhuǎn)為就緒狀態(tài)葛虐,擁有使用CPU的資格 |
3.5 數(shù)據(jù)結(jié)構(gòu)
操作系統(tǒng)抽象出多種概念以保障其平穩(wěn)高效地運行胎源。這些“抽象”的具體實現(xiàn)通常是c語言中定義的一個結(jié)構(gòu)體棉钧,即一個數(shù)據(jù)結(jié)構(gòu)屿脐,其中包含了各種變量和指針。
進程在具體實現(xiàn)時作為一種數(shù)據(jù)結(jié)構(gòu)宪卿,主要包含了下列信息:
- 寄存器狀態(tài)(上下文信息)
- 進程自身狀態(tài)(枚舉變量)
- 進程內(nèi)存分配起始地址和大小
- 內(nèi)核棧的起始地址
- 進程ID
- 指向父進程的指針
- 指向中斷結(jié)構(gòu)的指針
- 指向當(dāng)前打開文件和目錄的指針
4.總結(jié)
OS離不開虛擬化的诵,而虛擬化離不開對進程的處理,上文對進程的各項信息做了一個簡單的介紹佑钾。在此基礎(chǔ)上西疤,下篇筆記將要對虛擬化的具體實現(xiàn)——低級機制(上下文切換)和高級策略(進程調(diào)度策略)進行一個全面的盤點。