聊一聊操作系統(tǒng)八股文背誦版
備受期待的操作系統(tǒng)八股文來啦!
八股文在線網(wǎng)址: http://interviewtop.top
求大家給我們的八股文項目github來個贊!https://github.com/autoencoder-github/interviewtop
什么是操作系統(tǒng)?請簡要概述一下
操作系統(tǒng)是管理計算機硬件和軟件資源的計算機程序,提供一個計算機用戶與計算機硬件系統(tǒng)之間的接口。
向上對用戶程序提供接口,向下接管硬件資源。
操作系統(tǒng)本質(zhì)上也是一個軟件,作為最接近硬件的系統(tǒng)軟件,負責(zé)處理器管理、存儲器管理、設(shè)備管理、文件管理和提供用戶接口。
操作系統(tǒng)有哪些分類?
操作系統(tǒng)常規(guī)可分為批處理操作系統(tǒng)、分時操作系統(tǒng)、實時操作系統(tǒng)。
若一個操作系統(tǒng)兼顧批操作和分時的功能,則稱該系統(tǒng)為通用操作系統(tǒng)。
常見的通用操作系統(tǒng)有:Windows、Linux、MacOS等。
什么是內(nèi)核態(tài)和用戶態(tài)?
為了避免操作系統(tǒng)和關(guān)鍵數(shù)據(jù)被用戶程序破壞,將處理器的執(zhí)行狀態(tài)分為內(nèi)核態(tài)和用戶態(tài)。
內(nèi)核態(tài)是操作系統(tǒng)管理程序執(zhí)行時所處的狀態(tài),能夠執(zhí)行包含特權(quán)指令在內(nèi)的一切指令,能夠訪問系統(tǒng)內(nèi)所有的存儲空間。
用戶態(tài)是用戶程序執(zhí)行時處理器所處的狀態(tài),不能執(zhí)行特權(quán)指令,只能訪問用戶地址空間。
用戶程序運行在用戶態(tài),操作系統(tǒng)內(nèi)核運行在內(nèi)核態(tài)。
如何實現(xiàn)內(nèi)核態(tài)和用戶態(tài)的切換?
處理器從用戶態(tài)切換到內(nèi)核態(tài)的方法有三種:系統(tǒng)調(diào)用、異常和外部中斷。
- 系統(tǒng)調(diào)用是操作系統(tǒng)的最小功能單位,是操作系統(tǒng)提供的用戶接口,系統(tǒng)調(diào)用本身是一種軟中斷。
- 異常,也叫做內(nèi)中斷,是由錯誤引起的,如文件損壞、缺頁故障等。
- 外部中斷,是通過兩根信號線來通知處理器外設(shè)的狀態(tài)變化,是硬中斷。
并發(fā)和并行的區(qū)別
- 并發(fā)(concurrency):指宏觀上看起來兩個程序在同時運行,比如說在單核cpu上的多任務(wù)。但是從微觀上看兩個程序的指令是交織著運行的,指令之間交錯執(zhí)行,在單個周期內(nèi)只運行了一個指令。這種并發(fā)并不能提高計算機的性能,只能提高效率(如降低某個進程的相應(yīng)時間)。
- 并行(parallelism):指嚴(yán)格物理意義上的同時運行,比如多核cpu,兩個程序分別運行在兩個核上,兩者之間互不影響,單個周期內(nèi)每個程序都運行了自己的指令,也就是運行了兩條指令。這樣說來并行的確提高了計算機的效率。所以現(xiàn)在的cpu都是往多核方面發(fā)展。
什么是進程?
進程是操作系統(tǒng)中最重要的抽象概念之一,是資源分配的基本單位,是獨立運行的基本單位。
進程的經(jīng)典定義就是一個執(zhí)行中程序的實例。系統(tǒng)中的每個程序都運行在某個進程的上下文(context)中。
上下文是由程序正確運行所需的狀態(tài)組成的。這個狀態(tài)包括存放在內(nèi)存中的程序的代碼和數(shù)據(jù),它的棧、通用目的寄存器的內(nèi)容、程序計數(shù)器、環(huán)境變量以及打開文件描述符的集合。
進程一般由以下的部分組成:
- 進程控制塊PCB,是進程存在的唯一標(biāo)志,包含進程標(biāo)識符PID,進程當(dāng)前狀態(tài),程序和數(shù)據(jù)地址,進程優(yōu)先級、CPU現(xiàn)場保護區(qū)(用于進程切換),占有的資源清單等。
- 程序段
- 數(shù)據(jù)段
進程的基本操作
以Unix系統(tǒng)舉例:
進程的創(chuàng)建:fork()。新創(chuàng)建的子進程幾乎但不完全與父進程相同。子進程得到與父進程用戶級虛擬地址空間相同的(但是獨立的)一份副本,包括代碼和數(shù)據(jù)段、堆、共享庫以及用戶棧。子進程還獲得與父進程任何打開文件描述符相同的副本,這就意味著當(dāng)父進程調(diào)用 fork 時,子進程可以讀寫父進程中打開的任何文件。父進程和新創(chuàng)建的子進程之間最大的區(qū)別在于它們有不同的 PID。fork函數(shù)是有趣的(也常常令人迷惑), 因為它只被調(diào)用一次,卻會返回兩次:一次是在調(diào)用進程(父進程)中,一次是在新創(chuàng)建的子進程中。在父進程中,fork 返回子進程的 PID。在子進程中,fork 返回 0。因為子進程的 PID 總是為非零,返回值就提供一個明 確的方法來分辨程序是在父進程還是在子進程中執(zhí)行。
- pid_t fork(void);
回收子進程:當(dāng)一個進程由于某種原因終止時,內(nèi)核并不是立即把它從系統(tǒng)中清除。相反,進程被保持在一種已終止的狀態(tài)中,直到被它的父進程回收(reaped)。當(dāng)父進程回收已終止的子進程時,內(nèi)核將子進程的退出狀態(tài)傳遞給父進程,然后拋棄已終止的進程。一個進程可以通過調(diào)用 waitpid 函數(shù)來等待它的子進程終止或者停止。
- pid_t waitpid(pid_t pid, int *statusp, int options);
加載并運行程序:execve 函數(shù)在當(dāng)前進程的上下文中加載并運行一個新程序。
- int execve(const char *filename, const char *argv[], const char *envp[]);
進程終止:
- void exit(int status);
簡述進程間通信方法
每個進程各自有不同的用戶地址空間,任何一個進程的全局變量在另一個進程中都看不到,所以進程之間要交換數(shù)據(jù)必須通過內(nèi)核,在內(nèi)核中開辟一塊緩沖區(qū),進程A把數(shù)據(jù)從用戶空間拷到內(nèi)核緩沖區(qū),進程B再從內(nèi)核緩沖區(qū)把數(shù)據(jù)讀走,內(nèi)核提供的這種機制稱為進程間通信。
不同進程間的通信本質(zhì):進程之間可以看到一份公共資源;而提供這份資源的形式或者提供者不同,造成了通信方式不同。
進程間通信主要包括管道、系統(tǒng)IPC(包括消息隊列、信號量、信號、共享內(nèi)存等)、以及套接字socket。
進程如何通過管道進行通信
管道是一種最基本的IPC機制,作用于有血緣關(guān)系的進程之間,完成數(shù)據(jù)傳遞。調(diào)用pipe系統(tǒng)函數(shù)即可創(chuàng)建一個管道。有如下特質(zhì):
- 其本質(zhì)是一個偽文件(實為內(nèi)核緩沖區(qū))
- 由兩個文件描述符引用,一個表示讀端,一個表示寫端。
- 規(guī)定數(shù)據(jù)從管道的寫端流入管道,從讀端流出。
管道的原理: 管道實為內(nèi)核使用環(huán)形隊列機制,借助內(nèi)核緩沖區(qū)實現(xiàn)。
管道的局限性:
- 數(shù)據(jù)自己讀不能自己寫。
- 數(shù)據(jù)一旦被讀走,便不在管道中存在,不可反復(fù)讀取。
- 由于管道采用半雙工通信方式。因此,數(shù)據(jù)只能在一個方向上流動。
- 只能在有公共祖先的進程間使用管道。
進程如何通過共享內(nèi)存通信?
它使得多個進程可以訪問同一塊內(nèi)存空間,不同進程可以及時看到對方進程中對共享內(nèi)存中數(shù)據(jù)得更新。這種方式需要依靠某種同步操作,如互斥鎖和信號量等。
特點:
- 共享內(nèi)存是最快的一種IPC,因為進程是直接對內(nèi)存進行操作來實現(xiàn)通信,避免了數(shù)據(jù)在用戶空間和內(nèi)核空間來回拷貝。
- 因為多個進程可以同時操作,所以需要進行同步處理。
- 信號量和共享內(nèi)存通常結(jié)合在一起使用,信號量用來同步對共享內(nèi)存的訪問。
什么是信號
一個信號就是一條小消息,它通知進程系統(tǒng)中發(fā)生了一個某種類型的事件。Linux 系統(tǒng)上支持的30 種不同類型的信號。每種信號類型都對應(yīng)于某種系統(tǒng)事件。低層的硬件異常是由內(nèi)核異常處理程序處理的,正常情況下,對用戶進程而言是不可見的。信號提供了一種機制,通知用戶進程發(fā)生了這些異常。
發(fā)送信號:內(nèi)核通過更新目的進程上下文中的某個狀態(tài),發(fā)送(遞送)一個信號給目的進程。發(fā)送信號可以有如下兩種原因:
- 內(nèi)核檢測到一個系統(tǒng)事件,比如除零錯誤或者子進程終止。
- —個進程調(diào)用了kill 函數(shù), 顯式地要求內(nèi)核發(fā)送一個信號給目的進程。一個進程可以發(fā)送信號給它自己。
接收信號:當(dāng)目的進程被內(nèi)核強迫以某種方式對信號的發(fā)送做出反應(yīng)時,它就接收了信號。進程可以忽略這個信號,終止或者通過執(zhí)行一個稱為信號處理程序(signal handler)的用戶層函數(shù)捕獲這個信號。
如何編寫正確且安全的信號處理函數(shù)
1.處理程序要盡可能簡單。避免麻煩的最好方法是保持處理程序盡可能小和簡單。例如,處理程序可能只是簡單地設(shè)置全局標(biāo)志并立即返回;所有與接收信號相關(guān)的處理都由主程序執(zhí)行,它周期性地檢查(并重置)這個標(biāo)志。
2.在處理程序中只調(diào)用異步信號安全的函數(shù)。所謂異步信號安全的函數(shù)(或簡稱安全的函數(shù))能夠被信號處理程序安全地調(diào)用,原因有二:要么它是可重入的(例如只訪問局部變量),要么它不能被信號處理程序中斷。
3.保存和恢復(fù)errno。許多Linux 異步信號安全的函數(shù)都會在出錯返回時設(shè)置errno在處理程序中調(diào)用這樣的函數(shù)可能會干擾主程序中其他依賴于分。解決方法是在進人處理程序時把errno 保存在一個局部變量中,在處理程序返回前恢復(fù)它。注意,只有在處理程序要返回時才有此必要。如果處理程序調(diào)用_exit終止該進程,那么就不需要這樣做了。
4.阻塞所有的信號,保護對共享全局?jǐn)?shù)據(jù)結(jié)構(gòu)的訪問。如果處理程序和主程序或其他處理程序共享一個全局?jǐn)?shù)據(jù)結(jié)構(gòu),那么在訪問(讀或者寫)該數(shù)據(jù)結(jié)構(gòu)時,你的處理程序和主程序應(yīng)該暫時阻塞所有的信號。這條規(guī)則的原因是從主程序訪問一個數(shù)據(jù)結(jié)構(gòu)d 通常需要一系列的指令,如果指令序列被訪問d 的處理程序中斷,那么處理程序可能會發(fā)現(xiàn)d 的狀態(tài)不一致,得到不可預(yù)知的結(jié)果。在訪問d 時暫時阻塞信號保證了處理程序不會中斷該指令序列。
5.用volatile 聲明全局變量??紤]一個處理程序和一個main 函數(shù),它們共享一個全局變量g 。處理程序更新g,main 周期性地讀g, 對于一個優(yōu)化編譯器而言,main 中g(shù)的值看上去從來沒有變化過,因此使用緩存在寄存器中g(shù) 的副本來滿足對g 的每次引用是很安全的。如果這樣,main 函數(shù)可能永遠都無法看到處理程序更新過的值。可以用volatile 類型限定符來定義一個變量,告訴編譯器不要緩存這個變量。例如:volatile 限定符強迫編譯器毎次在代碼中引用g時,都要從內(nèi)存中讀取g的值。一般來說,和其他所有共享數(shù)據(jù)結(jié)構(gòu)一樣,應(yīng)該暫時阻塞信號,保護每次對全局變量的訪問。
- volatile int g;
6.用sig_atomic_t聲明標(biāo)志。在常見的處理程序設(shè)計中,處理程序會寫全局標(biāo)志來記錄收到了信號。主程序周期性地讀這個標(biāo)志,響應(yīng)信號,再清除該標(biāo)志。對于通過這種方式來共享的標(biāo)志,C 提供一種整型數(shù)據(jù)類型sig_atomic_t對它的讀和寫保證會是原子的(不可中斷的)。
7.信號的一個與直覺不符的方面是未處理的信號是不排隊的。因為 pending 位向量中每種類型的信號只對應(yīng)有一位,所以每種類型最多只能有一個未處理的信號。關(guān)鍵思想是如果存在一個未處理的信號就表明至少有一個信號到達了。
進程調(diào)度的時機
- 當(dāng)前運行的進程運行結(jié)束。
- 當(dāng)前運行的進程由于某種原因阻塞。
- 執(zhí)行完系統(tǒng)調(diào)用等系統(tǒng)程序后返回用戶進程。
- 在使用搶占調(diào)度的系統(tǒng)中,具有更高優(yōu)先級的進程就緒時。
- 分時系統(tǒng)中,分給當(dāng)前進程的時間片用完。
不能進行進程調(diào)度的情況
- 在中斷處理程序執(zhí)行時。
- 在操作系統(tǒng)的內(nèi)核程序臨界區(qū)內(nèi)。
- 其它需要完全屏蔽中斷的原子操作過程中。
進程的調(diào)度策略
- 先到先服務(wù)調(diào)度算法
- 短作業(yè)優(yōu)先調(diào)度算法
- 優(yōu)先級調(diào)度算法
- 時間片輪轉(zhuǎn)調(diào)度算法
- 高響應(yīng)比優(yōu)先調(diào)度算法
- 多級隊列調(diào)度算法
- 多級反饋隊列調(diào)度算法
進程調(diào)度策略的基本設(shè)計指標(biāo)
- CPU利用率
- 系統(tǒng)吞吐率,即單位時間內(nèi)CPU完成的作業(yè)的數(shù)量。
- 響應(yīng)時間。
- 周轉(zhuǎn)時間。是指作業(yè)從提交到完成的時間間隔。從每個作業(yè)的角度看,完成每個作業(yè)的時間也是很關(guān)鍵
- 平均周轉(zhuǎn)時間
- 帶權(quán)周轉(zhuǎn)時間
- 平均帶權(quán)周轉(zhuǎn)時間
進程的狀態(tài)與狀態(tài)轉(zhuǎn)換
進程在運行時有三種基本狀態(tài):就緒態(tài)、運行態(tài)和阻塞態(tài)。
1.運行(running)態(tài):進程占有處理器正在運行的狀態(tài)。進程已獲得CPU,其程序正在執(zhí)行。在單處理機系統(tǒng)中,只有一個進程處于執(zhí)行狀態(tài);在多處理機系統(tǒng)中,則有多個進程處于執(zhí)行狀態(tài)。
2.就緒(ready)態(tài):進程具備運行條件,等待系統(tǒng)分配處理器以便運行的狀態(tài)。 當(dāng)進程已分配到除CPU以外的所有必要資源后,只要再獲得CPU,便可立即執(zhí)行,進程這時的狀態(tài)稱為就緒狀態(tài)。在一個系統(tǒng)中處于就緒狀態(tài)的進程可能有多個,通常將它們排成一個隊列,稱為就緒隊列。
3.阻塞(wait)態(tài):又稱等待態(tài)或睡眠態(tài),指進程不具備運行條件,正在等待某個時間完成的狀態(tài)。
各狀態(tài)之間的轉(zhuǎn)換:
就緒→執(zhí)行 處于就緒狀態(tài)的進程,當(dāng)進程調(diào)度程序為之分配了處理機后,該進程便由就緒狀態(tài)轉(zhuǎn)變成執(zhí)行狀態(tài)。
執(zhí)行→就緒 處于執(zhí)行狀態(tài)的進程在其執(zhí)行過程中,因分配給它的一個時間片已用完而不得不讓出處理機,于是進程從執(zhí)行狀態(tài)轉(zhuǎn)變成就緒狀態(tài)。
執(zhí)行→阻塞 正在執(zhí)行的進程因等待某種事件發(fā)生而無法繼續(xù)執(zhí)行時,便從執(zhí)行狀態(tài)變成阻塞狀態(tài)。
阻塞→就緒 處于阻塞狀態(tài)的進程,若其等待的事件已經(jīng)發(fā)生,于是進程由阻塞狀態(tài)轉(zhuǎn)變?yōu)榫途w狀態(tài)。
什么是孤兒進程?僵尸進程?
1.孤兒進程:父進程退出,子進程還在運行的這些子進程都是孤兒進程,孤兒進程將被init進程(1號進程)所收養(yǎng),并由init進程對他們完成狀態(tài)收集工作。
2.僵尸進程:進程使用fork創(chuàng)建子進程,如果子進程退出,而父進程并沒有調(diào)用wait 獲waitpid 獲取子進程的狀態(tài)信息,那么子進程的進程描述符仍然保存在系統(tǒng)中的這些進程是僵尸進程。
什么是線程?
- 是進程劃分的任務(wù),是一個進程內(nèi)可調(diào)度的實體,是CPU調(diào)度的基本單位,用于保證程序的實時性,實現(xiàn)進程內(nèi)部的并發(fā)。
- 線程是操作系統(tǒng)可識別的最小執(zhí)行和調(diào)度單位。每個線程都獨自占用一個虛擬處理器:獨自的寄存器組,指令計數(shù)器和處理器狀態(tài)。
- 每個線程完成不同的任務(wù),但是屬于同一個進程的不同線程之間共享同一地址空間(也就是同樣的動態(tài)內(nèi)存,映射文件,目標(biāo)代碼等等),打開的文件隊列和其他內(nèi)核資源。
為什么需要線程?
線程產(chǎn)生的原因:進程可以使多個程序能并發(fā)執(zhí)行,以提高資源的利用率和系統(tǒng)的吞吐量;但是其具有一些缺點:
- 進程在同一時刻只能做一個任務(wù),很多時候不能充分利用CPU資源。
- 進程在執(zhí)行的過程中如果發(fā)生阻塞,整個進程就會掛起,即使進程中其它任務(wù)不依賴于等待的資源,進程仍會被阻塞。
引入線程就是為了解決以上進程的不足,線程具有以下的優(yōu)點:
- 從資源上來講,開辟一個線程所需要的資源要遠小于一個進程。
- 從切換效率上來講,運行于一個進程中的多個線程,它們之間使用相同的地址空間,而且線程間彼此切換所需時間也遠遠小于進程間切換所需要的時間(這種時間的差異主要由于緩存的大量未命中導(dǎo)致)。
- 從通信機制上來講,線程間方便的通信機制。對不同進程來說,它們具有獨立的地址空間,要進行數(shù)據(jù)的傳遞只能通過進程間通信的方式進行。線程則不然,屬于同一個進程的不同線程之間共享同一地址空間,所以一個線程的數(shù)據(jù)可以被其它線程感知,線程間可以直接讀寫進程數(shù)據(jù)段(如全局變量)來進行通信(需要一些同步措施)。
簡述線程和進程的區(qū)別和聯(lián)系
- 一個線程只能屬于一個進程,而一個進程可以有多個線程,但至少有一個線程。線程依賴于進程而存在。
- 進程在執(zhí)行過程中擁有獨立的地址空間,而多個線程共享進程的地址空間。(資源分配給進程,同一進程的所有線程共享該進程的所有資源。同一進程中的多個線程共享代碼段(代碼和常量),數(shù)據(jù)段(全局變量和靜態(tài)變量),擴展段(堆存儲)。但是每個線程擁有自己的棧段,棧段又叫運行時段,用來存放所有局部變量和臨時變量。)
- 進程是資源分配的最小單位,線程是CPU調(diào)度的最小單位。
- 通信:由于同一進程中的多個線程具有相同的地址空間,使它們之間的同步和通信的實現(xiàn),也變得比較容易。進程間通信IPC,線程間可以直接讀寫進程數(shù)據(jù)段(如全局變量)來進行通信(需要一些同步方法,以保證數(shù)據(jù)的一致性)。
- 進程編程調(diào)試簡單可靠性高,但是創(chuàng)建銷毀開銷大;線程正相反,開銷小,切換速度快,但是編程調(diào)試相對復(fù)雜。
- 進程間不會相互影響;一個進程內(nèi)某個線程掛掉將導(dǎo)致整個進程掛掉。
- 進程適應(yīng)于多核、多機分布;線程適用于多核。
進程和線程的基本API
進程API以Unix系統(tǒng)為例,線程相關(guān)的API屬于Posix線程(Pthreads)標(biāo)準(zhǔn)接口。
進程原語 | 線程原語 | 描述 |
---|---|---|
fork |
pthread_create |
創(chuàng)建新的控制流 |
exit |
pthread_exit |
從現(xiàn)有的控制流中退出 |
waitpid |
pthread_join |
從控制流中得到退出狀態(tài) |
atexit |
pthread_cancel_push |
注冊在退出控制流時調(diào)用的函數(shù) |
getpid |
pthread_self |
獲取控制流的ID |
abort |
pthread_cancel |
請求控制流的非正常退出 |
多線程模型
- 多對一模型。將多個用戶級線程映射到一個內(nèi)核級線程上。該模型下,線程在用戶空間進行管理,效率較高。缺點就是一個線程阻塞,整個進程內(nèi)的所有線程都會阻塞。幾乎沒有系統(tǒng)繼續(xù)使用這個模型。
- 一對一模型。將內(nèi)核線程與用戶線程一一對應(yīng)。優(yōu)點是一個線程阻塞時,不會影響到其它線程的執(zhí)行。該模型具有更好的并發(fā)性。缺點是內(nèi)核線程數(shù)量一般有上限,會限制用戶線程的數(shù)量。更多的內(nèi)核線程數(shù)目也給線程切換帶來額外的負擔(dān)。linux和Windows操作系統(tǒng)家族都是使用一對一模型。
- 多對多模型。將多個用戶級線程映射到多個內(nèi)核級線程上。結(jié)合了多對一模型和一對一模型的特點。
進程同步的方法
操作系統(tǒng)中,進程是具有不同的地址空間的,兩個進程是不能感知到對方的存在的。有時候,需要多個進程來協(xié)同完成一些任務(wù)。當(dāng)多個進程需要對同一個內(nèi)核資源進行操作時,這些進程便是競爭的關(guān)系,操作系統(tǒng)必須協(xié)調(diào)各個進程對資源的占用,進程的互斥是解決進程間競爭關(guān)系的方法。進程互斥指若干個進程要使用同一共享資源時,任何時刻最多允許一個進程去使用,其他要使用該資源的進程必須等待,直到占有資源的進程釋放該資源。當(dāng)多個進程協(xié)同完成一些任務(wù)時,不同進程的執(zhí)行進度不一致,這便產(chǎn)生了進程的同步問題。需要操作系統(tǒng)干預(yù),在特定的同步點對所有進程進行同步,這種協(xié)作進程之間相互等待對方消息或信號的協(xié)調(diào)關(guān)系稱為進程同步。進程互斥本質(zhì)上也是一種進程同步。進程的同步方法:
- 互斥鎖
- 讀寫鎖
- 條件變量
- 記錄鎖(record locking)
- 信號量
- 屏障(barrier)
線程同步的方法
操作系統(tǒng)中,屬于同一進程的線程之間具有相同的地址空間,線程之間共享數(shù)據(jù)變得簡單高效。遇到競爭的線程同時修改同一數(shù)據(jù)或是協(xié)作的線程設(shè)置同步點的問題時,需要使用一些線程同步的方法來解決這些問題。
線程同步的方法:
- 互斥鎖
- 讀寫鎖
- 條件變量
- 信號量
- 自旋鎖
- 屏障(barrier)
進程同步與線程同步有什么區(qū)別
進程之間地址空間不同,不能感知對方的存在,同步時需要將鎖放在多進程共享的空間。而線程之間共享同一地址空間,同步時把鎖放在所屬的同一進程空間即可。
死鎖是怎樣產(chǎn)生的?
死鎖是指兩個或兩個以上進程在執(zhí)行過程中,因爭奪資源而造成的下相互等待的現(xiàn)象。產(chǎn)生死鎖需要滿足下面四個條件:
- 互斥條件:進程對所分配到的資源不允許其他進程訪問,若其他進程訪問該資源,只能等待,直至占有該資源的進程使用完成后釋放該資源。
- 占有并等待條件:進程獲得一定的資源后,又對其他資源發(fā)出請求,但是該資源可能被其他進程占有,此時請求阻塞,但該進程不會釋放自己已經(jīng)占有的資源。
- 非搶占條件:進程已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用后自己釋放。
- 循環(huán)等待條件:進程發(fā)生死鎖后,必然存在一個進程-資源之間的環(huán)形鏈。
如何解決死鎖問題?
解決死鎖的方法即破壞產(chǎn)生死鎖的四個必要條件之一,主要方法如下:
- 資源一次性分配,這樣就不會再有請求了(破壞請求條件)。
- 只要有一個資源得不到分配,也不給這個進程分配其他的資源(破壞占有并等待條件)。
- 可搶占資源:即當(dāng)進程新的資源未得到滿足時,釋放已占有的資源,從而破壞不可搶占的條件。
- 資源有序分配法:系統(tǒng)給每類資源賦予一個序號,每個進程按編號遞增的請求資源,釋放則相反,從而破壞環(huán)路等待的條件
什么是虛擬地址,什么是物理地址?
地址空間是一個非負整數(shù)地址的有序集合。
在一個帶虛擬內(nèi)存的系統(tǒng)中,CPU 從一個有N=pow(2,n)個地址的地址空間中生成虛擬地址,這個地址空間稱為虛擬地址空間(virtual address space),現(xiàn)代系統(tǒng)通常支持 32 位或者 64 位虛擬地址空間。
一個系統(tǒng)還有一個物理地址空間(physical address space),對應(yīng)于系統(tǒng)中物理內(nèi)存的M 個字節(jié)。
地址空間的概念是很重要的,因為它清楚地區(qū)分了數(shù)據(jù)對象(字節(jié))和它們的屬性(地址)。
一旦認(rèn)識到了這種區(qū)別,那么我們就可以將其推廣,允許每個數(shù)據(jù)對象有多個獨立的地址,其中每個地址都選自一個不同的地址空間。這就是虛擬內(nèi)存的基本思想。
主存中的每字節(jié)都有一個選自虛擬地址空間的虛擬地址和一個選自物理地址空間的物理地址。
什么是虛擬內(nèi)存?
為了更加有效地管理內(nèi)存并且少出錯,現(xiàn)代系統(tǒng)提供了一種對主存的抽象概念,叫做虛擬內(nèi)存(VM)。虛擬內(nèi)存是硬件異常、硬件地址翻譯、主存、磁盤文件和內(nèi)核軟件的完美交互,它為每個進程提供了一個大的、一致的和私有的地址空間。通過一個很清晰的機制,虛擬內(nèi)存提供了三個重要的能力:
- 它將主存看成是一個存儲在磁盤上的地址空間的高速緩存,在主存中只保存活動區(qū)域,并根據(jù)需要在磁盤和主存之間來回傳送數(shù)據(jù),通過這種方式,它高效地使用了主存。
- 它為每個進程提供了一致的地址空間,從而簡化了內(nèi)存管理。
- 它保護了每個進程的地址空間不被其他進程破壞。
為什么要引入虛擬內(nèi)存?
虛擬內(nèi)存作為緩存的工具
- 虛擬內(nèi)存被組織為一個由存放在磁盤上的N個連續(xù)的字節(jié)大小的單元組成的數(shù)組。
- 虛擬內(nèi)存利用DRAM緩存來自通常更大的虛擬地址空間的頁面。
虛擬內(nèi)存作為內(nèi)存管理的工具。操作系統(tǒng)為每個進程提供了一個獨立的頁表,也就是獨立的虛擬地址空間。多個虛擬頁面可以映射到同一個物理頁面上。
- 一般:每個進程有各自私有的代碼,數(shù)據(jù),堆棧,是不和其他進程共享的,這樣OS創(chuàng)建頁表,將虛擬頁映射到不連續(xù)的物理頁面。
- 某些情況下,需要進程來共享代碼和數(shù)據(jù)。例如每個進程調(diào)用相同的操作系統(tǒng)內(nèi)核代碼,或者C標(biāo)準(zhǔn)庫函數(shù)。OS會把不同進程中適當(dāng)?shù)奶摂M頁面映射到相同的物理頁面。
- 加載器從不在磁盤到內(nèi)存實際復(fù)制任何數(shù)據(jù),在每個頁初次被引用時,虛擬內(nèi)存系統(tǒng)會按照需要自動的調(diào)入數(shù)據(jù)頁。
- 例如:一個給定的linux系統(tǒng)上的每個進程都是用類似的內(nèi)存格式,對于64為地址空間,代碼段總是從虛擬地址)0x400000開始,數(shù)據(jù)段,代碼段,棧,堆等等。
- 簡化鏈接: 獨立的地址空間允許每個進程的內(nèi)存映像使用相同的基本格式,而不管代碼和數(shù)據(jù)實際存放在物理內(nèi)存的何處。
- 簡化加載: 虛擬內(nèi)存還使得容易向內(nèi)存中加載可執(zhí)行文件和共享對象文件。要把目標(biāo)文件中.text和.data節(jié)加載到一個新創(chuàng)建的進程中,Linux加載器為代碼和數(shù)據(jù)段分配虛擬頁VP,把他們標(biāo)記為無效(未被緩存) ,將頁表條目指向目標(biāo)文件的起始位置。
- 簡化共享: 獨立地址空間為OS提供了一個管理用戶進程和操作系統(tǒng)自身之間共享的一致機制。
- 簡化內(nèi)存分配: 虛擬內(nèi)存向用戶提供一個簡單的分配額外內(nèi)存的機制。當(dāng)一個運行在用戶進程中的程序要求額外的堆空間時(如malloc),OS分配一個適當(dāng)k大小個連續(xù)的虛擬內(nèi)存頁面,并且將他們映射到物理內(nèi)存中任意位置的k個任意物理頁面,因此操作系統(tǒng)沒有必要分配k個連續(xù)的物理內(nèi)存頁面,頁面可以隨機的分散在物理內(nèi)存中。
虛擬內(nèi)存作為內(nèi)存保護的工具。不應(yīng)該允許一個用戶進程修改它的只讀段,也不允許它修改任何內(nèi)核代碼和數(shù)據(jù)結(jié)構(gòu),不允許讀寫其他進程的私有內(nèi)存,不允許修改任何與其他進程共享的虛擬頁面。每次CPU生成一個地址時,MMU會讀一個PTE,通過在PTE上添加一些額外的許可位來控制對一個虛擬頁面內(nèi)容的訪問十分簡單。
常見的頁面置換算法
當(dāng)訪問一個內(nèi)存中不存在的頁,并且內(nèi)存已滿,則需要從內(nèi)存中調(diào)出一個頁或?qū)?shù)據(jù)送至磁盤對換區(qū),替換一個頁,這種現(xiàn)象叫做缺頁置換。當(dāng)前操作系統(tǒng)最常采用的缺頁置換算法如下:
- 先進先出(FIFO)算法:
- 思路:置換最先調(diào)入內(nèi)存的頁面,即置換在內(nèi)存中駐留時間最久的頁面。
- 實現(xiàn):按照進入內(nèi)存的先后次序排列成隊列,從隊尾進入,從隊首刪除。
- 特點:實現(xiàn)簡單;性能較差,調(diào)出的頁面可能是經(jīng)常訪問的
- 最近最少使用(LRU)算法:
- 思路:置換最近一段時間以來最長時間未訪問過的頁面。根據(jù)程序局部性原理,剛被訪問的頁面,可能馬上又要被訪問;而較長時間內(nèi)沒有被訪問的頁面,可能最近不會被訪問。
- 實現(xiàn):缺頁時,計算內(nèi)存中每個邏輯頁面的上一次訪問時間,選擇上一次使用到當(dāng)前時間最長的頁面
- 特點:可能達到最優(yōu)的效果,維護這樣的訪問鏈表開銷比較大
當(dāng)前最常采用的就是LRU算法。
- 最不常用算法(Least Frequently Used, LFU)
- 思路:缺頁時,置換訪問次數(shù)最少的頁面
- 實現(xiàn):每個頁面設(shè)置一個訪問計數(shù),訪問頁面時,訪問計數(shù)加1,缺頁時,置換計數(shù)最小的頁面
- 特點:算法開銷大,開始時頻繁使用,但以后不使用的頁面很難置換
請說一下什么是寫時復(fù)制?
- 如果有多個進程要讀取它們自己的那部門資源的副本,那么復(fù)制是不必要的。每個進程只要保存一個指向這個資源的指針就可以了。只要沒有進程要去修改自己的“副本”,就存在著這樣的幻覺:每個進程好像獨占那個資源。從而就避免了復(fù)制帶來的負擔(dān)。如果一個進程要修改自己的那份資源“副本”,那么就會復(fù)制那份資源,并把復(fù)制的那份提供給進程。不過其中的復(fù)制對進程來說是透明的。這個進程就可以修改復(fù)制后的資源了,同時其他的進程仍然共享那份沒有修改過的資源。所以這就是名稱的由來:在寫入時進行復(fù)制。
- 寫時復(fù)制的主要好處在于:如果進程從來就不需要修改資源,則不需要進行復(fù)制。惰性算法的好處就在于它們盡量推遲代價高昂的操作,直到必要的時刻才會去執(zhí)行。
- 在使用虛擬內(nèi)存的情況下,寫時復(fù)制(Copy-On-Write)是以頁為基礎(chǔ)進行的。所以,只要進程不修改它全部的地址空間,那么就不必復(fù)制整個地址空間。在fork()調(diào)用結(jié)束后,父進程和子進程都相信它們有一個自己的地址空間,但實際上它們共享父進程的原始頁,接下來這些頁又可以被其他的父進程或子進程共享。
實時操作系統(tǒng)的概念
實時操作系統(tǒng)(Real-time operating system, RTOS),又稱即時操作系統(tǒng),它會按照排序運行、管理系統(tǒng)資源,并為開發(fā)應(yīng)用程序提供一致的基礎(chǔ)。實時操作系統(tǒng)與一般的操作系統(tǒng)相比,最大的特色就是“實時性”,如果有一個任務(wù)需要執(zhí)行,實時操作系統(tǒng)會馬上(在較短時間內(nèi))執(zhí)行該任務(wù),不會有較長的延時。這種特性保證了各個任務(wù)的及時執(zhí)行。
優(yōu)先級反轉(zhuǎn)是什么?如何解決
由于多進程共享資源,具有最高優(yōu)先權(quán)的進程被低優(yōu)先級進程阻塞,反而使具有中優(yōu)先級的進程先于高優(yōu)先級的進程執(zhí)行,導(dǎo)致系統(tǒng)的崩潰。這就是所謂的優(yōu)先級反轉(zhuǎn)(Priority Inversion)。其實,優(yōu)先級反轉(zhuǎn)是在高優(yōu)級(假設(shè)為A)的任務(wù)要訪問一個被低優(yōu)先級任務(wù)(假設(shè)為C)占有的資源時,被阻塞.而此時又有優(yōu)先級高于占有資源的任務(wù)(C)而低于被阻塞的任務(wù)(A)的優(yōu)先級的任務(wù)(假設(shè)為B)時,于是,占有資源的任務(wù)就被掛起(占有的資源仍為它占有),因為占有資源的任務(wù)優(yōu)先級很低,所以,它可能一直被另外的任務(wù)掛起.而它占有的資源也就一直不能釋放,這樣,引起任務(wù)A一直沒辦法執(zhí)行.而比它優(yōu)先低的任務(wù)卻可以執(zhí)行。
目前解決優(yōu)先級反轉(zhuǎn)有許多種方法。其中普遍使用的有2種方法:一種被稱作優(yōu)先級繼承(priority inheritance);另一種被稱作優(yōu)先級極限(priority ceilings)。
- 優(yōu)先級繼承(priority inheritance) 優(yōu)先級繼承是指將低優(yōu)先級任務(wù)的優(yōu)先級提升到等待它所占有的資源的最高優(yōu)先級任務(wù)的優(yōu)先級.當(dāng)高優(yōu)先級任務(wù)由于等待資源而被阻塞時,此時資源的擁有者的優(yōu)先級將會自動被提升。
- 優(yōu)先級天花板(priority ceilings)優(yōu)先級天花板是指將申請某資源的任務(wù)的優(yōu)先級提升到可能訪問該資源的所有任務(wù)中最高優(yōu)先級任務(wù)的優(yōu)先級.(這個優(yōu)先級稱為該資源的優(yōu)先級天花板)。