面試題-作業系統基礎
程序和執行緒的區別
程序:是資源分配的最小單位,一個程序可以有多個執行緒,多個執行緒共享程序的堆和方法區資源,不共享棧、程式計數器
執行緒:是任務排程和執行的最小單位,執行緒並行執行存在資源競爭和上下文切換的問題
協程:是一種比執行緒更加輕量級的存在,正如一個程序可以擁有多個執行緒一樣,一個執行緒可以擁有多個協程。
程序間通訊方式IPC
管道pipe:
親緣關係使用匿名管道,非親緣關係使用命名管道,管道遵循FIFO,半雙工,資料只能單向通訊;
訊號:
訊號是一種比較複雜的通訊方式,使用者呼叫kill命令將訊號傳送給其他程序。
訊息佇列:
訊息佇列克服了訊號傳遞資訊少,管道只能承載無格式位元組流以及緩衝區大小受限等特點。
共享記憶體(share memory):
- 使得多個程序可以可以直接讀寫同一塊記憶體空間,是最快的可用IPC形式。是針對其他通訊機制執行效率較低而設計的。
- 由於多個程序共享一段記憶體,因此需要依靠某種同步機制(如訊號量)來達到程序間的同步及互斥。
訊號量(Semaphores) :
訊號量是⼀個計數器,⽤於多程序對共享資料的訪問,這種通訊⽅式主要⽤於解決與同步相關的問題並避免競爭條件。
套接字(Sockets) :
簡單的說就是通訊的兩⽅的⼀種約定,⽤套接字中的相關函式來完成通訊過程。
使用者態和核心態
使用者態:只能受限的訪問記憶體,執行所有的應用程式
核心態:執行作業系統程式,cpu可以訪問記憶體的所有資料,包括外圍裝置
為什麼要有使用者態和核心態:
由於需要限制不同的程式之間的訪問能力, 防止他們獲取別的程式的記憶體資料, 或者獲取外圍裝置的資料, 併發送到網路
使用者態切換到核心態的3種方式:
a. 系統呼叫
主動呼叫,系統呼叫的機制其核心還是使用了作業系統為使用者特別開放的一箇中斷來實現,例如Linux的int 80h中斷。
b. 異常
當CPU在執行執行在使用者態下的程式時,發生了某些事先不可知的異常,比如缺頁異常,這時會觸發切換核心態處理異常。
c. 外圍裝置的中斷
當外圍裝置完成使用者請求的操作後,會向CPU發出相應的中斷訊號,這時CPU會由使用者態到核心態的切換。
作業系統的程序空間
棧區(stack)— 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。
堆區(heap)— 一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時可能由OS回收 。
靜態區(static)—存放全域性變數和靜態變數的儲存
程式碼區(text)—存放函式體的二進位制程式碼。
執行緒共享堆區、靜態區
作業系統記憶體管理
存管理方式:頁式管理、段式管理、段頁式管理
分段管理:
將程式的地址空間劃分為若干段(segment),如程式碼段,資料段,堆疊段;這樣每個程序有一個二維地址空間,相互獨立,互不干擾。段式管理的優點是:沒有內碎片(因為段大小可變,改變段大小來消除內碎片)。但段換入換出時,會產生外碎片(比如4k的段換5k的段,會產生1k的外碎片)
分頁管理:
在頁式儲存管理中,將程式的邏輯地址劃分為固定大小的頁(page),而實體記憶體劃分為同樣大小的頁框,程式載入時,可以將任意一頁放入記憶體中任意一個頁框,這些頁框不必連續,從而實現了離散分離。頁式儲存管理的優點是:沒有外碎片(因為頁的大小固定),但會產生內碎片(一個頁可能填充不滿)
段頁式管理:
段⻚式管理機制結合了段式管理和⻚式管理的優點。簡單來說段⻚式管理機制就是把主存先分成若⼲段,每個段⼜分成若⼲⻚,也就是說 段⻚式管理機制 中段與段之間以及段的內部的都是離散的
頁面置換演算法FIFO、LRU
置換演算法:先進先出FIFO、最近最久未使用LRU、最佳置換演算法OPT
先進先出FIFO:
缺點:沒有考慮到實際的頁面使用頻率,效能差、與通常頁面使用的規則不符合,實際應用較少
最近最久未使用LRU:
原理:選擇最近且最久未使用的頁面進行淘汰
優點:考慮到了程式訪問的時間區域性性,有較好的效能,實際應用也比較多
缺點:沒有合適的演算法,只有適合的演算法,lFU、random都可以
/** * @program: Java * @description: LRU最近最久未使用置換演算法,通過LinkedHashMap實現 * @author: Mr.Li * @create: 2020-07-17 10:29 **/ public class LRUCache { private LinkedHashMap<Integer,Integer> cache; private int capacity; //容量大小 /** *初始化建構函式 * @param capacity */ public LRUCache(int capacity) { cache = new LinkedHashMap<>(capacity); this.capacity = capacity; } public int get(int key) { //快取中不存在此key,直接返回 if(!cache.containsKey(key)) { return -1; } int res = cache.get(key); cache.remove(key); //先從連結串列中刪除 cache.put(key,res); //再把該節點放到連結串列末尾處 return res; } public void put(int key,int value) { if(cache.containsKey(key)) { cache.remove(key); //已經存在,在當前連結串列移除 } if(capacity == cache.size()) { //cache已滿,刪除連結串列頭位置 Set<Integer> keySet = cache.keySet(); Iterator<Integer> iterator = keySet.iterator(); cache.remove(iterator.next()); } cache.put(key,value); //插入到連結串列末尾 } }
/** * @program: Java * @description: LRU最近最久未使用置換演算法,通過LinkedHashMap內部removeEldestEntry方法實現 * @author: Mr.Li * @create: 2020-07-17 10:59 **/ class LRUCache { private Map<Integer, Integer> map; private int capacity; /** *初始化建構函式 * @param capacity */ public LRUCache(int capacity) { this.capacity = capacity; map = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > capacity; // 容量大於capacity 時就刪除 } }; } public int get(int key) { //返回key對應的value值,若不存在,返回-1 return map.getOrDefault(key, -1); } public void put(int key, int value) { map.put(key, value); } }
最佳置換演算法OPT:
原理:每次選擇當前物理塊中的頁面在未來長時間不被訪問的或未來不再使用的頁面進行淘汰
優點:具有較好的效能,可以保證獲得最低的缺頁率
缺點:過於理想化,但是實際上無法實現(沒辦法預知未來的頁面)
死鎖條件、解決方式:
死鎖是指兩個或兩個以上程序在執行過程中,因爭奪資源而造成的下相互等待的現象;
死鎖的條件:
互斥條件:程序對所分配到的資源不允許其他程序訪問,若其他程序訪問該資源,只能等待至佔有該資源的程序釋放該資源;
請求與保持條件:程序獲得一定的資源後,又對其他資源發出請求,阻塞過程中不會釋放自己已經佔有的資源
非剝奪條件:程序已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用後自己釋放
迴圈等待條件:系統中若干程序組成環路,環路中每個程序都在等待相鄰程序佔用的資源
解決方法:破壞死鎖的任意一條件
樂觀鎖,破壞資源互斥條件,CAS
資源一次性分配,從而剝奪請求和保持條件、tryLock
可剝奪資源:即當程序新的資源未得到滿足時,釋放已佔有的資源,從而破壞不可剝奪的條件,資料庫deadlock超時
資源有序分配法:系統給每類資源賦予一個序號,每個程序按編號遞增的請求資源,從而破壞環路等待的條件,轉賬場景