操作系統常見面試題

原文鏈接:https://www.cnblogs.com/inception6-lxc/p/9073983.html

1.進程的常見狀態?以及各種狀態之間的轉換條件?

就緒:進程已處於準備好運行的狀態,即進程已分配到除CPU外的所有必要資源後,只要再獲得CPU,便可立即執行。
執行:進程已經獲得CPU,程序正在執行狀態。
阻塞:正在執行的進程由於發生某事件(如I/O請求、申請緩衝區失敗等)暫時無法繼續執行的狀態。

在這裏插入圖片描述

2.進程同步

進程同步的主要任務:是對多個相關進程在執行次序上進行協調,以使併發執行的諸進程之間能有效地共享資源和相互合作,從而使程序的執行具有可再現性。

同步機制遵循的原則:

(1)空閒讓進;

(2)忙則等待(保證對臨界區的互斥訪問);

(3)有限等待(有限代表有限的時間,避免死等);

(4)讓權等待,(當進程不能進入自己的臨界區時,應該釋放處理機,以免陷入忙等狀態)。

3.進程的通信方式有哪些?

進程通信,是指進程之間的信息交換(信息量少則一個狀態或數值,多者則是成千上萬個字節)。因此,對於用信號量進行的進程間的互斥和同步,由於其所交換的信息量少而被歸結爲低級通信。

所謂高級進程通信指:用戶可以利用操作系統所提供的一組通信命令傳送大量數據的一種通信方式。操作系統隱藏了進程通信的實現細節。或者說,通信過程對用戶是透明的。

高級通信機制可歸結爲三大類:

(1)共享存儲器系統(存儲器中劃分的共享存儲區);實際操作中對應的是“剪貼板”(剪貼板實際上是系統維護管理的一塊內存區域)的通信方式,比如舉例如下:word進程按下ctrl+c,在ppt進程按下ctrl+v,即完成了word進程和ppt進程之間的通信,複製時將數據放入到剪貼板,粘貼時從剪貼板中取出數據,然後顯示在ppt窗口上。

