1. 程式人生 > 實用技巧 >C++面試回答

C++面試回答

PS:主要記錄一下瀏覽牛客面經過程中,經常遇到的面試回答問題,沒有先後次序之分,題目多摘於牛客網,答案是自己所搜尋而來,如有錯誤,還請告知。

1. vector、list、stack底層實現的原理、使用場景、特點對比?

a.vector是一個可變化長度的陣列;

b. list低層使用連結串列實現的;

c.底層也是用陣列實現的,因為繼承了Vector。

2. 過載和重寫的區別

過載:兩個函式名相同,但是引數列表不同(個數,型別),返回值型別沒有要求,在同一作用域中; 重寫:子類繼承了父類,父類中的函式是虛擬函式,在子類中重新定義了這個虛擬函式,這種情況是重寫;

3. 子類繼承二個有虛擬函式的父類,那麼子類有幾個虛擬函式表

4. static關鍵字的作用

a. 全域性靜態變數

在全域性變數前加上關鍵字static,全域性變數就定義成一個全域性靜態變數.

靜態儲存區,在整個程式執行期間一直存在。

初始化:未經初始化的全域性靜態變數會被自動初始化為0(自動物件的值是任意的,除非他被顯式初始化);

作用域:全域性靜態變數在宣告他的檔案之外是不可見的,準確地說是從定義之處開始,到檔案結尾。

b. 區域性靜態變數

在區域性變數之前加上關鍵字static,區域性變數就成為一個區域性靜態變數。

記憶體中的位置:靜態儲存區

初始化:未經初始化的全域性靜態變數會被自動初始化為0(自動物件的值是任意的,除非他被顯式初始化);

作用域:作用域仍為區域性作用域,當定義它的函式或者語句塊結束的時候,作用域結束。但是當局部靜態變數離開作用域後,並沒有銷燬,而是仍然駐留在記憶體當中,只不過我們不能再對它進行訪問,直到該函式再次被呼叫,並且值不變;

c. 靜態函式

在函式返回型別前加static,函式就定義為靜態函式。函式的定義和宣告在預設情況下都是extern的,但靜態函式只是在宣告他的檔案當中可見,不能被其他檔案所用。

函式的實現使用static修飾,那麼這個函式只可在本cpp內使用,不會同其他cpp中的同名函式引起衝突;

warning:不要再標頭檔案中宣告static的全域性函式,不要在cpp內宣告非static的全域性函式,如果你要在多個cpp中複用該函式,就把它的宣告提到標頭檔案裡去,否則cpp內部宣告需加上static修飾;

d. 類的靜態成員

在類中,靜態成員可以實現多個物件之間的資料共享,並且使用靜態資料成員還不會破壞隱藏的原則,即保證了安全性。因此,靜態成員是類的所有物件中共享的成員,而不是某個物件的成員。對多個物件來說,靜態資料成員只儲存一處,供所有物件共用

e. 類的靜態函式

靜態成員函式和靜態資料成員一樣,它們都屬於類的靜態成員,它們都不是物件成員。因此,對靜態成員的引用不需要用物件名。

在靜態成員函式的實現中不能直接引用類中說明的非靜態成員,可以引用類中說明的靜態成員(這點非常重要)。如果靜態成員函式中要引用非靜態成員時,可通過物件來引用。從中可看出,呼叫靜態成員函式使用如下格式:<類名>::<靜態成員函式名>(<引數表>);

5. 智慧指標、shared_ptr底層如何實現

智慧指標主要有三種:shared_ptr,unique_ptr和weak_ptr shared_ptr

shared_ptr採用了引用計數器,多個shared_ptr中的T *ptr指向同一個記憶體區域(同一個物件),並共同維護同一個引用計數器。shared_ptr定義如下,記錄同一個例項被引用的次數,當引用次數大於0時可用,等於0時釋放記憶體。

注意避免迴圈引用,shared_ptr的一個最大的陷阱是迴圈引用,迴圈,迴圈引用會導致堆記憶體無法正確釋放,導致記憶體洩漏。迴圈引用在weak_ptr中介紹。

