操做系統面試常見題型

進程 vs 線程

  • 進程(process)與線程(thread)最大的區別是進程擁有本身的地址空間,某進程內的線程對於其餘進程不可見,即進程A不能經過傳地址的方式直接讀寫進程B的存儲區域。進程之間的通訊須要經過進程間通訊(Inter-process communication,IPC)。與之相對的,同一進程的各線程間之間能夠直接經過傳遞地址或全局變量的方式傳遞信息linux

  • 進程做爲操做系統中擁有資源和獨立調度的基本單位,能夠擁有多個線程。一般操做系統中運行的一個程序就對應一個進程。在同一進程中,線程的切換不會引發進程切換。在不一樣進程中進行線程切換,如從一個進程內的線程切換到另外一個進程中的線程時,會引發進程切換。相比進程切換,線程切換的開銷要小不少。線程於進程相互結合可以提升系統的運行效率。程序員

線程能夠分爲兩類:算法

  • 用戶級線程(user level thread):對於這類線程,有關線程管理的全部工做都由應用程序完成,內核意識不到線程的存在。在應用程序啓動後,操做系統分配給該程序一個進程號,以及其對應的內存空間等資源。應用程序一般先在一個線程中運行,該線程被成爲主線程。在其運行的某個時刻,能夠經過調用線程庫中的函數建立一個在相同進程中運行的新線程。用戶級線程的好處是很是高效,不須要進入內核空間,但併發效率不高。數組

  • 內核級線程(kernel level thread):對於這類線程,有關線程管理的全部工做由內核完成,應用程序沒有進行線程管理的代碼,只能調用內核線程的接口。內核維護進程及其內部的每一個線程,調度也由內核基於線程架構完成。內核級線程的好處是,內核能夠將不一樣線程更好地分配到不一樣的CPU,以實現真正的並行計算。緩存

事實上,在現代操做系統中,每每使用組合方式實現多線程,即線程建立徹底在用戶空間中完成,而且一個應用程序中的多個用戶級線程被映射到一些內核級線程上,至關因而一種折中方案。安全

上下文切換

  • 對於單核單線程CPU而言,在某一時刻只能執行一條CPU指令。上下文切換(Context Switch)是一種將CPU資源從一個進程分配給另外一個進程的機制。從用戶角度看,計算機可以並行運行多個進程,這偏偏是操做系統經過快速上下文切換形成的結果。在切換的過程當中,操做系統須要先存儲當前進程的狀態(包括內存空間的指針,當前執行完的指令等等),再讀入下一個進程的狀態,而後執行此進程。服務器

系統調用與庫函數的區別

  • 系統調用(System call)是程序向系統內核請求服務的方式。能夠包括硬件相關的服務(例如,訪問硬盤等),或者建立新進程,調度其餘進程等。系統調用是程序和操做系統之間的重要接口。網絡

  • 庫函數:把一些經常使用的函數編寫完放到一個文件裏,編寫應用程序時調用,這是由第三方提供的,發生在用戶地址空間多線程

  • 移植性方面,不一樣操做系統的系統調用通常是不一樣的,移植性差;而在全部的ANSI C編譯器版本中,C庫函數是相同的。架構

  • 調用開銷方面,系統調用須要在用戶空間和內核環境間切換,開銷較大;而庫函數調用屬於「過程調用」,開銷較小。

守護、殭屍、孤兒進程的概念

  • 守護進程:運行在後臺的一種特殊進程,獨立於控制終端並週期性地執行某些任務

  • 殭屍進程:一個進程 fork 子進程,子進程退出,而父進程沒有wait/waitpid子進程,那麼子進程的進程描述符仍保存在系統中,這樣的進程稱爲殭屍進程。

  • 孤兒進程:一個父進程退出,而它的一個或多個子進程還在運行,這些子進程稱爲孤兒進程。(孤兒進程將由 init 進程收養並對它們完成狀態收集工做)

分時系統與實時系統的區別

  • 分時系統(Sharing time system):系統把CPU時間分紅很短的時間片,輪流地分配給多個做業。優勢:對多個用戶的多個做業都能保證足夠快的響應時間,而且有效提升了資源的利用率。

  • 實時系統(Real-time system):系統對外部輸入的信息,可以在規定的時間內(截止期限)處理完畢並作出反應。優勢:可以集中地及時地處理並做出反應,高可靠性,安全性。

  • 一般計算機採用的是sharing time,即多個進程/用戶之間共享CPU,從形勢上實現多任務。各個用戶/進程之間的調度並不是精準度特別高,若是一個進程被鎖住,能夠給它分配更多的時間。而實時操做系統則不一樣,軟件和硬件必須聽從嚴格的deadline,超過期限的進程可能直接被終止。在這樣的操做系統中,每次加鎖都須要仔細考慮。

Semaphore(信號量) Vs Mutex(互斥鎖)

  • 當用戶創立多個線程/進程時,若是不一樣線程/進程同時讀寫相同的內容,則可能形成讀寫錯誤,或者數據不一致。此時,須要經過加鎖的方式,控制臨界區(critical section)的訪問權限。對於semaphore而言,在初始化變量的時候能夠控制容許多少個線程/進程同時訪問一個臨界區,其餘的線程/進程會被堵塞,直到有人解鎖。

  • Mutex至關於只容許一個線程/進程訪問的semaphore。此外,根據實際須要,人們還實現了一種讀寫鎖(read-write lock),它容許同時存在多個閱讀者(reader),但任什麼時候候至多隻有一個寫者(writer),且不能於讀者共存。

邏輯地址 Vs 物理地址 Vs 虛擬內存

  • 所謂的邏輯地址,是指計算機用戶(例如程序開發者),看到的地址。例如,當建立一個長度爲100的整型數組時,操做系統返回一個邏輯上的連續空間:指針指向數組第一個元素的內存地址。因爲整型元素的大小爲4個字節,故第二個元素的地址時起始地址加4,以此類推。事實上,邏輯地址並不必定是元素存儲的真實地址,即數組元素的物理地址(在內存條中所處的位置),並不是是連續的,只是操做系統經過地址映射,將邏輯地址映射成連續的,這樣更符合人們的直觀思惟

  • 另外一個重要概念是虛擬內存。操做系統讀寫內存的速度能夠比讀寫磁盤的速度快幾個量級。可是,內存價格也相對較高,不能大規模擴展。因而,操做系統能夠經過將部分不太經常使用的數據移出內存,「存放到價格相對較低的磁盤緩存,以實現內存擴展。操做系統還能夠經過算法預測哪部分存儲到磁盤緩存的數據須要進行讀寫,提早把這部分數據讀回內存。虛擬內存空間相對磁盤而言要小不少,所以,即便搜索虛擬內存空間也比直接搜索磁盤要快。惟一慢於磁盤的多是,內存、虛擬內存中都沒有所須要的數據,最終還須要從硬盤中直接讀取。這就是爲何內存和虛擬內存中須要存儲會被重複讀寫的數據,不然就失去了緩存的意義。現代計算機中有一個專門的轉譯緩衝區(Translation Lookaside Buffer,TLB),用來實現虛擬地址到物理地址的快速轉換。

與內存/虛擬內存相關的還有以下兩個概念:
1) Resident Set

  • 當一個進程在運行的時候,操做系統不會一次性加載進程的全部數據到內存,只會加載一部分正在用,以及預期要用的數據。其餘數據可能存儲在虛擬內存,交換區和硬盤文件系統上。被加載到內存的部分就是resident set。

2) Thrashing

  • 因爲resident set包含預期要用的數據,理想狀況下,進程運行過程當中用到的數據都會逐步加載進resident set。但事實每每並不是如此:每當須要的內存頁面(page)不在resident set中時,操做系統必須從虛擬內存或硬盤中讀數據,這個過程被稱爲內存頁面錯誤(page faults)。當操做系統須要花費大量時間去處理頁面錯誤的狀況就是thrashing。

文件系統

  • Unix風格的文件系統利用樹形結構管理文件。每一個節點有多個指針,指向下一層節點或者文件的磁盤存儲位置。文件節點還附有文件的操做信息(metadata),包括修改時間,訪問權限等等。

  • 用戶的訪問權限經過能力表(Capability List)和訪問控制表(Access Control List)實現。前者從文件角度出發,標註了每一個用戶能夠對該文件進行何種操做。後者從用戶角度出發,標註了某用戶能夠以什麼權限操做哪些文件。

  • Unix的文件權限分爲讀、寫和執行,用戶組分爲文件擁有者,組和全部用戶。能夠經過命令對三組用戶分別設置權限。

請問死鎖的條件是什麼?以及如何處理死鎖問題?

  • 互斥條件(Mutual exclusion):資源不能被共享,只能由一個進程使用。

  • 請求與保持條件(Hold and wait):已經獲得資源的進程能夠再次申請新的資源。

  • 非搶佔條件(No pre-emption):已經分配的資源不能從相應的進程中被強制地剝奪。

  • 循環等待條件(Circular wait):系統中若干進程組成環路,該環路中每一個進程都在等待相鄰進程正佔用的資源。

如何處理死鎖問題:

  • 忽略該問題。例如鴕鳥算法,該算法能夠應用在極少發生死鎖的的狀況下。爲何叫鴕鳥算法呢,由於傳說中鴕鳥看到危險就把頭埋在地底下,可能鴕鳥以爲看不到危險也就沒危險了吧。跟掩耳盜鈴有點像。

  • 檢測死鎖而且恢復。

  • 仔細地對資源進行動態分配,以避免死鎖

  • 經過破除死鎖四個必要條件之一,來防止死鎖產生。

動態連接庫與靜態連接庫的區別

靜態庫

  • 靜態庫是一個外部函數與變量的集合體。靜態庫的文件內容,一般包含一堆程序員自定的變量與函數,其內容不像動態連接庫那麼複雜,在編譯期間由編譯器與鏈接器將它集成至應用程序內,並製做成目標文件以及能夠獨立運做的可執行文件。而這個可執行文件與編譯可執行文件的程序,都是一種程序的靜態建立(static build)。

clipboard.png

動態庫

  • 靜態庫很方便,可是若是咱們只是想用庫中的某一個函數,卻仍然得把全部的內容都連接進去。一個更現代的方法則是使用共享庫,避免了在文件中靜態庫的大量重複。

  • 動態連接能夠在首次載入的時候執行(load-time linking),這是 Linux 的標準作法,會由動態連接器ld-linux.so 完成,比方標準 C 庫(libc.so) 一般就是動態連接的,這樣全部的程序能夠共享同一個庫,而不用分別進行封裝。

  • 動態連接也能夠在程序開始執行的時候完成(run-time linking),在 Linux 中使用 dlopen()接口來完成(會使用函數指針),一般用於分佈式軟件,高性能服務器上。並且共享庫也能夠在多個進程間共享。

  • 連接使得咱們能夠用多個對象文件構造咱們的程序。能夠在程序的不一樣階段進行(編譯、載入、運行期間都可),理解連接能夠幫助咱們避免遇到奇怪的錯誤

clipboard.png

進程間通訊

  • 管道管道是單向的、先進先出的、無結構的、固定大小的字節流,它把一個進程的標準輸出和另外一個進程的標準輸入鏈接在一塊兒。寫進程在管道的尾端寫入數據,讀進程在管道的道端讀出數據。數據讀出後將從管道中移走,其它讀進程都不能再讀到這些數據。管道提供了簡單的流控制機制。進程試圖讀空管道時,在有數據寫入管道前,進程將一直阻塞。一樣地,管道已經滿時,進程再試圖寫管道,在其它進程從管道中移走數據以前,寫進程將一直阻塞。

  • 信號量信號量是一個計數器,能夠用來控制多個進程對共享資源的訪問。它常做爲一種鎖機制,防止某進程正在訪問共享資源時,其它進程也訪問該資源。所以,主要做爲進程間以及同一進程內不一樣線程之間的同步手段。

  • 消息隊列消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩衝區大小受限等缺點

  • 信號:信號是一種比較複雜的通訊方式,用於通知接收進程某個事件已經發生。

  • 共享內存共享內存就是映射一段能被其它進程所訪問的內存,這段共享內存由一個進程建立,但多個進程均可以訪問。共享內存是最快的IPC方式,它是針對其它進程間通訊方式運行效率低而專門設計的。它每每與其它通訊機制(如信號量)配合使用,來實現進程間的同步和通訊。

  • 套接字:套接字也是一種進程間通訊機制,與其它通訊機制不一樣的是,它可用於不一樣機器間的進程通訊。