(2)消息傳遞系統(進程間的數據交換以消息(message)爲單位,當今最流行的微內核操作系統中,微內核與服務器之間的通信,無一例外地都採用了消息傳遞機制。應用舉例:郵槽(MailSlot)是基於廣播通信體系設計出來的,它採用無連接的不可靠的數據傳輸。郵槽是一種單向通信機制,創建郵槽的服務器進程讀取數據,打開郵槽的客戶機進程寫入數據。

(3)管道通信系統(管道即:連接讀寫進程以實現他們之間通信的共享文件(pipe文件,類似先進先出的隊列,由一個進程寫,另一進程讀))。實際操作中,管道分爲:匿名管道、命名管道。匿名管道是一個未命名的、單向管道,通過父進程和一個子進程之間傳輸數據。匿名管道只能實現本地機器上兩個進程之間的通信,而不能實現跨網絡的通信。命名管道不僅可以在本機上實現兩個進程間的通信,還可以跨網絡實現兩個進程間的通信。

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

      注1:無名管道只能實現父子或者兄弟進程之間的通信,有名管道(FIFO)可以實現互不相關的兩個進程之間的通信。

注2:用FIFO讓一個服務器和多個客戶端進行交流時候,每個客戶在向服務器發送信息前建立自己的讀管道,或者讓服務器在得到數據後再建立管道。使用客戶的進程號(pid)作爲管道名是一種常用的方法。客戶可以先把自己的進程號告訴服務器,然後再到那個以自己進程號命名的管道中讀取回復。

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

消息隊列:是一個在系統內核中用來保存消 息的隊列,它在系統內核中是以消息鏈表的形式出現的。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩衝區大小受限等缺點。

共享內存:共享內存允許兩個或多個進程訪問同一個邏輯內存。這一段內存可以被兩個或兩個以上的進程映射至自身的地址空間中,一個進程寫入共享內存的信息,可以被其他使用這個共享內存的進程,通過一個簡單的內存讀取讀出,從而實現了進程間的通信。如果某個進程向共享內存寫入數據,所做的改動將立即影響到可以訪問同一段共享內存的任何其他進程。共享內存是最快的IPC方式,它是針對其它進程間通信方式運行效率低而專門設計的。它往往與其它通信機制(如信號量)配合使用,來實現進程間的同步和通信。

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

4.上下文切換

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

5.進程與線程的區別和聯繫?

進程是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。
線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。
進程和線程的關係

(1)一個線程只能屬於一個進程,而一個進程可以有多個線程,但至少有一個線程。線程是操作系統可識別的最小執行和調度單位。

(2)資源分配給進程,同一進程的所有線程共享該進程的所有資源。 同一進程中的多個線程共享代碼段(代碼和常量),數據段(全局變量和靜態變量),擴展段(堆存儲)。但是每個線程擁有自己的棧段,棧段又叫運行時段,用來存放所有局部變量和臨時變量。

(3)處理機分給線程,即真正在處理機上運行的是線程。

(4)線程在執行過程中,需要協作同步。不同進程的線程間要利用消息通信的辦法實現同步。

進程與線程的區別?

(1)進程有自己的獨立地址空間,線程沒有

(2)進程是資源分配的最小單位,線程是CPU調度的最小單位

(3)進程和線程通信方式不同(線程之間的通信比較方便。同一進程下的線程共享數據(比如全局變量,靜態變量),通過這些數據來通信不僅快捷而且方便,當然如何處理好這些訪問的同步與互斥正是編寫多線程程序的難點。而進程之間的通信只能通過進程通信的方式進行。)

(4)進程上下文切換開銷大,線程開銷小

(5)一個進程掛掉了不會影響其他進程,而線程掛掉了會影響其他線程

(6)對進程進程操作一般開銷都比較大,對線程開銷就小了

爲什麼進程上下文切換比線程上下文切換代價高?

進程切換分兩步:

1.切換頁目錄以使用新的地址空間

2.切換內核棧和硬件上下文

對於linux來說,線程和進程的最大區別就在於地址空間,對於線程切換,第1步是不需要做的,第2是進程和線程切換都要做的。

切換的性能消耗:

1、線程上下文切換和進程上下問切換一個最主要的區別是線程的切換虛擬內存空間依然是相同的,但是進程切換是不同的。這兩種上下文切換的處理都是通過操作系統內核來完成的。內核的這種切換過程伴隨的最顯著的性能損耗是將寄存器中的內容切換出。

2、另外一個隱藏的損耗是上下文的切換會擾亂處理器的緩存機制。簡單的說,一旦去切換上下文,處理器中所有已經緩存的內存地址一瞬間都作廢了。還有一個顯著的區別是當你改變虛擬內存空間的時候,處理的頁表緩衝(processor’s Translation Lookaside Buffer (TLB))或者相當的神馬東西會被全部刷新,這將導致內存的訪問在一段時間內相當的低效。但是在線程的切換中,不會出現這個問題。

轉自知乎:進程和線程的區別

鏈接:https://www.zhihu.com/question/25532384/answer/81152571
首先來一句概括的總論:進程和線程都是一個時間段的描述,是CPU工作時間段的描述。

下面細說背景:
CPU+RAM+各種資源(比如顯卡,光驅,鍵盤,GPS, 等等外設)構成我們的電腦,但是電腦的運行,實際就是CPU和相關寄存器以及RAM之間的事情。

一個最最基礎的事實:CPU太快,太快,太快了,寄存器僅僅能夠追的上他的腳步,RAM和別的掛在各總線上的設備完全是望其項背。那當多個任務要執行的時候怎麼辦呢?輪流着來?或者誰優先級高誰來?不管怎麼樣的策略,一句話就是在CPU看來就是輪流着來。

一個必須知道的事實:執行一段程序代碼,實現一個功能的過程介紹 ,當得到CPU的時候,相關的資源必須也已經就位,就是顯卡啊,GPS啊什麼的必須就位,然後CPU開始執行。這裏除了CPU以外所有的就構成了這個程序的執行環境,也就是我們所定義的程序上下文。當這個程序執行完了,或者分配給他的CPU執行時間用完了,那它就要被切換出去,等待下一次CPU的臨幸。在被切換出去的最後一步工作就是保存程序上下文,因爲這個是下次他被CPU臨幸的運行環境,必須保存。

串聯起來的事實:前面講過在CPU看來所有的任務都是一個一個的輪流執行的,具體的輪流方法就是:先加載程序A的上下文,然後開始執行A,保存程序A的上下文,調入下一個要執行的程序B的程序上下文,然後開始執行B,保存程序B的上下文。。。。

========= 重要的東西出現了========
進程和線程就是這樣的背景出來的,兩個名詞不過是對應的CPU時間段的描述,名詞就是這樣的功能。
進程就是包換上下文切換的程序執行時間總和 = CPU加載上下文+CPU執行+CPU保存上下文
線程是什麼呢?
進程的顆粒度太大,每次都要有上下的調入,保存,調出。如果我們把進程比喻爲一個運行在電腦上的軟件,那麼一個軟件的執行不可能是一條邏輯執行的,必定有多個分支和多個程序段,就好比要實現程序A,實際分成 a,b,c等多個塊組合而成。那麼這裏具體的執行就可能變成:

程序A得到CPU =》CPU加載上下文,開始執行程序A的a小段,然後執行A的b小段,然後再執行A的c小段,最後CPU保存A的上下文。

這裏a,b,c的執行是共享了A的上下文,CPU在執行的時候沒有進行上下文切換的。這裏的a,b,c就是線程,也就是說線程是共享了進程的上下文環境,的更爲細小的CPU時間段。

到此全文結束,再一個總結:

進程和線程都是一個時間段的描述,是CPU工作時間段的描述,不過是顆粒大小不同。

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

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

線程可以分爲兩類:

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

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

事實上,在現代操作系統中,往往使用組合方式實現多線程,即線程創建完全在用戶空間中完成,並且一個應用程序中的多個用戶級線程被映射到一些內核級線程上,相當於是一種折中方案。

6.進程調度

調度種類

高級調度:(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等。

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

在這裏插入圖片描述

進程在進入待調度的隊列等待時,首先進入優先級最高的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再經過一個時間片,完成了任務。於是整個調度過程結束。

7.死鎖的條件?以及如何處理死鎖問題?

定義:如果一組進程中的每一個進程都在等待僅由該組進程中的其他進程才能引發的事件,那麼該組進程就是死鎖的。或者在兩個或多個併發進程中,如果每個進程持有某種資源而又都等待別的進程釋放它或它們現在保持着的資源,在未改變這種狀態之前都不能向前推進,稱這一組進程產生了死鎖。通俗地講,就是兩個或多個進程被無限期地阻塞、相互等待的一種狀態。

產生死鎖的必要條件:

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

請求與保持條件(Hold and wait):已經得到資源的進程可以再次申請新的資源。

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

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

如何處理死鎖問題:

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

檢測死鎖並且恢復。

仔細地對資源進行動態分配,使系統始終處於安全狀態以避免死鎖。

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

8.臨界資源

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

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

9.一個程序從開始運行到結束的完整過程(四個過程)

1、預處理:條件編譯,頭文件包含,宏替換的處理,生成.i文件。

2、編譯:將預處理後的文件轉換成彙編語言,生成.s文件

3、彙編:彙編變爲目標代碼(機器代碼)生成.o的文件

4、鏈接:連接目標代碼,生成可執行程序

鏈接

10.內存池、進程池、線程池。(c++程序員必須掌握)

首先介紹一個概念“池化技術 ”。池化技術就是:提前保存大量的資源,以備不時之需以及重複使用。池化技術應用廣泛,如內存池,線程池,連接池等等。內存池相關的內容,建議看看Apache、Nginx等開源web服務器的內存池實現。
  由於在實際應用當做,分配內存、創建進程、線程都會設計到一些系統調用,系統調用需要導致程序從用戶態切換到內核態,是非常耗時的操作。因此,當程序中需要頻繁的進行內存申請釋放,進程、線程創建銷燬等操作時,通常會使用內存池、進程池、線程池技術來提升程序的性能。

線程池:線程池的原理很簡單,類似於操作系統中的緩衝區的概念,它的流程如下:先啓動若干數量的線程,並讓這些線程都處於睡眠狀態,當需要一個開闢一個線程去做具體的工作時,就會喚醒線程池中的某一個睡眠線程,讓它去做具體工作,當工作完成後,線程又處於睡眠狀態,而不是將線程銷燬。

進程池與線程池同理。

內存池:內存池是指程序預先從操作系統申請一塊足夠大內存,此後,當程序中需要申請內存的時候,不是直接向操作系統申請,而是直接從內存池中獲取;同理,當程序釋放內存的時候,並不真正將內存返回給操作系統,而是返回內存池。當程序退出(或者特定時間)時,內存池纔將之前申請的內存真正釋放。

11.動態鏈接庫與靜態鏈接庫的區別

靜態庫

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

在這裏插入圖片描述

動態庫

靜態庫很方便,但是如果我們只是想用庫中的某一個函數,卻仍然得把所有的內容都鏈接進去。一個更現代的方法則是使用共享庫,避免了在文件中靜態庫的大量重複。

動態鏈接可以在首次載入的時候執行(load-time linking),這是 Linux 的標準做法,會由動態鏈接器ld-linux.so 完成,比方標準 C 庫(libc.so) 通常就是動態鏈接的,這樣所有的程序可以共享同一個庫,而不用分別進行封裝。

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

鏈接使得我們可以用多個對象文件構造我們的程序。可以在程序的不同階段進行(編譯、載入、運行期間均可),理解鏈接可以幫助我們避免遇到奇怪的錯誤。
在這裏插入圖片描述

區別:

使用靜態庫的時候,靜態鏈接庫要參與編譯,在生成執行文件之前的鏈接過程中,要將靜態鏈接庫的全部指令直接鏈接入可執行文件中。而動態庫提供了一種方法,使進程可以調用不屬於其可執行代碼的函數。函數的可執行代碼位於一個.dll文件中,該dll包含一個或多個已被編譯,鏈接並與使用它們的進程分開儲存的函數。
靜態庫中不能再包含其他動態庫或靜態庫,而在動態庫中還可以再包含其他動態或者靜態庫。
靜態庫在編譯的時候,就將庫函數裝在到程序中去了,而動態庫函數必須在運行的時候才被裝載,所以使用靜態庫速度快一些。
鏈接

12.虛擬內存?優缺點?

定義:具有請求調入功能和置換功能,能從邏輯上對內存容量加以擴充得一種存儲器系統。其邏輯容量由內存之和和外存之和決定。

與傳統存儲器比較虛擬存儲器有以下三個主要特徵:

多次性,是指無需在作業運行時一次性地全部裝入內存,而是允許被分成多次調入內存運行。
對換性,是指無需在作業運行時一直常駐內存,而是允許在作業的運行過程中,進行換進和換出。
虛擬性,是指從邏輯上擴充內存的容量,使用戶所看到的內存容量,遠大於實際的內存容量。
虛擬內存的實現有以下兩種方式:

  • 請求分頁存儲管理。
  • 請求分段存儲管理。

13.頁面置換算法

操作系統將內存按照頁面進行管理,在需要的時候才把進程相應的部分調入內存。當產生缺頁中斷時,需要選擇一個頁面寫入。如果要換出的頁面在內存中被修改過,變成了“髒”頁面,那就需要先寫會到磁盤。頁面置換算法,就是要選出最合適的一個頁面,使得置換的效率最高。頁面置換算法有很多,簡單介紹幾個,重點介紹比較重要的LRU及其實現算法。

一、最優頁面置換算法

最理想的狀態下,我們給頁面做個標記,挑選一個最遠纔會被再次用到的頁面調出。當然,這樣的算法不可能實現,因爲不確定一個頁面在何時會被用到。

二、先進先出頁面置換算法(FIFO)及其改進

這種算法的思想和隊列是一樣的,該算法總是淘汰最先進入內存的頁面,即選擇在內存中駐留時間最久的頁面予淘汰。實現:把一個進程已調入內存的頁面按先後次序鏈接成一個隊列,並且設置一個指針總是指向最老的頁面。缺點:對於有些經常被訪問的頁面如含有全局變量、常用函數、例程等的頁面,不能保證這些不被淘汰。

三、最近最少使用頁面置換算法LRU(Least Recently Used)

根據頁面調入內存後的使用情況做出決策。LRU置換算法是選擇最近最久未使用的頁面進行淘汰。

1.爲每個在內存中的頁面配置一個移位寄存器。(P165)定時信號將每隔一段時間將寄存器右移一位。最小數值的寄存器對應頁面就是最久未使用頁面。

2.利用一個特殊的棧保存當前使用的各個頁面的頁面號。每當進程訪問某頁面時,便將該頁面的頁面號從棧中移出,將它壓入棧頂。因此,棧頂永遠是最新被訪問的頁面號,棧底是最近最久未被訪問的頁面號。

鏈接:分頁內存管理(把虛擬內存空間和物理內存空間均劃分爲大小相同的頁面等內容):
https://www.cnblogs.com/edisonchou/p/5094066.html

鏈接:分段內存管理:
https://www.cnblogs.com/edisonchou/p/5115242.html

14.中斷與系統調用

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

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

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

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

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

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

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

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

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

       用戶空間就是用戶進程所在的內存區域,相對的,系統空間就是操作系統佔據的內存區域。用戶進程和系統進程的所有數據都在內存中。處於用戶態的程序只能訪問用戶空間,而處於內核態的程序可以訪問用戶空間和內核空間。

用戶態切換到內核態的方式如下:

系統調用:程序的執行一般是在用戶態下執行的,但當程序需要使用操作系統提供的服務時,比如說打開某一設備、創建文件、讀寫文件(這些均屬於系統調用)等,就需要向操作系統發出調用服務的請求,這就是系統調用。

異常:當CPU在執行運行在用戶態下的程序時,發生了某些事先不可知的異常,這時會觸發由當前運行進程切換到處理此異常的內核相關程序中,也就轉到了內核態,比如缺頁異常。

外圍設備的中斷:當外圍設備完成用戶請求的操作後,會向CPU發出相應的中斷信號,這時CPU會暫停執行下一條即將要執行的指令轉而去執行與中斷信號對應的處理程序,如果先前執行的指令是用戶態下的程序,那麼這個轉換的過程自然也就發生了由用戶態到內核態的切換。比如硬盤讀寫操作完成,系統會切換到硬盤讀寫的中斷處理程序中執行後續操作等。

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

   權限不一樣。

用戶態的進程能存取它們自己的指令和數據,但不能存取內核指令和數據(或其他進程的指令和數據)。

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

15.C++多線程,互斥,同步

同步和互斥
當有多個線程的時候,經常需要去同步(注:同步不是同時刻)這些線程以訪問同一個數據或資源。例如,假設有一個程序,其中一個線程用於把文件讀到內存,而另一個線程用於統計文件中的字符數。當然,在把整個文件調入內存之前,統計它的計數是沒有意義的。但是,由於每個操作都有自己的線程,操作系統會把兩個線程當作是互不相干的任務分別執行,這樣就可能在沒有把整個文件裝入內存時統計字數。爲解決此問題,你必須使兩個線程同步工作。

所謂同步,是指在不同進程之間的若干程序片斷,它們的運行必須嚴格按照規定的某種先後次序來運行,這種先後次序依賴於要完成的特定的任務。如果用對資源的訪問來定義的話,同步是指在互斥的基礎上(大多數情況),通過其它機制實現訪問者對資源的有序訪問。在大多數情況下,同步已經實現了互斥,特別是所有寫入資源的情況必定是互斥的。少數情況是指可以允許多個訪問者同時訪問資源。

所謂互斥,是指散佈在不同進程之間的若干程序片斷,當某個進程運行其中一個程序片段時,其它進程就不能運行它們之中的任一程序片段,只能等到該進程運行完這個程序片段後纔可以運行。如果用對資源的訪問來定義的話,互斥某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。

多線程同步和互斥有幾種實現方法
線程間的同步方法大體可分爲兩類:用戶模式和內核模式。顧名思義,內核模式就是指利用系統內核對象的單一性來進行同步,使用時需要切換內核態與用戶態,而用戶模式就是不需要切換到內核態,只在用戶態完成操作。

用戶模式下的方法有:原子操作(例如一個單一的全局變量),臨界區。

內核模式下的方法有:事件,信號量,互斥量。

1、臨界區:通過對多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數據訪問。
2、互斥量:爲協調共同對一個共享資源的單獨訪問而設計的。
3、信號量:爲控制一個具有有限數量用戶資源而設計。
4、事 件:用來通知線程有一些事件已發生,從而啓動後繼任務的開始。

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

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

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

與內存/虛擬內存相關的還有如下兩個概念:

  1. Resident Set

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

  1. Thrashing

由於resident set包含預期要用的數據,理想情況下,進程運行過程中用到的數據都會逐步加載進resident set。但事實往往並非如此:每當需要的內存頁面(page)不在resident set中時,操作系統必須從虛擬內存或硬盤中讀數據,這個過程被稱爲內存頁面錯誤(page faults)。當操作系統需要花費大量時間去處理頁面錯誤的情況就是thrashing。
參考鏈接:https://blog.csdn.net/newcong0123/article/details/52792070

17.內部碎片與外部碎片

在內存管理中,內部碎片是已經被分配出去的的內存空間大於請求所需的內存空間。

外部碎片是指還沒有分配出去,但是由於大小太小而無法分配給申請空間的新進程的內存空間空閒塊。

固定分區存在內部碎片,可變式分區分配會存在外部碎片;

頁式虛擬存儲系統存在內部碎片;段式虛擬存儲系統,存在外部碎片

爲了有效的利用內存,使內存產生更少的碎片,要對內存分頁,內存以頁爲單位來使用,最後一頁往往裝不滿,於是形成了內部碎片。

爲了共享要分段,在段的換入換出時形成外部碎片,比如5K的段換出後,有一個4k的段進來放到原來5k的地方,於是形成1k的外部碎片。

18.同步和互斥的區別

    當有多個線程的時候,經常需要去同步這些線程以訪問同一個數據或資源。例如,假設有一個程序,其中一個線程用於把文件讀到內存,而另一個線程用於統計文件中的字符數。當然,在把整個文件調入內存之前,統計它的計數是沒有意義的。但是,由於每個操作都有自己的線程,操作系統會把兩個線程當作是互不相干的任務分別執行,這樣就可能在沒有把整個文件裝入內存時統計字數。爲解決此問題,你必須使兩個線程同步工作。

  所謂同步,是指散步在不同進程之間的若干程序片斷,它們的運行必須嚴格按照規定的某種先後次序來運行,這種先後次序依賴於要完成的特定的任務。如果用對資源的訪問來定義的話,同步是指在互斥的基礎上(大多數情況),通過其它機制實現訪問者對資源的有序訪問。在大多數情況下,同步已經實現了互斥,特別是所有寫入資源的情況必定是互斥的。少數情況是指可以允許多個訪問者同時訪問資源。

  所謂互斥,是指散佈在不同進程之間的若干程序片斷,當某個進程運行其中一個程序片段時,其它進程就不能運行它們之中的任一程序片段,只能等到該進程運行完這個程序片段後纔可以運行。如果用對資源的訪問來定義的話,互斥某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。

19.什麼是線程安全

如果多線程的程序運行結果是可預期的,而且與單線程的程序運行結果一樣,那麼說明是“線程安全”的。

20.同步與異步

同步:
同步的定義:是指一個進程在執行某個請求的時候,若該請求需要一段時間才能返回信息,那麼,這個進程將會一直等待下去,直到收到返回信息才繼續執行下去。
特點:
同步是阻塞模式;
同步是按順序執行,執行完一個再執行下一個,需要等待,協調運行;
異步:
是指進程不需要一直等下去,而是繼續執行下面的操作,不管其他進程的狀態。當有消息返回時系統會通知進程進行處理,這樣可以提高執行的效率。
特點:
異步是非阻塞模式,無需等待;
異步是彼此獨立,在等待某事件的過程中,繼續做自己的事,不需要等待這一事件完成後再工作。線程是異步實現的一個方式。
同步與異步的優缺點:
同步可以避免出現死鎖,讀髒數據的發生。一般共享某一資源的時候,如果每個人都有修改權限,同時修改一個文件,有可能使一個讀取另一個人已經刪除了內容,就會出錯,同步就不會出錯。但,同步需要等待資源訪問結束,浪費時間,效率低。
異步可以提高效率,但,安全性較低。

21.系統調用與庫函數的區別

系統調用(System call)是程序向系統內核請求服務的方式。可以包括硬件相關的服務(例如,訪問硬盤等),或者創建新進程,調度其他進程等。系統調用是程序和操作系統之間的重要接口。

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

在移植性方面,不同操作系統的系統調用一般是不同的,移植性差;而在所有的ANSI C編譯器版本中,C庫函數是相同的。

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

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

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

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

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

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

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

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

24.IO多路複用

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

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

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

如果一個TCP服務器既要處理監聽套接口,又要處理已連接套接口,一般也要用到I/O複用。

如果一個服務器即要處理TCP,又要處理UDP,一般要使用I/O複用。

如果一個服務器要處理多個服務或多個協議,一般要使用I/O複用。

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

25.線程安全

如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。或者說:一個類或者程序所提供的接口對於線程來說是原子操作或者多個線程之間的切換不會導致該接口的執行結果存在二義性,也就是說我們不用考慮同步的問題。

線程安全問題都是由全局變量及靜態變量引起的。
若每個線程中對全局變量、靜態變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全。

26.線程共享資源和獨佔資源問題

參考鏈接:https://www.cnblogs.com/baoendemao/p/3804677.html

一個進程中的所有線程共享該進程的地址空間,但它們有各自獨立的(/私有的)棧(stack),Windows線程的缺省堆棧大小爲1M。堆(heap)的分配與棧有所不同,一般是一個進程有一個C運行時堆,這個堆爲本進程中所有線程共享,windows進程還有所謂進程默認堆,用戶也可以創建自己的堆。
用操作系統術語,線程切換的時候實際上切換的是一個可以稱之爲線程控制塊的結構(TCB),裏面保存所有將來用於恢復線程環境必須的信息,包括所有必須保存的寄存器集,線程的狀態等。

堆: 是大家共有的空間,分全局堆和局部堆。全局堆就是所有沒有分配的空間,局部堆就是用戶分配的空間。堆在操作系統對進程初始化的時候分配,運行過程中也可以向系統要額外的堆,但是記得用完了要還給操作系統,要不然就是內存泄漏。

棧:是個線程獨有的,保存其運行狀態和局部自動變量的。棧在線程開始的時候初始化,每個線程的棧互相獨立,因此,棧是 thread safe的。操作系統在切換線程的時候會自動的切換棧,就是切換 SS/ESP寄存器。棧空間不需要在高級語言裏面顯式的分配和釋放。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章