操作系統


操作系統


進程和線程有什么區別?

  • 進程(Process)是系統進行資源分配和調度的基本單位,線程(Thread)是CPU調度和分派的基本單位;
  • 線程依賴於進程而存在,一個進程至少有一個線程;
  • 進程有自己的獨立地址空間,線程共享所屬進程的地址空間;
  • 進程是擁有系統資源的一個獨立單位,而線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),和其他線程共享本進程的相關資源如內存、I/O、cpu等;
  • 在進程切換時,涉及到整個當前進程CPU環境的保存環境的設置以及新被調度運行的CPU環境的設置,而線程切換只需保存和設置少量的寄存器的內容,並不涉及存儲器管理方面的操作,可見,進程切換的開銷遠大於線程切換的開銷;
  • 線程之間的通信更方便,同一進程下的線程共享全局變量等數據,而進程之間的通信需要以進程間通信(IPC)的方式進行;
  • 多線程程序只要有一個線程崩潰,整個程序就崩潰了,但多進程程序中一個進程崩潰並不會對其它進程造成影響,因為進程有自己的獨立地址空間,因此多進程更加健壯

進程操作代碼實現,可以參考:多進程 - 廖雪峰的官方網站

同一進程中的線程可以共享哪些數據?
展開
  • 進程代碼段
  • 進程的公有數據(全局變量、靜態變量...)
  • 進程打開的文件描述符
  • 進程的當前目錄
  • 信號處理器/信號處理函數:對收到的信號的處理方式
  • 進程ID與進程組ID
線程獨占哪些資源?
展開
  • 線程ID
  • 一組寄存器的值
  • 線程自身的棧(堆是共享的)
  • 錯誤返回碼:線程可能會產生不同的錯誤返回碼,一個線程的錯誤返回碼不應該被其它線程修改;
  • 信號掩碼/信號屏蔽字(Signal mask):表示是否屏蔽/阻塞相應的信號(SIGKILL,SIGSTOP除外)

進程間通信有哪些方式?

  1. 管道(Pipe)
展開
  • 管道是半雙工的,數據只能向一個方向流動;需要雙方通信時,需要建立起兩個管道;
  • 一個進程向管道中寫的內容被管道另一端的進程讀出。寫入的內容每次都添加在管道緩沖區的末尾,並且每次都是從緩沖區的頭部讀出數據;
  • 只能用於父子進程或者兄弟進程之間(具有親緣關系的進程)
  1. 命名管道
  2. 消息隊列
  3. 信號(Signal)
  4. 共享內存
  5. 信號量(Semaphore):初始化操作、P操作、V操作;P操作:信號量-1,檢測是否小於0,小於則進程進入阻塞狀態;V操作:信號量+1,若小於等於0,則從隊列中喚醒一個等待的進程進入就緒態
  6. 套接字(Socket)

更詳細的可以參考(待整理):

進程同步問題

進程的同步是目的,而進程間通信是實現進程同步的手段

管程 Monitor

管程將共享變量以及對這些共享變量的操作封裝起來,形成一個具有一定接口的功能模塊,這樣只能通過管程提供的某個過程才能訪問管程中的資源。進程只能互斥地使用管程,使用完之后必須釋放管程並喚醒入口等待隊列中的進程。