中斷與系統調用

所謂的中斷就是在計算機執行程序的過程當中,因爲出現了某些特殊事情,使得CPU暫停對程序的執行,轉而去執行處理這一事件的程序。等這些特殊事情處理完以後再回去執行以前的程序。中斷通常分爲三類:

  • 由計算機硬件異常或故障引發的中斷,稱爲內部異常中斷

  • 由程序中執行了引發中斷的指令而形成的中斷,稱爲軟中斷(這也是和咱們將要說明的系統調用相關的中斷);

  • 由外部設備請求引發的中斷,稱爲外部中斷。簡單來講,對中斷的理解就是對一些特殊事情的處理。

與中斷緊密相連的一個概念就是中斷處理程序了。當中斷髮生的時候,系統須要去對中斷進行處理,對這些中斷的處理是由操做系統內核中的特定函數進行的,這些處理中斷的特定的函數就是咱們所說的中斷處理程序了。

另外一個與中斷緊密相連的概念就是中斷的優先級。中斷的優先級說明的是當一箇中斷正在被處理的時候,處理器能接受的中斷的級別。中斷的優先級也代表了中斷須要被處理的緊急程度。每一箇中斷都有一個對應的優先級,當處理器在處理某一中斷的時候,只有比這個中斷優先級高的中斷能夠被處理器接受而且被處理。優先級比這個當前正在被處理的中斷優先級要低的中斷將會被忽略。

典型的中斷優先級以下所示:

  • 機器錯誤 > 時鐘 > 磁盤 > 網絡設備 > 終端 > 軟件中斷


在講系統調用以前,先說下進程的執行在系統上的兩個級別:用戶級和核心級,也稱爲用戶態和系統態(user mode and kernel mode)

  • 程序的執行通常是在用戶態下執行的,但當程序須要使用操做系統提供的服務時,好比說打開某一設備、建立文件、讀寫文件等,就須要向操做系統發出調用服務的請求,這就是系統調用。

  • Linux系統有專門的函數庫來提供這些請求操做系統服務的入口,這個函數庫中包含了操做系統所提供的對外服務的接口。當進程發出系統調用以後,它所處的運行狀態就會由用戶態變成核心態。但這個時候,進程自己其實並無作什麼事情,這個時候是由內核在作相應的操做,去完成進程所提出的這些請求

  • 系統調用和中斷的關係就在於,當進程發出系統調用申請的時候,會產生一個軟件中斷。產生這個軟件中斷之後,系統會去對這個軟中斷進行處理,這個時候進程就處於核心態了

用戶態和核心態之間的區別是什麼呢?

  • 用戶態的進程能存取它們本身的指令和數據,但不能存取內核指令和數據(或其餘進程的指令和數據)

  • 核心態下的進程可以存取內核和用戶地址某些機器指令是特權指令,在用戶態下執行特權指令會引發錯誤。在系統中內核並非做爲一個與用戶進程平行的估計的進程的集合。

進程的三種狀態

  • 阻塞態:等待某個事件的完成;

  • 就緒態:等待系統分配處理器以便運行;

  • 運行態:佔有處理器正在運行。

clipboard.png

運行態→阻塞態:每每是因爲等待外設,等待主存等資源分配或等待人工干預而引發的。
阻塞態→就緒態:則是等待的條件已知足,只需分配處處理器後就能運行。
運行態→就緒態:不是因爲自身緣由,而是由外界緣由使運行狀態的進程讓出處理器,這時候就變成就緒態。例如時間片用完,或有更高優先級的進程來搶佔處理器等。
就緒態→運行態:系統按某種策略選中就緒隊列中的一個進程佔用處理器,此時就變成了運行態

進程調度

調度種類

  • 高級調度:(High-Level Scheduling)又稱爲做業調度,它決定把後備做業調入內存運行;

  • 低級調度:(Low-Level Scheduling)又稱爲進程調度,它決定把就緒隊列的某進程得到CPU;

  • 中級調度:(Intermediate-Level Scheduling)又稱爲在虛擬存儲器中引入,在內、外存對換區進行進程對換。

非搶佔式調度與搶佔式調度

  • 非搶佔式:分派程序一旦把處理機分配給某進程後便讓它一直運行下去,直到進程完成或發生進程調度進程調度某事件而阻塞時,才把處理機分配給另外一個進程。

  • 搶佔式:操做系統將正在運行的進程強行暫停,由調度程序將CPU分配給其餘就緒進程的調度方式。

調度策略的設計

  • 響應時間: 從用戶輸入到產生反應的時間

  • 週轉時間: 從任務開始到任務結束的時間

CPU任務能夠分爲交互式任務批處理任務,調度最終的目標是合理的使用CPU,使得交互式任務的響應時間儘量短,用戶不至於感到延遲,同時使得批處理任務的週轉時間儘量短,減小用戶等待的時間。

調度算法

FIFO或First Come, First Served (FCFS)

  • 調度的順序就是任務到達就緒隊列的順序。

  • 公平、簡單(FIFO隊列)、非搶佔、不適合交互式。

  • 未考慮任務特性,平均等待時間能夠縮短。

Shortest Job First (SJF)

  • 最短的做業(CPU區間長度最小)最早調度。

  • SJF能夠保證最小的平均等待時間。

Shortest Remaining Job First (SRJF)

  • SJF的可搶佔版本,比SJF更有優點。

  • SJF(SRJF): 如何知道下一CPU區間大小?根據歷史進行預測: 指數平均法。

優先權調度

  • 每一個任務關聯一個優先權,調度優先權最高的任務。

  • 注意:優先權過低的任務一直就緒,得不到運行,出現「飢餓」現象。

Round-Robin(RR)

  • 設置一個時間片,按時間片來輪轉調度(「輪叫」算法)

  • 優勢: 定時有響應,等待時間較短;缺點: 上下文切換次數較多;

  • 時間片太大,響應時間太長;吞吐量變小,週轉時間變長;當時間片過長時,退化爲FCFS。

多級隊列調度

  • 按照必定的規則創建多個進程隊列

  • 不一樣的隊列有固定的優先級(高優先級有搶佔權)

  • 不一樣的隊列能夠給不一樣的時間片和採用不一樣的調度方法

  • 存在問題1:無法區分I/O bound和CPU bound;

  • 存在問題2:也存在必定程度的「飢餓」現象;

多級反饋隊列

  • 在多級隊列的基礎上,任務能夠在隊列之間移動,更細緻的區分任務。

  • 能夠根據「享用」CPU時間多少來移動隊列,阻止「飢餓」。

  • 最通用的調度算法,多數OS都使用該方法或其變形,如UNIX、Windows等。

多級反饋隊列調度算法描述:

clipboard.png

  • 進程在進入待調度的隊列等待時,首先進入優先級最高的Q1等待。

  • 首先調度優先級高的隊列中的進程。若高優先級中隊列中已沒有調度的進程,則調度次優先級隊列中的進程。例如:Q1,Q2,Q3三個隊列,只有在Q1中沒有進程等待時纔去調度Q2,同理,只有Q1,Q2都爲空時纔會去調度Q3。

  • 對於同一個隊列中的各個進程,按照時間片輪轉法調度。好比Q1隊列的時間片爲N,那麼Q1中的做業在經歷了N個時間片後若尚未完成,則進入Q2隊列等待,若Q2的時間片用完後做業還不能完成,一直進入下一級隊列,直至完成。

  • 在低優先級的隊列中的進程在運行時,又有新到達的做業,那麼在運行完這個時間片後,CPU立刻分配給新到達的做業(搶佔式)。

一個簡單的例子
假設系統中有3個反饋隊列Q1,Q2,Q3,時間片分別爲2,4,8。如今有3個做業J1,J2,J3分別在時間 0 ,1,3時刻到達。而它們所須要的CPU時間分別是3,2,1個時間片。

  • 時刻0 J1到達。 因而進入到隊列1 ,運行1個時間片 ,時間片還未到,此時J2到達。

  • 時刻1 J2到達。 因爲時間片仍然由J1掌控,因而等待。J1在運行了1個時間片後,已經完成了在Q1中的2個時間片的限制,因而J1置於Q2等待被調度。如今處理機分配給J2。

  • 時刻2 J1進入Q2等待調度,J2得到CPU開始運行。

  • 時刻3 J3到達,因爲J2的時間片未到,故J3在Q1等待調度,J1也在Q2等待調度。

  • 時刻4 J2處理完成,因爲J3,J1都在等待調度,可是J3所在的隊列比J1所在的隊列的優先級要高,因而J3被調度,J1繼續在Q2等待。

  • 時刻5 J3通過1個時間片,完成。

  • 時刻6 因爲Q1已經空閒,因而開始調度Q2中的做業,則J1獲得處理器開始運行。 J1再通過一個時間片,完成了任務。因而整個調度過程結束。

臨界資源與臨界區

  • 在操做系統中,進程是佔有資源的最小單位(線程能夠訪問其所在進程內的全部資源,但線程自己並不佔有資源或僅僅佔有一點必須資源)。但對於某些資源來講,其在同一時間只能被一個進程所佔用。這些一次只能被一個進程所佔用的資源就是所謂的臨界資源。典型的臨界資源好比物理上的打印機,或是存在硬盤或內存中被多個進程所共享的一些變量和數據等(若是這類資源不被當作臨界資源加以保護,那麼頗有可能形成丟數據的問題)。

  • 對於臨界資源的訪問,必須是互斥進行。也就是當臨界資源被佔用時,另外一個申請臨界資源的進程會被阻塞,直到其所申請的臨界資源被釋放。而進程內訪問臨界資源的代碼被成爲臨界區。

信號量

信號量是一個肯定的二元組(s,q),其中s是一個具備非負初值的整形變量,q是一個初始狀態爲空的隊列,整形變量s表示系統中某類資源的數目:

  • 當其值 ≥ 0 時,表示系統中當前可用資源的數目

  • 當其值 < 0 時,其絕對值表示系統中因請求該類資源而被阻塞的進程數目

除信號量的初值外,信號量的值僅能由P操做和V操做更改,操做系統利用它的狀態對進程和資源進行管理。

P操做

P 操做記爲P(s),其中s爲一信號量,它執行時主要完成如下動做:

  • s.value = s.value - 1; /可理解爲佔用1個資源,若原來就沒有則記賬「欠」1個/

若s.value ≥ 0,進程繼續執行,不然(即s.value < 0)進程被阻塞,並將該進程插入到信號量s的等待隊列s.queue中。

  • P操做能夠理解爲分配資源的計數器,或是使進程處於等待狀態的控制指令

V操做

V 操做記爲V(s),其中s爲一信號量,它執行時,主要完成如下動做:

  • s.value = s.value + 1;/可理解爲歸還1個資源,若原來就沒有則意義是用此資源還1個欠賬/

若s.value > 0,進程繼續執行,不然從信號量s的等待隊s.queue中移出第一個進程,使其變爲就緒狀態,而後返回原進程繼續執行。

V操做能夠理解爲歸還資源的計數器,或是喚醒進程使其處於就緒狀態的控制指令

IO多路複用

IO多路複用是指內核一旦發現進程指定的一個或者多個IO條件準備讀取,它就通知該進程。IO多路複用適用以下場合:

  • 當客戶處理多個描述字時(通常是交互式輸入和網絡套接口),必須使用I/O複用。

  • 當一個客戶同時處理多個套接口時,而這種狀況是可能的,但不多出現。

  • 若是一個TCP服務器既要處理監聽套接口,又要處理已鏈接套接口,通常也要用到I/O複用。

  • 若是一個服務器即要處理TCP,又要處理UDP,通常要使用I/O複用。

  • 若是一個服務器要處理多個服務或多個協議,通常要使用I/O複用。

  • 與多進程和多線程技術相比,I/O多路複用技術的最大優點是系統開銷小,系統沒必要建立進程/線程,也沒必要維護這些進程/線程,從而大大減少了系統的開銷。

相關文章
相關標籤/搜索