shared_ptr物件每次離開作用域時會自動呼叫解構函式,而解構函式並不像其他類的解構函式一樣,而是在釋放記憶體是先判斷引用計數器是否為0。等於0才做delete操作,否則只對引用計數器左減一操作。

成員函式:

use_count 返回引用計數的個數

unique 返回是否是獨佔所有權( use_count 為 1)

swap 交換兩個 shared_ptr 物件(即交換所擁有的物件)

reset 放棄內部物件的所有權或擁有物件的變更, 會引起原有物件的引用計數的減少

get 返回內部物件(指標), 由於已經過載了()方法, 因此和直接使用物件是一樣的.如 shared_ptr<int> sp(new int(1)); sp 與 sp.get()是等價的

6. 共享指標的迴圈引用、weak_ptr如何解決shared_ptr的迴圈引用

weak_ptr 設計的目的是為配合 shared_ptr 而引入的一種智慧指標來協助 shared_ptr 工作, 它只可以從一個 shared_ptr 或另一個 weak_ptr 物件構造, 它的構造和析構不會引起引用記數的增加或減少。weak_ptr是用來解決shared_ptr相互引用時的死鎖問題,如果說兩個shared_ptr相互引用,那麼這兩個指標的引用計數永遠不可能下降為0,資源永遠不會釋放。它是對物件的一種弱引用,不會增加物件的引用計數,和shared_ptr之間可以相互轉化,shared_ptr可以直接賦值給它,它可以通過呼叫lock函式來獲得shared_ptr

7. struct的記憶體佔用計算規則

首先明確:在32位編譯環境下:
1位元組:char
2位元組:short(不管是不是signed或unsigned)
4位元組:int、long、float(不管是不是signed或unsigned)
8位元組:double

這裡需要有記憶體對齊的知識,直接舉例子(https://blog.csdn.net/bailang_zhizun/article/details/83821370?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-2)。

struct MsgTest
{
	char a;
	int b;
};

 

由上圖可以看出,char型別的元素a的起始地址為0x43fd50,而int型元素b的起始地址為0x43fd54,也就是說,系統為元素a總共分配了4個位元組的記憶體(雖然a本身只佔用了一個位元組長度的記憶體),剩下的3位元組長度的記憶體就是基於記憶體對齊的概念進行填充的,不計入操作。

struct MsgTest
{
	char a;
	char b;
	int c;
};

 

這個結構體也是佔了8位元組。為什麼會這樣呢,那是因為基於記憶體對齊的概念,每一個元素應該是按照4位元組的標準進行補齊,但是元素a和元素b各佔1位元組,所以還剩下2位元組需要補齊,右上圖也可以看出,元素a和元素b的記憶體只相差了一個位元組。

8. map與unordered_map的區別,底層實現原理

unordered_map儲存機制是雜湊表,,即unordered_map內部元素是無序的。 map是紅黑樹,map中的元素是按照二叉搜尋樹儲存,進行中序遍歷會得到有序遍歷。

9. dfs和bfs的區別,具體如何實現

a. DFS(深度優先搜尋):遍歷當前節點的全部子節點。再遍歷同一級的節點 ,實現用遞迴

b. BFS(廣度優先搜尋):遍歷兄弟節點,再遍歷節點下的子節點,實現用佇列

10.鎖的原理、共享鎖、自旋鎖、讀寫鎖,自旋鎖的底層實現

互斥鎖
當有一個執行緒要訪問共享資源(臨界資源)之前會對執行緒訪問的這段程式碼(臨界區)進行加鎖。如果在加鎖之後沒釋放鎖之前其他執行緒要對臨界資源進行訪問,則這些執行緒會被阻塞睡眠,直到解鎖,如果解鎖時有一個或者多個執行緒阻塞,那麼這些鎖上的執行緒就會變成就緒狀態,然後第一個變為就緒狀態的執行緒就會獲取資源的使用權,並且再次加鎖,其他執行緒繼續阻塞等待。

讀寫鎖
也叫做共享互斥鎖,讀模式共享,寫模式互斥。有點像資料庫負載均衡的讀寫分離模式。它有三種模式:讀加鎖狀態,寫加鎖狀態和不加鎖狀態。簡單來說就是隻有一個執行緒可以佔有寫模式的讀寫鎖,但是可以有多個執行緒佔用讀模式的讀寫鎖。
當寫加鎖的模式下,任何執行緒對其進行加鎖操作都會被阻塞,直到解鎖。
當在讀加鎖的模式下,任何執行緒都可以對其進行讀加鎖的操作,但所有試圖進行寫加鎖操作的執行緒都會被阻塞。直到所有讀執行緒解鎖。但是當讀執行緒太多時,寫執行緒一直被阻塞顯然是不對的,所以一個執行緒想要對其進行寫加鎖時,就會阻塞讀加鎖,先讓寫加鎖執行緒加鎖

自旋鎖
自旋鎖和互斥鎖很像,唯一不同的是自旋鎖訪問加鎖資源時,會一直迴圈的檢視是否釋放鎖。這樣要比互斥鎖效率高很多,但是隻會佔用CPU。所以自旋鎖適用於多核的CPU。但是還有一個問題是當自旋鎖遞迴呼叫的時候會造成死鎖現象。所以慎重使用自旋鎖。

樂觀鎖
這其實是一種思想,當執行緒去拿資料的時候,認為別的執行緒不會修改資料,就不上鎖,但是在更新資料的時候會去判斷以下其他執行緒是否修改了資料。通過版本來判斷,如果資料被修改了就拒絕更新,之所以叫樂觀鎖是因為並沒有加鎖。

悲觀鎖
當執行緒去哪資料的時候,總以為別的執行緒會去修改資料,所以它每次拿資料的時候都會上鎖,別的執行緒去拿資料的時候就會阻塞。
這兩種鎖一般用於資料庫,當一個數據庫的讀操作遠遠大於寫的操作次數時,使用樂觀鎖會加大資料庫的吞吐量。

原理見:https://www.cnblogs.com/zafu/p/7399980.html

11. 缺頁中斷

缺頁中斷就是要訪問的頁不在主存,需要作業系統將其調入主存後再進行訪問。在這個時候,被記憶體對映的檔案實際上成了一個分頁交換檔案。

12. socket程式設計

13.指標與引用區別

a.指標有自己的一塊空間,而引用只是一個別名;

b.使用sizeof看一個指標的大小是4,而引用則是被引用物件的大小;

c.指標可以被初始化為NULL,而引用必須被初始化且必須是一個已有物件 的引用;

d.作為引數傳遞時,指標需要被解引用才可以對物件進行操作,而直接對引 用的修改都會改變引用所指向的物件;

e.可以有const指標,但是沒有const引用;

f.指標在使用中可以指向其它物件,但是引用只能是一個物件的引用,不能 被改變;

g.指標可以有多級指標(**p),而引用至於一級;

h.指標和引用使用++運算子的意義不一樣;

i.如果返回動態記憶體分配的物件或者記憶體,必須使用指標,引用可能引起記憶體洩露。

14.TCP與UDP的區別

a. TCP面向連線, UDP面向無連線的 b. TCP有保障的,UDP傳輸無保障的 c. TCP是效率低的,UDP效率高的 d. TCP是基於流的,UDP基於資料報文 e. TCP傳輸重要資料,UDP傳輸不重要的資料

15.三次握手與四次握手

三次握手過程

1. Client將標誌位SYN置為1,隨機產生一個值seq=J,並將該資料包傳送給Server,Client進入SYN_SENT狀態,等待Server確認。

2. Server收到資料包後由標誌位SYN=1知道Client請求建立連線,Server將標誌位SYN和ACK都置為1,ack=J+1,隨機產生一個值seq=K,並將該資料包傳送給Client以確認連線請求,Server進入SYN_RCVD狀態。

3. Client收到確認後,檢查ack是否為J+1,ACK是否為1,如果正確則將標誌位ACK置為1,ack=K+1,並將該資料包傳送給Server,Server檢查ack是否為K+1,ACK是否為1,如果正確則連線建立成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client與Server之間可以開始傳輸資料了。

簡潔版:

首先客戶端傳送seq=c的SYN資料包

伺服器端響應一個seq=s,ack=c+1的SYN+ACK資料包

最後客戶端回覆一個seq=c+1,ack=s+1的ACK資料包,三次握手完成

三次握手原因

通俗化回答:tcp三次握手只要是為了確認線路的連通,第一次客戶端傳送到服務端,那麼服務端就可以知道客戶端的傳送沒有問題,但客戶端不知道伺服器的情況,第二次伺服器傳送ACK給客戶端,客戶端收到了證明服務端的收發都沒有問題的,但服務端不知道客戶端的接收情況,所以第三次是客戶端再回給伺服器一個ACK,伺服器收到後就可以確保客戶端的收發沒有問題。

原理化回答:三次握手可以防止已經失效的連線請求報文突然又傳輸到伺服器端導致的伺服器資源浪費。例如,客戶端先發送了一個SYN,但是由於網路阻塞,該SYN資料包在某個節點長期滯留。然後客戶端又重傳SYN資料包並正確建立TCP連線,然後傳輸完資料後關閉該連線。該連線釋放後失效的SYN資料包才到達伺服器端。在二次握手的前提下,伺服器端會認為這是客戶端發起的又一次請求,然後傳送SYN ,並且在伺服器端建立socket套接字,一直等待客戶端傳送資料。但是由於客戶端並沒有發起新的請求,所以會丟棄服務端的SYN 。此時伺服器會一直等待客戶端傳送資料從而造成資源浪費。

四次握手過程

由於TCP連線時全雙工的,因此,每個方向都必須要單獨進行關閉,這一原則是當一方完成資料傳送任務後,傳送一個FIN來終止這一方向的連線,收到一個FIN只是意味著這一方向上沒有資料流動了,即不會再收到資料了,但是在這個TCP連線上仍然能夠傳送資料,直到這一方向也傳送了FIN。首先進行關閉的一方將執行主動關閉,而另一方則執行被動關閉。

1.資料傳輸結束後,客戶端的應用程序發出連線釋放報文段,並停止傳送資料,客戶端進入FIN_WAIT_1狀態,此時客戶端依然可以接收伺服器傳送來的資料。

2.伺服器接收到FIN後,傳送一個ACK給客戶端,確認序號為收到的序號+1,伺服器進入CLOSE_WAIT狀態。客戶端收到後進入FIN_WAIT_2狀態。

3.當伺服器沒有資料要傳送時,伺服器傳送一個FIN報文,此時伺服器進入LAST_ACK狀態,等待客戶端的確認

4.客戶端收到伺服器的FIN報文後,給伺服器傳送一個ACK報文,確認序列號為收到的序號+1。此時客戶端進入TIME_WAIT狀態,等待2MSL(MSL:報文段最大生存時間),然後關閉連線。

四次握手原因

由於連線的關閉控制權在應用層,所以被動關閉的一方在接收到FIN包時,TCP協議棧會直接傳送一個ACK確認包,優先關閉一端的通訊。然後通知應用層,由應用層決定什麼時候傳送FIN包。應用層可以使用系統呼叫函式read==0來判斷對端是否關閉連線。

其他回答:

1、當客戶端確認傳送完資料且知道伺服器已經接收完了,想要關閉傳送資料口(當然確認訊號還是可以發),就會發FIN給伺服器。

2、伺服器收到客戶端傳送的FIN,表示收到了,就會發送ACK回覆。

3、但這時候伺服器可能還在傳送資料,沒有想要關閉資料口的意思,所以伺服器的FIN與ACK不是同時傳送的,而是等到伺服器資料傳送完了,才會傳送FIN給客戶端。

4、客戶端收到伺服器發來的FIN,知道伺服器的資料也傳送完了,回覆ACK, 客戶端等待2MSL以後,沒有收到伺服器傳來的任何訊息,知道伺服器已經收到自己的ACK了,客戶端就關閉連結,伺服器也關閉連結了。

16.虛擬函式與解構函式

(1)靜態函式可以宣告為虛擬函式嗎?

原因主要有兩方面:

靜態函式不可以宣告為虛擬函式,同時也不能被const 和 volatile關鍵字修飾

static成員函式不屬於任何類物件或類例項,所以即使給此函式加上virutal也是沒有任何意義

虛擬函式依靠vptr和vtable來處理。vptr是一個指標,在類的建構函式中建立生成,並且只能用this指標來訪問它,靜態成員函式沒有this指標,所以無法訪問vptr。

(2)建構函式可以為虛擬函式嗎?

建構函式不可以宣告為虛擬函式。同時除了inline|explicit之外,建構函式不允許使用其它任何關鍵字。

為什麼建構函式不可以為虛擬函式?

儘管虛擬函式表vtable是在編譯階段就已經建立的,但指向虛擬函式表的指標vptr是在執行階段例項化物件時才產生的。 如果類含有虛擬函式,編譯器會在建構函式中新增程式碼來建立vptr。 問題來了,如果建構函式是虛的,那麼它需要vptr來訪問vtable,可這個時候vptr還沒產生。 因此,建構函式不可以為虛擬函式。

我們之所以使用虛擬函式,是因為需要在資訊不全的情況下進行多型執行。而建構函式是用來初始化例項的,例項的型別必須是明確的。 因此,建構函式沒有必要被宣告為虛擬函式。

(3)解構函式可以為虛擬函式嗎?

解構函式可以宣告為虛擬函式。如果我們需要刪除一個指向派生類的基類指標時,應該把解構函式宣告為虛擬函式。 事實上,只要一個類有可能會被其它類所繼承, 就應該宣告虛解構函式(哪怕該解構函式不執行任何操作)。

(4)虛擬函式可以為私有函式嗎?

  • 基類指標指向繼承類物件,則呼叫繼承類物件的函式;
  • int main()必須宣告為Base類的友元,否則編譯失敗。 編譯器報錯: ptr無法訪問私有函式。 當然,把基類宣告為public, 繼承類為private,該問題就不存在了。

請你回答一下為什麼解構函式必須是虛擬函式?為什麼C++預設的解構函式不是虛擬函式 ?

將可能會被繼承的父類的解構函式設定為虛擬函式,可以保證當我們new一個子類,然後使用基類指標指向該子類物件,釋放基類指標時可以釋放掉子類的空間,防止記憶體洩漏。

C++預設的解構函式不是虛擬函式是因為虛擬函式需要額外的虛擬函式表和虛表指標,佔用額外的記憶體。而對於不會被繼承的類來說,其解構函式如果是虛擬函式,就會浪費記憶體。因此C++預設的解構函式不是虛擬函式,而是隻有當需要當作父類時,設定為虛擬函式。

17.請問C++11有哪些新特性?

a.auto關鍵字:編譯器可以根據初始值自動推匯出型別。但是不能用於函式傳參以及陣列型別的推導

b. nullptr關鍵字:nullptr是一種特殊型別的字面值,它可以被轉換成任意其它的指標型別;而NULL一般被巨集定義為0,在遇到過載時可能會出現問題。

c. 智慧指標:C++11新增了std::shared_ptr、std::weak_ptr等型別的智慧指標,用於解決記憶體管理的問題。

d. 初始化列表:使用初始化列表來對類進行初始化

e. 右值引用:基於右值引用可以實現移動語義和完美轉發,消除兩個物件互動時不必要的物件拷貝,節省運算儲存資源,提高效率

f. atomic原子操作用於多執行緒資源互斥操作

g. 新增STL容器array以及tuple

18.虛擬函式與純虛擬函式的區別在於

1) 純虛擬函式只有定義沒有實現,虛擬函式既有定義又有實現; 2) 含有純虛擬函式的類不能定義物件,含有虛擬函式的類能定義物件;

19.unordered_map和map

unordered_map儲存機制是雜湊表,,即unordered_map內部元素是無序的。 map是紅黑樹,map中的元素是按照二叉搜尋樹儲存,進行中序遍歷會得到有序遍歷。

20.請你回答一下new/delete與malloc/free的區別是什麼

new/delete是C++的關鍵字,而malloc/free是C語言的庫函式,後者使用必須指明申請記憶體空間的大小,對於類型別的物件,後者不會呼叫建構函式和解構函式

21. struct 與 class的區別

a. 預設的繼承訪問許可權。struct是public的,class是private的。 b. struct作為資料結構的實現體,它預設的資料訪問控制是public的,而class作為物件的實現體,它預設的成員變數訪問控制是private的;

22. c和c++的struct的區別

22.指標與陣列

23. 程序與執行緒的區別

a.程序是cpu資源分配的最小單位,執行緒是cpu排程的最小單位。 b. 程序有獨立的系統資源,而同一程序內的執行緒共享程序的大部分系統資源,包括堆、程式碼段、資料段,每個執行緒只擁有一些在執行中必不可少的私有屬性,比如tcb,執行緒Id,棧、暫存器。 c. 一個程序崩潰,不會對其他程序產生影響;而一個執行緒崩潰,會讓同一程序內的其他執行緒也死掉。 d. 程序在建立、切換和銷燬時開銷比較大,而執行緒比較小。程序建立的時候需要分配系統資源,而銷燬的的時候需要釋放系統資源。程序切換需要分兩步:切換頁目錄、重新整理TLB以使用新的地址空間;切換核心棧和硬體上下文(暫存器);而同一程序的執行緒間邏輯地址空間是一樣的,不需要切換頁目錄、重新整理TLB。 e. 程序間通訊比較複雜,而同一程序的執行緒由於共享程式碼段和資料段,所以通訊比較容易。

24. 執行緒如何保證原子性

保證多執行緒原子性的方式: a. 加鎖

  使用synchronized同步程式碼塊保證執行緒的同步,從而保證多執行緒的原子性,但是加鎖的話,就會使開銷比較大。加鎖和解鎖是有消耗的。並且只要有加鎖、解鎖就會伴隨著執行緒阻塞、執行緒喚醒,這樣執行緒的切換也是消耗效能的。加鎖本質上是將併發轉變為序列來實現的,勢必會影響吞吐量。

b.CAS無鎖演算法

CAS (比較與交換,Compare and swap)是一種有名的無鎖演算法, 即不使用鎖的情況下實現多執行緒之間的變數同步。

CAS包含 3 個引數:共享變數的原始值A、預期值B和新值 C。只有當A的值等於B,才能把A的值變成C。也就是說預期值B等於原始值A,說明共享變數沒有被其他執行緒修改過,所以才允許更新新值,這樣就保證了原子性!如果A不等於B,說明共享變數已經被其他執行緒修改過了,當前執行緒可以放棄此操作。

25. HashMap的實現原理

  使用一個下標範圍比較大的陣列來儲存元素。可以設計一個函式(雜湊函式,也叫做雜湊函式),使得每個元素的關鍵字都與一個函式值(hash值)相對應,於是用這個陣列單元來儲存這個元素;也可以理解為,按照關鍵字為每一個元素“分類”,然後將這個元素儲存在相應“類”所在的地方,稱為桶。

hash_map,首先分配一大塊記憶體,形成許多桶,利用hash函式,對key進行對映到不同區域(桶)進行儲存。

其插入過程是:

a.得到key

b.通過hash函式得到hash值

c.得到桶號(一般都為hash值對桶數求模)

d.存放key與value在桶內。

其取值過程是:

a.得到key

b.通過hash函式得到hash值

c.得到桶號

d.比較桶的內部元素是否與key相等,若不想等,則沒有找到

e.取出相等記錄的value。

26. 如何解決Hash衝突

Hash衝突:就是根據key即經過一個函式f(key)得到的結果的作為地址去存放當前的key value鍵值對(這個是hashmap的存值方式),但是卻發現算出來的地址上已經有人先來了。 解決Hash衝突方法: 開放定址法

其中 m 為表的長度
對增量di有三種取法
1:線性探測再雜湊 di = 1 , 2 , 3 , … , m-1
2:平方探測再雜湊 di = 1 , -1 , 2, -2 , 3 , -3 , … , k , -k(取相應數的平方)
3: 隨機探測再雜湊 di 是一組偽隨機數列
例子:

鏈地址法

再雜湊、建立公共溢位區

建立一個公共溢位區域,就是把衝突的都放在另一個地方,不在表裡面。

27.程序間通訊,同步機制

管道/FIFO/共享記憶體/訊息佇列/訊號 a.管道中還有命名管道和非命名管道(即匿名管道)之分,非命名管道(即匿名管道)只能用於父子程序通訊,命名管道可用於非父子程序,命名管道就是FIFO,管道是先進先出的通訊方式 b.訊息佇列是用於兩個程序之間的通訊,首先在一個程序中建立一個訊息佇列,然後再往訊息佇列中寫資料,而另一個程序則從那個訊息佇列中取資料。需要注意的是,訊息佇列是用建立檔案的方式建立的,如果一個程序向某個訊息佇列中寫入了資料之後,另一個程序並沒有取出資料,即使向訊息佇列中寫資料的程序已經結束,儲存在訊息佇列中的資料並沒有消失,也就是說下次再從這個訊息佇列讀資料的時候,就是上次的資料!!!! c.訊號量,它與WINDOWS下的訊號量是一樣的,所以就不用多說了 d.共享記憶體,類似於WINDOWS下的DLL中的共享變數,但LINUX下的共享記憶體區不需要像DLL這樣的東西,只要首先建立一個共享記憶體區,其它程序按照一定的步驟就能訪問到這個共享記憶體區中的資料,當然可讀可寫 以上幾種方式的比較: a.管道:速度慢,容量有限,只有父子程序能通訊 b.FIFO:任何程序間都能通訊,但速度慢 c.訊息佇列:容量受到系統限制,且要注意第一次讀的時候,要考慮上一次沒有讀完資料的問題 d.訊號量:不能傳遞複雜訊息,只能用來同步 e.共享記憶體區:能夠很容易控制容量,速度快,但要保持同步,比如一個程序在寫的時候,另一個程序要注意讀寫的問題,相當於執行緒中的執行緒安全,當然,共享記憶體區同樣可以用作執行緒間通訊,不過沒這個必要,執行緒間本來就已經共享了同一程序內的一塊記憶體

28.請你來介紹一下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函式告訴核心描述字緩衝區指標和緩衝區的大小、檔案偏移及通知的方式,然後立即返回,當核心將資料拷貝到緩衝區後,再通知應用程式。

29.請你說一說併發(concurrency)和並行(parallelism)

併發(concurrency):指巨集觀上看起來兩個程式在同時執行,比如說在單核cpu上的多工。但是從微觀上看兩個程式的指令是交織著執行的,你的指令之間穿插著我的指令,我的指令之間穿插著你的,在單個週期內只運行了一個指令。這種併發並不能提高計算機的效能,只能提高效率。 並行(parallelism):指嚴格物理意義上的同時執行,比如多核cpu,兩個程式分別執行在兩個核上,兩者之間互不影響,單個週期內每個程式都運行了自己的指令,也就是運行了兩條指令。這樣說來並行的確提高了計算機的效率。所以現在的cpu都是往多核方面發展。

30.請問tcp握手為什麼兩次不可以?為什麼不用四次?

兩次不可以:tcp是全雙工通訊,兩次握手只能確定單向資料鏈路是可以通訊的,並不能保證反向的通訊正常

不用四次:
本來握手應該和揮手一樣都是需要確認兩個方向都能聯通的,本來模型應該是:
1.客戶端傳送syn0給伺服器
2.伺服器收到syn0,回覆ack(syn0+1)
3.伺服器傳送syn1
4.客戶端收到syn1,回覆ack(syn1+1)
因為tcp是全雙工的,上邊的四部確認了資料在兩個方向上都是可以正確到達的,但是2,3步沒有沒有上下的聯絡,可以將其合併,加快握手效率,所有就變成了3步握手。

持續更新。。。