當一個進程試圖進入管程時,在入口等待隊列等待。若P進程喚醒了Q進程,則Q進程先執行,P在緊急等待隊列中等待。(HOARE管程

wait操作:執行wait操作的進程進入條件變量鏈末尾,喚醒緊急等待隊列或者入口隊列中的進程;signal操作:喚醒條件變量鏈中的進程,自己進入緊急等待隊列,若條件變量鏈為空,則繼續執行。(HOARE管程

MESA管程:將HOARE中的signal換成了notify(或者broadcast通知所有滿足條件的),進行通知而不是立馬交換管程的使用權,在合適的時候,條件隊列首位的進程可以進入,進入之前必須用while檢查條件是否合適。優點:沒有額外的進程切換

生產者-消費者問題

問題描述:使用一個緩沖區來存放數據,只有緩沖區沒有滿,生產者才可以寫入數據;只有緩沖區不為空,消費者才可以讀出數據

代碼實現:

// 偽代碼描述 
// 定義信號量 full記錄緩沖區物品數量 empty代表緩沖區空位數量 mutex為互斥量
semaphore full = 0, empty = n, mutex = 1;

// 生產者進程
void producer(){
	do{
   	  P(empty);
	  P(mutex);

     // 生產者進行生產
   	
   	  V(mutex);
   	  V(full);
 	} while(1);
}

void consumer(){
	do{
	  P(full);
	  P(mutex);

    	// 消費者進行消費

	  V(mutex);
	  V(empty);
 	} while(1);
}
 
哲學家就餐問題

問題描述:有五位哲學家圍繞着餐桌坐,每一位哲學家要么思考,要么吃飯。為了吃飯,哲學家必須拿起兩雙筷子(分別放於左右兩端)不幸的是,筷子的數量和哲學家相等,所以每只筷子必須由兩位哲學家共享。

代碼實現:

#define N 5  // number of philosopher
#define LEFT (i + N - 1)%N // number of i's left neighbors
#define RIGHT (i + 1)%N // number of i's right neighbors
#define THINKING 0
#define HUNGRY 1
#define EATING 2
typedef int semaphore;
int state[N]; // array to keep track of everyone's state
semaphore mutex = 1; // mutual exclusion of critical region
semaphore s[N]; 

void philosopher(int i) {
	while (TRUE) {
		think();
		take_forks(i);
		eat();
		put_forks(i);
	}
}

void take_forks(int i) {
	down(&mutex); // enter critical region
	state[i] = HUNGRY; // record that i is hungry
	test_forks(i); // try to acquire two forks
	up(&mutex); // exit critical region
	down(&s[i]); // block if forks are not acquired
}

void put_forks(int i) {
	down(&mutex); // enter critical region
	state[i] = THINKING; // record that has finished eating
	test_forks(LEFT); // see if left neighbor can now eat
	test_forks(RIGHT); // see if right neighbor can now eat
	up(&mutex); // exit critical region
}

void test_forks(int i) {
	if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) {
		state[i] = EATING;
		up(&s[i]);
	}
}
 
讀者-寫者問題
臨界區的概念?
展開

各個進程中對臨界資源(互斥資源/共享變量,一次只能給一個進程使用)進行操作的程序片段

同步與互斥的概念?
展開
  • 同步:多個進程因為合作而使得進程的執行有一定的先后順序。比如某個進程需要另一個進程提供的消息,獲得消息之前進入阻塞態;
  • 互斥:多個進程在同一時刻只有一個進程能進入臨界區
並發、並行、異步的區別?
展開

並發:在一個時間段中同時有多個程序在運行,但其實任一時刻,只有一個程序在CPU上運行,宏觀上的並發是通過不斷的切換實現的;

多線程:並發運行的一段代碼。是實現異步的手段

並行(和串行相比):在多CPU系統中,多個程序無論宏觀還是微觀上都是同時執行的

異步(和同步相比):同步是順序執行,異步是在等待某個資源的時候繼續做自己的事

進程有哪幾種狀態?

 

  • 就緒狀態:進程已獲得除處理機以外的所需資源,等待分配處理機資源
  • 運行狀態:占用處理機資源運行,處於此狀態的進程數小於等於CPU數
  • 阻塞狀態: 進程等待某種條件,在條件滿足之前無法執行

進程調度策略有哪些?

  1. 批處理系統
先來先服務 first-come first-serverd(FCFS)

按照請求的順序進行調度。非搶占式,開銷小,無飢餓問題,響應時間不確定(可能很慢);

對短進程不利,對IO密集型進程不利。

最短作業優先 shortest job first(SJF)

按估計運行時間最短的順序進行調度。非搶占式,吞吐量高,開銷可能較大,可能導致飢餓問題;

對短進程提供好的響應時間,對長進程不利。

最短剩余時間優先 shortest remaining time next(SRTN)

按剩余運行時間的順序進行調度。(最短作業優先的搶占式版本)。吞吐量高,開銷可能較大,提供好的響應時間;

可能導致飢餓問題,對長進程不利。

最高響應比優先 Highest Response Ratio Next(HRRN)

響應比 = 1+ 等待時間/處理時間。同時考慮了等待時間的長短和估計需要的執行時間長短,很好的平衡了長短進程。非搶占,吞吐量高,開銷可能較大,提供好的響應時間,無飢餓問題。

  1. 交互式系統
    交互式系統有大量的用戶交互操作,在該系統中調度算法的目標是快速地進行響應。
時間片輪轉 Round Robin

將所有就緒進程按 FCFS 的原則排成一個隊列,用完時間片的進程排到隊列最后。搶占式(時間片用完時),開銷小,無飢餓問題,為短進程提供好的響應時間;

若時間片小,進程切換頻繁,吞吐量低;若時間片太長,實時性得不到保證。

優先級調度算法

為每個進程分配一個優先級,按優先級進行調度。為了防止低優先級的進程永遠等不到調度,可以隨着時間的推移增加等待進程的優先級。

多級反饋隊列調度算法 Multilevel Feedback Queue

設置多個就緒隊列1、2、3...,優先級遞減,時間片遞增。只有等到優先級更高的隊列為空時才會調度當前隊列中的進程。如果進程用完了當前隊列的時間片還未執行完,則會被移到下一隊列。

搶占式(時間片用完時),開銷可能較大,對IO型進程有利,可能會出現飢餓問題。

什么叫優先級反轉?如何解決?
展開

高優先級的進程等待被一個低優先級進程占用的資源時,就會出現優先級反轉,即優先級較低的進程比優先級較高的進程先執行。此處詳細解釋優先級反轉帶來的問題:如果有一個中等優先級的進程將低優先級的進程搶占,那么此時低優先級的進程無法正常進行並在后續釋放被占用的資源,導致高優先級的任務一直被掛起,直到中等優先級的進程完成后,低優先級的進程才可以繼續並在后續釋放占用的資源,最后高優先級的進程才可以執行。導致的問題就是高優先級的進程在中等優先級的進程調度之后。

解決方法:

  • 優先級天花板(priority ceiling):當任務申請某資源時,把該任務的優先級提升到可訪問這個資源的所有任務中的最高優先級,這個優先級稱為該資源的優先級天花板。簡單易行。
  • 優先級繼承(priority inheritance):當任務A申請共享資源S時,如果S正在被任務C使用,通過比較任務C與自身的優先級,如發現任務C的優先級小於自身的優先級,則將任務C的優先級提升到自身的優先級,任務C釋放資源S后,再恢復任務C的原優先級。

什么是僵屍進程?

一個子進程結束后,它的父進程並沒有等待它(調用wait或者waitpid),那么這個子進程將成為一個僵屍進程。僵屍進程是一個已經死亡的進程,但是並沒有真正被銷毀。它已經放棄了幾乎所有內存空間,沒有任何可執行代碼,也不能被調度,僅僅在進程表中保留一個位置,記載該進程的進程ID、終止狀態以及資源利用信息(CPU時間,內存使用量等等)供父進程收集,除此之外,僵屍進程不再占有任何內存空間。這個僵屍進程可能會一直留在系統中直到系統重啟。

危害:占用進程號,而系統所能使用的進程號是有限的;占用內存。

以下情況不會產生僵屍進程:

  • 該進程的父進程先結束了。每個進程結束的時候,系統都會掃描是否存在子進程,如果有則用Init進程接管,成為該進程的父進程,並且會調用wait等待其結束。
  • 父進程調用wait或者waitpid等待子進程結束(需要每隔一段時間查詢子進程是否結束)。wait系統調用會使父進程暫停執行,直到它的一個子進程結束為止。waitpid則可以加入WNOHANG(wait-no-hang)選項,如果沒有發現結束的子進程,就會立即返回,不會將調用waitpid的進程阻塞。同時,waitpid還可以選擇是等待任一子進程(同wait),還是等待指定pid的子進程,還是等待同一進程組下的任一子進程,還是等待組ID等於pid的任一子進程;
  • 子進程結束時,系統會產生SIGCHLD(signal-child)信號,可以注冊一個信號處理函數,在該函數中調用waitpid,等待所有結束的子進程(注意:一般都需要循環調用waitpid,因為在信號處理函數開始執行之前,可能已經有多個子進程結束了,而信號處理函數只執行一次,所以要循環調用將所有結束的子進程回收);
  • 也可以用signal(SIGCLD, SIG_IGN)(signal-ignore)通知內核,表示忽略SIGCHLD信號,那么子進程結束后,內核會進行回收。
什么是孤兒進程?
展開

一個父進程已經結束了,但是它的子進程還在運行,那么這些子進程將成為孤兒進程。孤兒進程會被Init(進程ID為1)接管,當這些孤兒進程結束時由Init完成狀態收集工作。

線程同步有哪些方式?

為什么需要線程同步:線程有時候會和其他線程共享一些資源,比如內存、數據庫等。當多個線程同時讀寫同一份共享資源的時候,可能會發生沖突。因此需要線程的同步,多個線程按順序訪問資源。

  • 互斥量 Mutex:互斥量是內核對象,只有擁有互斥對象的線程才有訪問互斥資源的權限。因為互斥對象只有一個,所以可以保證互斥資源不會被多個線程同時訪問;當前擁有互斥對象的線程處理完任務后必須將互斥對象交出,以便其他線程訪問該資源;
  • 信號量 Semaphore:信號量是內核對象,它允許同一時刻多個線程訪問同一資源,但是需要控制同一時刻訪問此資源的最大線程數量。信號量對象保存了最大資源計數當前可用資源計數,每增加一個線程對共享資源的訪問,當前可用資源計數就減1,只要當前可用資源計數大於0,就可以發出信號量信號,如果為0,則將線程放入一個隊列中等待。線程處理完共享資源后,應在離開的同時通過ReleaseSemaphore函數將當前可用資源數加1。如果信號量的取值只能為0或1,那么信號量就成為了互斥量;
  • 事件 Event:允許一個線程在處理完一個任務后,主動喚醒另外一個線程執行任務。事件分為手動重置事件和自動重置事件。手動重置事件被設置為激發狀態后,會喚醒所有等待的線程,而且一直保持為激發狀態,直到程序重新把它設置為未激發狀態。自動重置事件被設置為激發狀態后,會喚醒一個等待中的線程,然后自動恢復為未激發狀態。
  • 臨界區 Critical Section:任意時刻只允許一個線程對臨界資源進行訪問。擁有臨界區對象的線程可以訪問該臨界資源,其它試圖訪問該資源的線程將被掛起,直到臨界區對象被釋放。
互斥量和臨界區有什么區別?
展開

互斥量是可以命名的,可以用於不同進程之間的同步;而臨界區只能用於同一進程中線程的同步。創建互斥量需要的資源更多,因此臨界區的優勢是速度快,節省資源。

什么是協程?

協程是一種用戶態的輕量級線程,協程的調度完全由用戶控制。協程擁有自己的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧,直接操作棧則基本沒有內核切換的開銷,可以不加鎖的訪問全局變量,所以上下文的切換非常快。

協程多與線程進行比較?
展開
  1. 一個線程可以擁有多個協程,一個進程也可以單獨擁有多個協程,這樣python中則能使用多核CPU。

  2. 線程進程都是同步機制,而協程則是異步

  3. 協程能保留上一次調用時的狀態,每次過程重入時,就相當於進入上一次調用的狀態

進程的異常控制流:陷阱、中斷、異常和信號

陷阱是有意造成的“異常”,是執行一條指令的結果。陷阱是同步的。陷阱的主要作用是實現系統調用。比如,進程可以執行 syscall n 指令向內核請求服務。當進程執行這條指令后,會中斷當前的控制流,陷入到內核態,執行相應的系統調用。內核的處理程序在執行結束后,會將結果返回給進程,同時退回到用戶態。進程此時繼續執行下一條指令

中斷由處理器外部硬件產生,不是執行某條指令的結果,也無法預測發生時機。由於中斷獨立於當前執行的程序,因此中斷是異步事件。中斷包括 I/O 設備發出的 I/O 中斷、各種定時器引起的時鍾中斷、調試程序中設置的斷點等引起的調試中斷等。

異常是一種錯誤情況,是執行當前指令的結果,可能被錯誤處理程序修正,也可能直接終止應用程序。異常是同步的。這里特指因為執行當前指令而產生的錯誤情況,比如除法異常、缺頁異常等。有些書上為了區分,也將這類“異常”稱為**“故障”**。

信號是一種更高層的軟件形式的異常,同樣會中斷進程的控制流,可以由進程進行處理。一個信號代表了一個消息。信號的作用是用來通知進程發生了某種系統事件。

更詳細的可以參考:https://imageslr.github.io/2020/07/09/trap-interrupt-exception.html

什么是IO多路復用?怎么實現?

IO多路復用(IO Multiplexing)是指單個進程/線程就可以同時處理多個IO請求。

實現原理:用戶將想要監視的文件描述符(File Descriptor)添加到select/poll/epoll函數中,由內核監視,函數阻塞。一旦有文件描述符就緒(讀就緒或寫就緒),或者超時(設置timeout),函數就會返回,然后該進程可以進行相應的讀/寫操作。

select/poll/epoll三者的區別?
  • select:將文件描述符放入一個集合中,調用select時,將這個集合從用戶空間拷貝到內核空間(缺點1:每次都要復制,開銷大),由內核根據就緒狀態修改該集合的內容。(缺點2)集合大小有限制,32位機默認是1024(64位:2048);采用水平觸發機制。select函數返回后,需要通過遍歷這個集合,找到就緒的文件描述符(缺點3:輪詢的方式效率較低),當文件描述符的數量增加時,效率會線性下降;
  • poll:和select幾乎沒有區別,區別在於文件描述符的存儲方式不同,poll采用鏈表的方式存儲,沒有最大存儲數量的限制;
  • epoll:通過內核和用戶空間共享內存,避免了不斷復制的問題;支持的同時連接數上限很高(1G左右的內存支持10W左右的連接數);文件描述符就緒時,采用回調機制,避免了輪詢(回調函數將就緒的描述符添加到一個鏈表中,執行epoll_wait時,返回這個鏈表);支持水平觸發和邊緣觸發,采用邊緣觸發機制時,只有活躍的描述符才會觸發回調函數。

總結,區別主要在於:

  • 一個線程/進程所能打開的最大連接數
  • 文件描述符傳遞方式(是否復制)
  • 水平觸發 or 邊緣觸發
  • 查詢就緒的描述符時的效率(是否輪詢)
什么時候使用select/poll,什么時候使用epoll?

當連接數較多並且有很多的不活躍連接時,epoll的效率比其它兩者高很多;但是當連接數較少並且都十分活躍的情況下,由於epoll需要很多回調,因此性能可能低於其它兩者。

什么是文件描述符?

文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核為每一個進程所維護的該進程打開文件的記錄表。當程序打開一個現有文件或者創建一個新文件時,內核向進程返回一個文件描述符。

內核通過文件描述符來訪問文件。文件描述符指向一個文件。

什么是水平觸發?什么是邊緣觸發?
展開
  • 水平觸發(LT,Level Trigger)模式下,只要一個文件描述符就緒,就會觸發通知,如果用戶程序沒有一次性把數據讀寫完,下次還會通知;
  • 邊緣觸發(ET,Edge Trigger)模式下,當描述符從未就緒變為就緒時通知一次,之后不會再通知,直到再次從未就緒變為就緒(緩沖區從不可讀/寫變為可讀/寫)。
  • 區別:邊緣觸發效率更高,減少了被重復觸發的次數,函數不會返回大量用戶程序可能不需要的文件描述符。
  • 為什么邊緣觸發一定要用非阻塞(non-block)IO:避免由於一個描述符的阻塞讀/阻塞寫操作讓處理其它描述符的任務出現飢餓狀態。
有哪些常見的IO模型?
展開
  • 同步阻塞IO(Blocking IO):用戶線程發起IO讀/寫操作之后,線程阻塞,直到可以開始處理數據;對CPU資源的利用率不夠;
  • 同步非阻塞IO(Non-blocking IO):發起IO請求之后可以立即返回,如果沒有就緒的數據,需要不斷地發起IO請求直到數據就緒;不斷重復請求消耗了大量的CPU資源;
  • IO多路復用
  • 異步IO(Asynchronous IO):用戶線程發出IO請求之后,繼續執行,由內核進行數據的讀取並放在用戶指定的緩沖區內,在IO完成之后通知用戶線程直接使用。

什么是用戶態和內核態?

為了限制不同程序的訪問能力,防止一些程序訪問其它程序的內存數據,CPU划分了用戶態和內核態兩個權限等級。

  • 用戶態只能受限地訪問內存,且不允許訪問外圍設備,沒有占用CPU的能力,CPU資源可以被其它程序獲取;
  • 內核態可以訪問內存所有數據以及外圍設備,也可以進行程序的切換。

所有用戶程序都運行在用戶態,但有時需要進行一些內核態的操作,比如從硬盤或者鍵盤讀數據,這時就需要進行系統調用,使用陷阱指令,CPU切換到內核態,執行相應的服務,再切換為用戶態並返回系統調用的結果。

為什么要分用戶態和內核態?
展開

(我自己的見解:)

  • 安全性:防止用戶程序惡意或者不小心破壞系統/內存/硬件資源;
  • 封裝性:用戶程序不需要實現更加底層的代碼;
  • 利於調度:如果多個用戶程序都在等待鍵盤輸入,這時就需要進行調度;統一交給操作系統調度更加方便。
如何從用戶態切換到內核態?
展開
  • 系統調用:比如讀取命令行輸入。本質上還是通過中斷實現
  • 用戶程序發生異常時:比如缺頁異常
  • 外圍設備的中斷:外圍設備完成用戶請求的操作之后,會向CPU發出中斷信號,這時CPU會轉去處理對應的中斷處理程序

什么是死鎖?

在兩個或者多個並發進程中,每個進程持有某種資源而又等待其它進程釋放它們現在保持着的資源,在未改變這種狀態之前都不能向前推進,稱這一組進程產生了死鎖(deadlock)。

死鎖產生的必要條件?

  • 互斥:一個資源一次只能被一個進程使用;
  • 占有並等待:一個進程至少占有一個資源,並在等待另一個被其它進程占用的資源;
  • 非搶占:已經分配給一個進程的資源不能被強制性搶占,只能由進程完成任務之后自願釋放;
  • 循環等待:若干進程之間形成一種頭尾相接的環形等待資源關系,該環路中的每個進程都在等待下一個進程所占有的資源。

死鎖有哪些處理方法?

鴕鳥策略

直接忽略死鎖。因為解決死鎖問題的代價很高,因此鴕鳥策略這種不采取任務措施的方案會獲得更高的性能。當發生死鎖時不會對用戶造成多大影響,或發生死鎖的概率很低,可以采用鴕鳥策略。

死鎖預防

基本思想是破壞形成死鎖的四個必要條件:

  • 破壞互斥條件:允許某些資源同時被多個進程訪問。但是有些資源本身並不具有這種屬性,因此這種方案實用性有限;
  • 破壞占有並等待條件:
    • 實行資源預先分配策略(當一個進程開始運行之前,必須一次性向系統申請它所需要的全部資源,否則不運行);
    • 或者只允許進程在沒有占用資源的時候才能申請資源(申請資源前先釋放占有的資源);
    • 缺點:很多時候無法預知一個進程所需的全部資源;同時,會降低資源利用率,降低系統的並發性;
  • 破壞非搶占條件:允許進程強行搶占被其它進程占有的資源。會降低系統性能;
  • 破壞循環等待條件:對所有資源統一編號,所有進程對資源的請求必須按照序號遞增的順序提出,即只有占有了編號較小的資源才能申請編號較大的資源。這樣避免了占有大號資源的進程去申請小號資源。
死鎖避免

動態地檢測資源分配狀態,以確保系統處於安全狀態,只有處於安全狀態時才會進行資源的分配。所謂安全狀態是指:即使所有進程突然請求需要的所有資源,也能存在某種對進程的資源分配順序,使得每一個進程運行完畢。

銀行家算法

死鎖解除

如何檢測死鎖:檢測有向圖是否存在環;或者使用類似死鎖避免的檢測算法。

死鎖解除的方法:

  • 利用搶占:掛起某些進程,並搶占它的資源。但應防止某些進程被長時間掛起而處於飢餓狀態;
  • 利用回滾:讓某些進程回退到足以解除死鎖的地步,進程回退時自願釋放資源。要求系統保持進程的歷史信息,設置還原點;
  • 利用殺死進程:強制殺死某些進程直到死鎖解除為止,可以按照優先級進行。

分頁和分段有什么區別?

  • 頁式存儲:用戶空間划分為大小相等的部分稱為頁(page),內存空間划分為同樣大小的區域稱為頁框,分配時以頁為單位,按進程需要的頁數分配,邏輯上相鄰的頁物理上不一定相鄰;
  • 段式存儲:用戶進程地址空間按照自身邏輯關系划分為若干個段(segment)(如代碼段,數據段,堆棧段),內存空間被動態划分為長度不同的區域,分配時以段為單位,每段在內存中占據連續空間,各段可以不相鄰;
  • 段頁式存儲:用戶進程先按段划分,段內再按頁划分,內存划分和分配按頁。

區別:

  • 目的不同:分頁的目的是管理內存,用於虛擬內存以獲得更大的地址空間;分段的目的是滿足用戶的需要,使程序和數據可以被划分為邏輯上獨立的地址空間;
  • 大小不同:段的大小不固定,由其所完成的功能決定;頁的大小固定,由系統決定;
  • 地址空間維度不同:分段是二維地址空間(段號+段內偏移),分頁是一維地址空間(每個進程一個頁表/多級頁表,通過一個邏輯地址就能找到對應的物理地址);
  • 分段便於信息的保護和共享;分頁的共享收到限制;
  • 碎片:分段沒有內碎片,但會產生外碎片;分頁沒有外碎片,但會產生內碎片(一個頁填不滿)

什么是虛擬內存?

每個程序都擁有自己的地址空間,這個地址空間被分成大小相等的頁,這些頁被映射到物理內存;但不需要所有的頁都在物理內存中,當程序引用到不在物理內存中的頁時,由操作系統將缺失的部分裝入物理內存。這樣,對於程序來說,邏輯上似乎有很大的內存空間,只是實際上有一部分是存儲在磁盤上,因此叫做虛擬內存。

虛擬內存的優點是讓程序可以獲得更多的可用內存。

虛擬內存的實現方式、頁表/多級頁表、缺頁中斷、不同的頁面淘汰算法:答案

如何進行地址空間到物理內存的映射?
展開

內存管理單元(MMU)管理着邏輯地址和物理地址的轉換,其中的頁表(Page table)存儲着頁(邏輯地址)和頁框(物理內存空間)的映射表,頁表中還包含包含有效位(是在內存還是磁盤)、訪問位(是否被訪問過)、修改位(內存中是否被修改過)、保護位(只讀還是可讀寫)。邏輯地址:頁號+頁內地址(偏移);每個進程一個頁表,放在內存,頁表起始地址在PCB/寄存器中。

有哪些頁面置換算法?

在程序運行過程中,如果要訪問的頁面不在內存中,就發生缺頁中斷從而將該頁調入內存中。此時如果內存已無空閑空間,系統必須從內存中調出一個頁面到磁盤中來騰出空間。頁面置換算法的主要目標是使頁面置換頻率最低(也可以說缺頁率最低)。

  • 最佳頁面置換算法OPT(Optimal replacement algorithm):置換以后不需要或者最遠的將來才需要的頁面,是一種理論上的算法,是最優策略;
  • 先進先出FIFO:置換在內存中駐留時間最長的頁面。缺點:有可能將那些經常被訪問的頁面也被換出,從而使缺頁率升高;
  • 第二次機會算法SCR:按FIFO選擇某一頁面,若其訪問位為1,給第二次機會,並將訪問位置0;
  • 時鍾算法 Clock:SCR中需要將頁面在鏈表中移動(第二次機會的時候要將這個頁面從鏈表頭移到鏈表尾),時鍾算法使用環形鏈表,再使用一個指針指向最老的頁面,避免了移動頁面的開銷;
  • 最近未使用算法NRU(Not Recently Used):檢查訪問位R、修改位M,優先置換R=M=0,其次是(R=0, M=1);
  • 最近最少使用算法LRU(Least Recently Used):置換出未使用時間最長的一頁;實現方式:維護時間戳,或者維護一個所有頁面的鏈表。當一個頁面被訪問時,將這個頁面移到鏈表表頭。這樣就能保證鏈表表尾的頁面是最近最久未訪問的。
  • 最不經常使用算法NFU:置換出訪問次數最少的頁面
局部性原理
  • 時間上:最近被訪問的頁在不久的將來還會被訪問;
  • 空間上:內存中被訪問的頁周圍的頁也很可能被訪問。
什么是顛簸現象

顛簸本質上是指頻繁的頁調度行為。進程發生缺頁中斷時必須置換某一頁。然而,其他所有的頁都在使用,它置換一個頁,但又立刻再次需要這個頁。因此會不斷產生缺頁中斷,導致整個系統的效率急劇下降,這種現象稱為顛簸。內存顛簸的解決策略包括:

  • 修改頁面置換算法;
  • 降低同時運行的程序的數量;
  • 終止該進程或增加物理內存容量。

緩沖區溢出問題

什么是緩沖區溢出? C 語言使用運行時棧來存儲過程信息。每個函數的信息存儲在一個棧幀中,包括寄存器、局部變量、參數、返回地址等。C 對於數組引用不進行任何邊界檢查,因此**對越界的數組元素的寫操作會破壞存儲在棧中的狀態信息**,這種現象稱為緩沖區溢出。緩沖區溢出會破壞程序運行,也可以被用來進行攻擊計算機,如使用一個指向攻擊代碼的指針覆蓋返回地址。緩沖區溢出的防范方式

防范緩沖區溢出攻擊的機制有三種:隨機化、棧保護和限制可執行代碼區域。

  • 隨機化:包括棧隨機化(程序開始時在棧上分配一段隨機大小的空間)和地址空間布局隨機化(Address-Space Layout Randomization,ASLR,即每次運行時程序的不同部分,包括代碼段、數據段、棧、堆等都會被加載到內存空間的不同區域),但只能增加攻擊一個系統的難度,不能完全保證安全。
  • 棧保護:在每個函數的棧幀的局部變量和棧狀態之間存儲一個隨機產生的特殊的值,稱為金絲雀值(canary)。在恢復寄存器狀態和函數返回之前,程序檢測這個金絲雀值是否被改變了,如果是,那么程序異常終止。
  • 限制可執行代碼區域:內存頁的訪問形式有三種:可讀、可寫、可執行,只有編譯器產生的那部分代碼所處的內存才是可執行的,其他頁限制為只允許讀和寫。

更詳細的可以參考:https://imageslr.github.io/2020/07/08/tech-interview.html#stackoverflow

磁盤調度

過程:磁頭(找到對應的盤面);磁道(一個盤面上的同心圓環,尋道時間);扇區(旋轉時間)。為減小尋道時間的調度算法:

  • 先來先服務
  • 最短尋道時間優先
  • 電梯算法:電梯總是保持一個方向運行,直到該方向沒有請求為止,然后改變運行方向。

參考

待完成

  • IPC
  • 進程同步問題:生產者-消費者問題...
  • 銀行家算法
  • 文件與文件系統、文件管理?
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM