1. 程式人生 > 其它 >C++面經(2):作業系統

C++面經(2):作業系統

1. 請你說一說程序和執行緒區別

1)程序是cpu資源分配的最小單位,執行緒是cpu排程的最小單位。

2)程序有獨立的系統資源,而同一程序內的執行緒共享程序的大部分系統資源,包括堆、程式碼段、資料段,每個執行緒只擁有一些在執行中必不可少的私有屬性,比如tcb,執行緒Id,棧、暫存器。

3)一個程序崩潰,不會對其他程序產生影響;而一個執行緒崩潰,會讓同一程序內的其他執行緒也死掉。

4)程序在建立、切換和銷燬時開銷比較大,而執行緒比較小。程序建立的時候需要分配系統資源,而銷燬的的時候需要釋放系統資源。程序切換需要分兩步:切換頁目錄、重新整理TLB以使用新的地址空間;切換核心棧和硬體上下文(暫存器);而同一程序的執行緒間邏輯地址空間是一樣的,不需要切換頁目錄、重新整理TLB。

5)程序間通訊比較複雜,而同一程序的執行緒由於共享程式碼段和資料段,所以通訊比較容易。

2. 請問程序間怎麼通訊

程序間通訊主要包括管道、系統IPC(包括訊息佇列、訊號量、訊號、共享記憶體等)、以及套接字socket。

1.管道:

管道主要包括無名管道和命名管道:管道可用於具有親緣關係的父子程序間的通訊,有名管道除了具有管道所具有的功能外,它還允許無親緣關係程序間的通訊

1.1 普通管道PIPE:

1)它是半雙工的(即資料只能在一個方向上流動),具有固定的讀端和寫端

2)它只能用於具有親緣關係的程序之間的通訊(也是父子程序或者兄弟程序之間)

3)它可以看成是一種特殊的檔案,對於它的讀寫也可以使用普通的read、write等函式。但是它不是普通的檔案,並不屬於其他任何檔案系統,並且只存在於記憶體中。

1.2 命名管道FIFO:

1)FIFO可以在無關的程序之間交換資料

2)FIFO有路徑名與之相關聯,它以一種特殊裝置檔案形式存在於檔案系統中。

  1. 系統IPC:

2.1 訊息佇列

訊息佇列,是訊息的連結表,存放在核心中。一個訊息佇列由一個識別符號(即佇列ID)來標記。 (訊息佇列克服了訊號傳遞資訊少,管道只能承載無格式位元組流以及緩衝區大小受限等特點)具有寫許可權得程序可以按照一定得規則向訊息佇列中新增新資訊;對訊息佇列有讀許可權得程序則可以從訊息佇列中讀取資訊;

特點:

1)訊息佇列是面向記錄的,其中的訊息具有特定的格式以及特定的優先順序。

2)訊息佇列獨立於傳送與接收程序。程序終止時,訊息佇列及其內容並不會被刪除。

3)訊息佇列可以實現訊息的隨機查詢,訊息不一定要以先進先出的次序讀取,也可以按訊息的型別讀取。

2.2 訊號量semaphore

訊號量(semaphore)與已經介紹過的 IPC 結構不同,它是一個計數器,可以用來控制多個程序對共享資源的訪問。訊號量用於實現程序間的互斥與同步,而不是用於儲存程序間通訊資料。

特點:

1)訊號量用於程序間同步,若要在程序間傳遞資料需要結合共享記憶體。

2)訊號量基於作業系統的 PV 操作,程式對訊號量的操作都是原子操作。

3)每次對訊號量的 PV 操作不僅限於對訊號量值加 1 或減 1,而且可以加減任意正整數。

4)支援訊號量組。

2.3 訊號signal

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

2.4 共享記憶體(Shared Memory)

它使得多個程序可以訪問同一塊記憶體空間,不同程序可以及時看到對方程序中對共享記憶體中資料得更新。這種方式需要依靠某種同步操作,如互斥鎖和訊號量等

特點:

1)共享記憶體是最快的一種IPC,因為程序是直接對記憶體進行存取

2)因為多個程序可以同時操作,所以需要進行同步

3)訊號量+共享記憶體通常結合在一起使用,訊號量用來同步對共享記憶體的訪問

3.套接字SOCKET:

socket也是一種程序間通訊機制,與其他通訊機制不同的是,它可用於不同主機之間的程序通訊。

3. 請你講述一下互斥鎖(mutex)機制,以及互斥鎖和讀寫鎖的區別

4. 請你說一說程序狀態轉換圖,動態就緒,靜態就緒,動態阻塞,靜態阻塞

5. C++的鎖你知道幾種?

鎖包括互斥鎖,條件變數,自旋鎖和讀寫鎖

6. 怎樣確定當前執行緒是繁忙還是阻塞?

使用ps命令檢視

7. 請你說一說死鎖產生的必要條件?

  1. 互斥條件:一個資源每次只能被一個程序使用。
  2. 請求與保持條件:一個程序因請求資源而阻塞時,對已獲得的資源保持不放。
  3. 不剝奪條件:程序已獲得的資源,在末使用完之前,不能強行剝奪。
  4. 迴圈等待條件:若干程序之間形成一種頭尾相接的迴圈等待資源關係。

8. 請你說一說記憶體溢位和記憶體洩漏

1、記憶體溢位
指程式申請記憶體時,沒有足夠的記憶體供申請者使用。記憶體溢位就是你要的記憶體空間超過了系統實際分配給你的空間,此時系統相當於沒法滿足你的需求,就會報記憶體溢位的錯誤

記憶體溢位原因:

記憶體中載入的資料量過於龐大,如一次從資料庫取出過多資料

集合類中有對物件的引用,使用完後未清空,使得不能回收

程式碼中存在死迴圈或迴圈產生過多重複的物件實體

使用的第三方軟體中的BUG

啟動引數記憶體值設定的過小

2、記憶體洩漏

記憶體洩漏是指由於疏忽或錯誤造成了程式未能釋放掉不再使用的記憶體的情況。記憶體洩漏並非指記憶體在物理上的消失,而是應用程式分配某段記憶體後,由於設計錯誤,失去了對該段記憶體的控制,因而造成了記憶體的浪費。

記憶體洩漏的分類:

1、堆記憶體洩漏 (Heap leak)。對記憶體指的是程式執行中根據需要分配通過malloc,realloc new等從堆中分配的一塊記憶體,再是完成後必須通過呼叫對應的 free或者delete 刪掉。如果程式的設計的錯誤導致這部分記憶體沒有被釋放,那麼此後這塊記憶體將不會被使用,就會產生Heap Leak。

2、系統資源洩露(Resource Leak)。主要指程式使用系統分配的資源比如 Bitmap,handle ,SOCKET等沒有使用相應的函式釋放掉,導致系統資源的浪費,嚴重可導致系統效能降低,系統執行不穩定。

3、沒有將基類的解構函式定義為虛擬函式。當基類指標指向子類物件時,如果基類的解構函式不是virtual,那麼子類的解構函式將不會被呼叫,子類的資源沒有正確是釋放,因此造成記憶體洩露。

9. 你知道常用的執行緒模型嗎?

1. Future模型

該模型通常在使用的時候需要結合Callable介面配合使用。

Future是把結果放在將來獲取,當前主執行緒並不急於獲取處理結果。允許子執行緒先進行處理一段時間,處理結束之後就把結果儲存下來,當主執行緒需要使用的時候再向子執行緒索取。

Callable是類似於Runnable的介面,其中call方法類似於run方法,所不同的是run方法不能丟擲受檢異常沒有返回值,而call方法則可以丟擲受檢異常並可設定返回值。兩者的方法體都是執行緒執行體。

2. fork&join模型

該模型包含遞迴思想和回溯思想,遞迴用來拆分任務,回溯用合併結果。可以用來處理一些可以進行拆分的大任務。其主要是把一個大任務逐級拆分為多個子任務,然後分別在子執行緒中執行,當每個子執行緒執行結束之後逐級回溯,返回結果進行彙總合併,最終得出想要的結果。

這裡模擬一個摘蘋果的場景:有100棵蘋果樹,每棵蘋果樹有10個蘋果,現在要把他們摘下來。為了節約時間,規定每個執行緒最多隻能摘10棵蘋樹以便於節約時間。各個執行緒摘完之後彙總計算總蘋果樹。

3. actor模型

actor模型屬於一種基於訊息傳遞機制並行任務處理思想,它以訊息的形式來進行執行緒間資料傳輸,避免了全域性變數的使用,進而避免了資料同步錯誤的隱患。actor在接受到訊息之後可以自己進行處理,也可以繼續傳遞(分發)給其它actor進行處理。在使用actor模型的時候需要使用第三方Akka提供的框架。

4. 生產者消費者模型

生產者消費者模型都比較熟悉,其核心是使用一個快取來儲存任務。開啟一個/多個執行緒來生產任務,然後再開啟一個/多個來從快取中取出任務進行處理。這樣的好處是任務的生成和處理分隔開,生產者不需要處理任務,只負責向生成任務然後儲存到快取。而消費者只需要從快取中取出任務進行處理。使用的時候可以根據任務的生成情況和處理情況開啟不同的執行緒來處理。比如,生成的任務速度較快,那麼就可以靈活的多開啟幾個消費者執行緒進行處理,這樣就可以避免任務的處理響應緩慢的問題。

5. master-worker模型

master-worker模型類似於任務分發策略,開啟一個master執行緒接收任務,然後在master中根據任務的具體情況進行分發給其它worker子執行緒,然後由子執行緒處理任務。如需返回結果,則worker處理結束之後把處理結果返回給master。

10. 請你來說一說協程

11. 請你來介紹一下5種IO模型

  1. 阻塞IO:呼叫者呼叫了某個函式,等待這個函式返回,期間什麼也不做,不停的去檢查這個函式有沒有返回,必須等這個函式返回才能進行下一步動作
  2. 非阻塞IO:非阻塞等待,每隔一段時間就去檢測IO事件是否就緒。沒有就緒就可以做其他事。
  3. 訊號驅動IO:訊號驅動IO:linux用套介面進行訊號驅動IO,安裝一個訊號處理函式,程序繼續執行並不阻塞,當IO時間就緒,程序收到SIGIO訊號。然後處理IO事件。
  4. IO複用/多路轉接IO:linux用select/poll函式實現IO複用模型,這兩個函式也會使程序阻塞,但是和阻塞IO所不同的是這兩個函式可以同時阻塞多個IO操作。而且可以同時對多個讀操作、寫操作的IO函式進行檢測。知道有資料可讀或可寫時,才真正呼叫IO操作函式
  5. 非同步IO:linux中,可以呼叫aio_read函式告訴核心描述字緩衝區指標和緩衝區的大小、檔案偏移及通知的方式,然後立即返回,當核心將資料拷貝到緩衝區後,再通知應用程式。

12. 就緒狀態的程序在等待什麼?

被排程使用cpu的執行權

13.