C++後臺開發校招面試常見問題

C和C++語言基礎

參考書籍:《C++ primer》,《effective C++》,《STL源碼解析》,《深度搜索C++對象模型》html

  • extern關鍵字做用linux

    1. extern聲明變量在在外部定義?
    2. extern修飾函數?
    3. extern C的做用?用法?
  • static關鍵字做用c++

    1. static修飾局部變量?
    2. static全局變量?(限定變量在一個編譯單元內,一個編譯單元就是指一個cpp和它包含的頭文件,這個回答能夠結合編譯須要經歷的幾個過程來答)
    3. static修飾普通函數?
    4. static修飾成員變量?
    5. static修飾成員函數?
  • volatile是幹啥的git

    1. 訪問寄存器要比訪問內存要塊,所以CPU會優先訪問該數據在寄存器中的存儲結果,可是內存中的數據可能已經發生了改變,而寄存器中還保留着原來的結果。爲了不這種狀況的發生將該變量聲明爲volatile,告訴CPU每次都從內存去讀取數據。
    2. 一個參數能夠便是const又是volatile的嗎?能夠,一個例子是隻讀狀態寄存器,是volatile是由於它可能被意想不到的被改變,是const告訴程序不該該試圖去修改他。
  • 說說const的做用,越多越好程序員

    1. const修飾全局變量;
    2. const修飾局部變量;
    3. const修飾指針,const int *;
    4. const修飾指針指向的對象, int * const;
    5. const修飾引用作形參;
    6. const修飾成員變量,必須在構造函數列表中初始化;
    7. const修飾成員函數,說明該函數不該該修改非靜態成員,可是這並非十分可靠的,指針所指的非成員對象值可能會被改變
  • new與malloc區別github

    1. new分配內存按照數據類型進行分配,malloc分配內存按照大小分配;
    2. new不只分配一段內存,並且會調用構造函數,可是malloc則不會。new的實現原理?可是還須要注意的是,以前看到過一個題說int* p = new int與int* p = new int()的區別,由於int屬於C++內置對象,不會默認初始化,必須顯示調用默認構造函數,可是對於自定義對象都會默認調用構造函數初始化。翻閱資料後,在C++11中二者沒有區別了,本身測試的結構也都是爲0;
    3. new返回的是指定對象的指針,而malloc返回的是void*,所以malloc的返回值通常都須要進行類型轉化;
    4. new是一個操做符能夠重載,malloc是一個庫函數;
    5. new分配的內存要用delete銷燬,malloc要用free來銷燬;delete銷燬的時候會調用對象的析構函數,而free則不會;
    6. malloc分配的內存不夠的時候,能夠用realloc擴容。擴容的原理?new沒用這樣操做;
    7. new若是分配失敗了會拋出bad_malloc的異常,而malloc失敗了會返回NULL。所以對於new,正確的姿式是採用try…catch語法,而malloc則應該判斷指針的返回值。爲了兼容不少c程序員的習慣,C++也能夠採用new nothrow的方法禁止拋出異常而返回NULL;
    8. new和new[]的區別,new[]一次分配全部內存,屢次調用構造函數,分別搭配使用delete和delete[],同理,delete[]屢次調用析構函數,銷燬數組中的每一個對象。而malloc則只能sizeof(int) * n;
    9. 若是不夠能夠繼續談new和malloc的實現,空閒鏈表,分配方法(首次適配原則,最佳適配原則,最差適配原則,快速適配原則)。delete和free的實現原理,free爲何直到銷燬多大的空間?
  • C++多態性與虛函數表web

    1. C++多態的實現?
      多態分爲靜態多態和動態多態。靜態多態是經過重載和模板技術實現,在編譯的時候肯定。動態多態經過虛函數和繼承關係來實現,執行動態綁定,在運行的時候肯定。
      動態多態實現有幾個條件:
      (1) 虛函數;
      (2) 一個基類的指針或引用指向派生類的對象;
      基類指針在調用成員函數(虛函數)時,就會去查找該對象的虛函數表。虛函數表的地址在每一個對象的首地址。查找該虛函數表中該函數的指針進行調用。
      每一個對象中保存的只是一個虛函數表的指針,C++內部爲每個類維持一個虛函數表,該類的對象的都指向這同一個虛函數表。
      虛函數表中爲何就能準確查找相應的函數指針呢?由於在類設計的時候,虛函數表直接從基類也繼承過來,若是覆蓋了其中的某個虛函數,那麼虛函數表的指針就會被替換,所以能夠根據指針準確找到該調用哪一個函數。
    2. 虛函數的做用?
    3. 虛函數用於實現多態,這點你們都能答上來
    4. 可是虛函數在設計上還具備封裝和抽象的做用。好比抽象工廠模式。面試

    5. 動態綁定是如何實現的?
      第一個問題中基本回答了,主要都是結合虛函數表來答就行。算法

    6. 靜態多態和動態多態。靜態多態是指經過模板技術或者函數重載技術實現的多態,其在編譯器肯定行爲。動態多態是指經過虛函數技術實如今運行期動態綁定的技術。數據庫

    7. 虛函數表

    8. 虛函數表是針對類的仍是針對對象的?同一個類的兩個對象的虛函數表是怎麼維護的?
    9. 編譯器爲每個類維護一個虛函數表,每一個對象的首地址保存着該虛函數表的指針,同一個類的不一樣對象實際上指向同一張虛函數表。
  • 純虛函數如何定義,爲何對於存在虛函數的類中析構函數要定義成虛函數
    爲了實現多態進行動態綁定,將派生類對象指針綁定到基類指針上,對象銷燬時,若是析構函數沒有定義爲析構函數,則會調用基類的析構函數,顯然只能銷燬部分數據。若是要調用對象的析構函數,就須要將該對象的析構函數定義爲虛函數,銷燬時經過虛函數表找到對應的析構函數。

  
  
  
  
  • 1
  • 2
//純虛函數定義 virtual ~myClass() = 0;
  • 析構函數能拋出異常嗎
    答案確定是不能。

    C++標準指明析構函數不能、也不該該拋出異常。C++異常處理模型最大的特色和優點就是對C++中的面向對象提供了最強大的無縫支持。那麼若是對象在運行期間出現了異常,C++異常處理模型有責任清除那些因爲出現異常所致使的已經失效了的對象(也即對象超出了它原來的做用域),並釋放對象原來所分配的資源, 這就是調用這些對象的析構函數來完成釋放資源的任務,因此從這個意義上說,析構函數已經變成了異常處理的一部分。


(1) 若是析構函數拋出異常,則異常點以後的程序不會執行,若是析構函數在異常點以後執行了某些必要的動做好比釋放某些資源,則這些動做不會執行,會形成諸如資源泄漏的問題。

(2) 一般異常發生時,c++的機制會調用已經構造對象的析構函數來釋放資源,此時若析構函數自己也拋出異常,則前一個異常還沒有處理,又有新的異常,會形成程序崩潰的問題。

  • 構造函數和析構函數中調用虛函數嗎?

  • 指針和引用的區別

    1. 指針保存的是所指對象的地址,引用是所指對象的別名,指針須要經過解引用間接訪問,而引用是直接訪問;
    2. 指針能夠改變地址,從而改變所指的對象,而引用必須從一而終;
    3. 引用在定義的時候必須初始化,而指針則不須要;
    4. 指針有指向常量的指針和指針常量,而引用沒有常量引用;
    5. 指針更靈活,用的好威力無比,用的很差到處是坑,而引用用起來則安全多了,可是比較死板。
  • 指針與數組千絲萬縷的聯繫

    1. 一個一維int數組的數組名其實是一個int* const 類型;
    2. 一個二維int數組的數組名其實是一個int (*const p)[n];
    3. 數組名作參數會退化爲指針,除了sizeof
  • 智能指針是怎麼實現的?何時改變引用計數?

    1. 構造函數中計數初始化爲1;
    2. 拷貝構造函數中計數值加1;
    3. 賦值運算符中,左邊的對象引用計數減一,右邊的對象引用計數加一;
    4. 析構函數中引用計數減一;
    5. 在賦值運算符和析構函數中,若是減一後爲0,則調用delete釋放對象。
    6. share_prt與weak_ptr的區別?
  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
//share_ptr可能出現循環引用,從而致使內存泄露 class A { public: share_ptr<B> p; }; class B { public: share_ptr<A> p; } int main() { while(true) { share_prt<A> pa(new A()); //pa的引用計數初始化爲1 share_prt<B> pb(new B()); //pb的引用計數初始化爲1 pa->p = pb; //pb的引用計數變爲2 pb->p = pa; //pa的引用計數變爲2 } //假設pa先離開,引用計數減一變爲1,不爲0所以不會調用class A的析構函數,所以其成員p也不會被析構,pb的引用計數仍然爲2; //同理pb離開的時候,引用計數也不能減到0 return 0; } /* ** weak_ptr是一種弱引用指針,其存在不會影響引用計數,從而解決循環引用的問題 */
  • C++四種類型轉換static_cast, dynamic_cast, const_cast, reinterpret_cast

    1. const_cast用於將const變量轉爲非const
    2. static_cast用的最多,對於各類隱式轉換,非const轉const,void*轉指針等, static_cast能用於多態想上轉化,若是向下轉能成功可是不安全,結果未知;
    3. dynamic_cast用於動態類型轉換。只能用於含有虛函數的類,用於類層次間的向上和向下轉化。只能轉指針或引用。向下轉化時,若是是非法的對於指針返回NULL,對於引用拋異常。要深刻了解內部轉換的原理。
    4. reinterpret_cast幾乎什麼均可以轉,好比將int轉指針,可能會出問題,儘可能少用;
    5. 爲何不使用C的強制轉換?C的強制轉換表面上看起來功能強大什麼都能轉,可是轉化不夠明確,不能進行錯誤檢查,容易出錯。
  • 內存對齊的原則

    1. 從0位置開始存儲;
    2. 變量存儲的起始位置是該變量大小的整數倍;
    3. 結構體總的大小是其最大元素的整數倍,不足的後面要補齊;
    4. 結構體中包含結構體,從結構體中最大元素的整數倍開始存;
    5. 若是加入pragma pack(n) ,取n和變量自身大小較小的一個。
  • 內聯函數有什麼優勢?內聯函數與宏定義的區別?

    1. 宏定義在預編譯的時候就會進行宏替換;
    2. 內聯函數在編譯階段,在調用內聯函數的地方進行替換,減小了函數的調用過程,可是使得編譯文件變大。所以,內聯函數適合簡單函數,對於複雜函數,即便定義了內聯編譯器可能也不會按照內聯的方式進行編譯。
    3. 內聯函數相比宏定義更安全,內聯函數能夠檢查參數,而宏定義只是簡單的文本替換。所以推薦使用內聯函數,而不是宏定義。
    4. 使用宏定義函數要特別注意給全部單元都加上括號,#define MUL(a, b) a * b,這很危險,正確寫法:#define MUL(a, b) ((a) * (b))
  • C++內存管理

    1. C++內存分爲那幾塊?(堆區,棧區,常量區,靜態和全局區)
    2. 每塊存儲哪些變量?
    3. 學會遷移,能夠說到malloc,從malloc說到操做系統的內存管理,說道內核態和用戶態,而後就什麼高端內存,slab層,夥伴算法,VMA能夠巴拉巴拉了,接着能夠遷移到fork()。
  • STL裏的內存池實現
    STL內存分配分爲一級分配器和二級分配器,一級分配器就是採用malloc分配內存,二級分配器採用內存池。

二級分配器設計的很是巧妙,分別給8k,16k,…, 128k等比較小的內存片都維持一個空閒鏈表,每一個鏈表的頭節點由一個數組來維護。須要分配內存時從合適大小的鏈表中取一塊下來。假設須要分配一塊10K的內存,那麼就找到最小的大於等於10k的塊,也就是16K,從16K的空閒鏈表裏取出一個用於分配。釋放該塊內存時,將內存節點歸還給鏈表。
若是要分配的內存大於128K則直接調用一級分配器。
爲了節省維持鏈表的開銷,採用了一個union結構體,分配器使用union裏的next指針來指向下一個節點,而用戶則使用union的空指針來表示該節點的地址。

  • STL裏set和map是基於什麼實現的。紅黑樹的特色?

    1. set和map都是基於紅黑樹實現的。
    2. 紅黑樹是一種平衡二叉查找樹,與AVL樹的區別是什麼?AVL樹是徹底平衡的,紅黑樹基本上是平衡的。
    3. 爲何選用紅黑數呢?由於紅黑數是平衡二叉樹,其插入和刪除的效率都是N(logN),與AVL相比紅黑數插入和刪除最多隻須要3次旋轉,而AVL樹爲了維持其徹底平衡性,在壞的狀況下要旋轉的次數太多。
      紅黑樹的定義:
      (1) 節點是紅色或者黑色;
      (2) 父節點是紅色的話,子節點就不能爲紅色;
      (3) 從根節點到每一個頁子節點路徑上黑色節點的數量相同;
      (4) 根是黑色的,NULL節點被認爲是黑色的。
  • STL裏的其餘數據結構和算法實現也要清楚
    這個問題,把STL源碼剖析好好看看,不只面試不慌,本身對STL的使用也會上升一個層次。

  • 必須在構造函數初始化式裏進行初始化的數據成員有哪些
    (1) 常量成員,由於常量只能初始化不能賦值,因此必須放在初始化列表裏面
    (2) 引用類型,引用必須在定義的時候初始化,而且不能從新賦值,因此也要寫在初始化列表裏面
    (3) 沒有默認構造函數的類類型,由於使用初始化列表能夠沒必要調用默認構造函數來初始化,而是直接調用拷貝構造函數初始化

  • 模板特化
    (1) 模板特化分爲全特化和偏特化,模板特化的目的就是對於某一種變量類型具備不一樣的實現,所以須要特化版本。例如,在STL裏迭代器爲了適應原生指針就將原生指針進行特化。

  • 定位內存泄露
    (1)在windows平臺下經過CRT中的庫函數進行檢測;
    (2)在可能泄漏的調用先後生成塊的快照,比較先後的狀態,定位泄漏的位置
    (3)Linux下經過工具valgrind檢測

  • 手寫strcpy

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
char* strcpy(char* dst, const char* src) { assert(dst); assert(src); char* ret = dst; while((*dst++ = *src++) != '\0'); return ret; } //該函數是沒有考慮重疊的 char* strcpy(char* dst, const char* src) { assert((dst != NULL) && (src != NULL)); char* ret = dst; int size = strlen(src) + 1; if(dst > src || dst < src + len) { dst = dst + size - 1; src = src + size - 1; while(size--) { *dst-- = *src--; } } else { while(size--) { *dst++ = *src++; } } return ret; }
  • 手寫memcpy函數
  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
void* memcpy(void* dst, const void* src, size_t size) { if(dst == NULL || src == NULL) { return NULL; } void* res = dst; char* pdst = (char*)dst; char* psrc = (char*)src; if(pdst > psrc && pdst < psrc + size) //重疊 { pdst = pdst + size - 1; psrc = pdst + size - 1; while(size--) { *pdst-- = *psrc--; } } else //無重疊 { while(size--) { *dst++ = *src++; } } return ret; }
  • 手寫strcat函數
  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
char* strcat(char* dst, const char* src) { char* ret = dst; while(*dst != '\0') ++dst; while((*dst++ = *src) != '\0'); return ret; }
  • 手寫strcmp函數
  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
int strcmp(const char* str1, const char* str2) { while(*str1 == *str2 && *str1 != '\0') { ++str1; ++str2; } return *str1 - *str2; }

數據結構與算法

這一塊考察範圍太廣,主要靠多刷題吧,牛客網,劍指OFFER,LeetCode等。

Hash表

  • Hash表實現(拉鍊和分散地址)
  • Hash策略常見的有哪些?
  • STL中hash_map擴容發生什麼?
    (1) 建立一個新桶,該桶是原來桶兩倍大最接近的質數(判斷n是否是質數的方法:用n除2到 s q r t ( n ) 範圍內的數) ;
    (2) 將原來桶裏的數經過指針的轉換,插入到新桶中(注意STL這裏作的很精細,沒有直接將數據從舊桶遍歷拷貝數據插入到新桶,而是經過指針轉換)
    (3) 經過swap函數將新桶和舊桶交換,銷燬新桶。

  • 二叉樹結構,二叉查找樹實現;
  • 二叉樹的六種遍歷;
  • 二叉樹的按層遍歷;
  • 遞歸是解決二叉樹相關問題的神級方法;
  • 樹的各類常見算法題(http://blog.csdn.net/xiajun07061225/article/details/12760493);

  • 什麼是紅黑樹?

    • 節點爲紅色或者黑色;
    • 根節點爲黑色;
    • 從根節點到每一個葉子節點通過的黑色節點個數的和相同;
    • 若是父節點爲紅色,那麼其子節點就不能爲紅色。
  • 紅黑樹與AVL樹的區別

    • 紅黑樹與AVL樹都是平衡樹,可是AVL是徹底平衡的(平衡就是值樹中任意節點的左子樹和右子樹高度差不超過1);
    • 紅黑樹效率更高,由於AVL爲了保證其徹底平衡,插入和刪除的時候在最壞的狀況下要旋轉logN次,而紅黑樹插入和刪除的旋轉次數要比AVL少。
  • Trie樹(字典樹)

    • 每一個節點保存一個字符
    • 根節點不保存字符
    • 每一個節點最多有n個子節點(n是全部可能出現字符的個數)
    • 查詢的複雜父爲O(k),k爲查詢字符串長度

鏈表

  • 鏈表和插入和刪除,單向和雙向鏈表都要會
  • 鏈表的問題考慮多個指針和遞歸
    (1) 反向打印鏈表(遞歸)
    (2) 打印倒數第K個節點(先後指針)
    (3) 鏈表是否有環(快慢指針)等等。b ggg

棧和隊列

  • 隊列和棧的區別?(從實現,應用,自身特色多個方面來闡述,不要只說一個先入先出,先入後出,這個你會別人也會,要展示出你比別人掌握的更深)
  • 典型的應用場景

海量數據問題

  • 十億整數(隨機生成,可重複)中前K最大的數
    相似問題的解決方法思路:首先哈希將數據分紅N個文件,而後對每一個文件創建K個元素最小/大堆(根據要求來選擇)。最後將文件中剩餘的數插入堆中,並維持K個元素的堆。最後將N個堆中的元素合起來分析。能夠採用歸併的方式來合併。在歸併的時候爲了提升效率還須要建一個N個元素構成的最大堆,先用N個堆中的最大值填充這個堆,而後就是彈出最大值,指針後移的操做了。固然這種問題在如今的互聯網技術中,通常就用map-reduce框架來作了。
    大數據排序相同的思路:先哈希(哈希是好處是分佈均勻,相同的數在同一個文件中),而後小文件裝入內存快排,排序結果輸出到文件。最後建堆歸併。

  • 十億整數(隨機生成,可重複)中出現頻率最高的一千個

排序算法

  • 排序算法固然是基礎內容了,必須至少能快速寫出,快排,建堆,和歸併
  • 每種算法的時間空間複雜度,最好最差平均狀況

位運算

布隆過濾器

幾十億個數常常要查找某一個數在不在裏面,使用布隆過濾器,布隆過濾器的原理。布隆過濾器可能出現誤判,怎麼保證無偏差?

網絡與TCP/IP

參考書籍:《圖解TCP/IP》,《TCP/IP詳解 卷一》,《圖解HTTP》,《HTTP權威指南》

  
  
  
  
  • 1
  • 2
(1) Ping是經過發送ICMP報文回顯請求實現。 (2) TraceRoute經過發送UDP報文,設置目的端口爲一個不可能的值,將IP首部中的TTL分別設置從1N,每次逐個增長,若是收到端口不可達,說明到達目的主機,若是是由於TTL跳數超過,路由器會發送主機不可達的ICMP報文。

HTTP

http/https 1.0、1.一、2.0

  1. http的主要特色:
    簡單快速:當客戶端向服務器端發送請求時,只是簡單的填寫請求路徑和請求方法便可,而後就能夠經過瀏覽器或其餘方式將該請求發送就好了
    靈活: HTTP 協議容許客戶端和服務器端傳輸任意類型任意格式的數據對象
    無鏈接:無鏈接的含義是限制每次鏈接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開鏈接,採用這種方式能夠節省傳輸時間。(當今多數服務器支持Keep-Alive功能,使用服務器支持長鏈接,解決無鏈接的問題)
    無狀態:無狀態是指協議對於事務處理沒有記憶能力,服務器不知道客戶端是什麼狀態。即客戶端發送HTTP請求後,服務器根據請求,會給咱們發送數據,發送完後,不會記錄信息。(使用 cookie 機制能夠保持 session,解決無狀態的問題)

  2. http1.1的特色
    a、默認持久鏈接節省通訊量,只要客戶端服務端任意一端沒有明確提出斷開TCP鏈接,就一直保持鏈接,能夠發送屢次HTTP請求
    b、管線化,客戶端能夠同時發出多個HTTP請求,而不用一個個等待響應
    c、斷點續傳

  3. http2.0的特色
    a、HTTP/2採用二進制格式而非文本格式
    b、HTTP/2是徹底多路複用的,而非有序並阻塞的——只需一個HTTP鏈接就能夠實現多個請求響應
    c、使用報頭壓縮,HTTP/2下降了開銷
    d、HTTP/2讓服務器能夠將響應主動「推送」到客戶端緩存中

get/post 區別

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
區別一: get重點在從服務器上獲取資源,post重點在向服務器發送數據; 區別二: get傳輸數據是經過URL請求,以field(字段)= value的形式,置於URL後,並用"?"鏈接,多個請求數據間用"&"鏈接,如http://127.0.0.1/Test/login.action?name=admin&password=admin,這個過程用戶是可見的; post傳輸數據經過Http的post機制,將字段與對應值封存在請求實體中發送給服務器,這個過程對用戶是不可見的; 區別三: Get傳輸的數據量小,由於受URL長度限制,但效率較高; Post能夠傳輸大量數據,因此上傳文件時只能用Post方式; 區別四: get是不安全的,由於URL是可見的,可能會泄露私密信息,如密碼等; postget安全性較高;

返回狀態碼

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
200:請求被正常處理 204:請求被受理但沒有資源能夠返回 206:客戶端只是請求資源的一部分,服務器只對請求的部分資源執行GET方法,相應報文中經過Content-Range指定範圍的資源。 301:永久性重定向 302:臨時重定向 303:與302狀態碼有類似功能,只是它但願客戶端在請求一個URI的時候,能經過GET方法重定向到另外一個URI上 304:發送附帶條件的請求時,條件不知足時返回,與重定向無關 307:臨時重定向,與302相似,只是強制要求使用POST方法 400:請求報文語法有誤,服務器沒法識別 401:請求須要認證 403:請求的對應資源禁止被訪問 404:服務器沒法找到對應資源 500:服務器內部錯誤 503:服務器正忙

http 協議頭相關

http數據由請求行,首部字段,空行,報文主體四個部分組成
首部字段分爲:通用首部字段,請求首部字段,響應首部字段,實體首部字段

https與http的區別?如何實現加密傳輸?

  • https就是在http與傳輸層之間加上了一個SSL
  • 對稱加密與非對稱加密

瀏覽器中輸入一個URL發生什麼,用到哪些協議?

瀏覽器中輸入URL,首先瀏覽器要將URL解析爲IP地址,解析域名就要用到DNS協議,首先主機會查詢DNS的緩存,若是沒有就給本地DNS發送查詢請求。DNS查詢分爲兩種方式,一種是遞歸查詢,一種是迭代查詢。若是是迭代查詢,本地的DNS服務器,向根域名服務器發送查詢請求,根域名服務器告知該域名的一級域名服務器,而後本地服務器給該一級域名服務器發送查詢請求,而後依次類推直到查詢到該域名的IP地址。DNS服務器是基於UDP的,所以會用到UDP協議。

獲得IP地址後,瀏覽器就要與服務器創建一個http鏈接。所以要用到http協議,http協議報文格式上面已經提到。http生成一個get請求報文,將該報文傳給TCP層處理。若是採用https還會先對http數據進行加密。TCP層若是有須要先將HTTP數據包分片,分片依據路徑MTU和MSS。TCP的數據包而後會發送給IP層,用到IP協議。IP層經過路由選路,一跳一跳發送到目的地址。固然在一個網段內的尋址是經過以太網協議實現(也能夠是其餘物理層協議,好比PPP,SLIP),以太網協議須要直到目的IP地址的物理地址,有須要ARP協議。

安全相關

至少了解攻擊的原理和基本的防護方法,常見的攻擊方法有一下幾種

  • SQL注入
  • XSS
  • CSRF
  • SYN洪水攻擊
  • APR欺騙

數據庫

主要參考書籍:《數據庫系統概念》,《高性能MySQL》

  • SQL語言(內外鏈接,子查詢,分組,彙集,嵌套,邏輯)
  • MySQL索引方法?索引的優化?
  • InnoDB與MyISAM區別?
  • 事務的ACID
  • 事務的四個隔離級別
  • 查詢優化(從索引上優化,從SQL語言上優化)
  • B-與B+樹區別?
  • MySQL的聯合索引(又稱多列索引)是什麼?生效的條件?
  • 分庫分表

Linux

主要參考書籍:《現代操做系統》,《APUE》,《UNP》,《LINUX內核設計與實現》,《深刻理解LINUX內核》

進程與線程

(1) 進程與線程區別?
(2) 線程比進程具備哪些優點?
(3) 何時用多進程?何時用多線程?
(4) LINUX中進程和線程使用的幾個函數?
(5) 線程同步?
在Windows下線程同步的方式有:互斥量,信號量,事件,關鍵代碼段
在Linux下線程同步的方式有:互斥鎖,自旋鎖,讀寫鎖,屏障(併發完成同一項任務時,屏障的做用特別好使)
知道這些鎖之間的區別,使用場景?

進程間通信方式

管道( pipe ):管道是一種半雙工的通訊方式,數據只能單向流動,並且只能在具備親緣關係的進程間使用。進程的親緣關係一般是指父子進程關係。

命名管道 (FIFO) : 有名管道也是半雙工的通訊方式,可是它容許無親緣關係進程間的通訊。

信號量:信號量用於實現進程間的互斥與同步,而不是用於存儲進程間通訊數據,有XSI信號量和POSIX信號量,POSIX信號量更加完善。

消息隊列( message queue ) : 消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩衝區大小受限等缺點。

共享內存( shared memory ) :共享內存就是映射一段能被其餘進程所訪問的內存,這段共享內存由一個進程建立,但多個進程均可以訪問。共享內存是最快的 IPC 方式,它是針對其餘進程間通訊方式運行效率低而專門設計的。它每每與其餘通訊機制,如信號兩,配合使用,來實現進程間的同步和通訊。(原理必定要清楚,常考)

信號 ( sinal ) : 信號是一種比較複雜的通訊方式,用於通知接收進程某個事件已經發生,常見的信號。

套接字( socket ) : 套解口也是一種進程間通訊機制,與其餘通訊機制不一樣的是,它可用於不一樣及其間的進程通訊。

  • 匿名管道與命名管道的區別:匿名管道只能在具備公共祖先的兩個進程間使用。
  • 共享文件映射mmap
    mmap創建進程空間到文件的映射,在創建的時候並不直接將文件拷貝到物理內存,一樣採用缺頁終端。mmap映射一個具體的文件能夠實現任意進程間共享內存,映射一個匿名文件,能夠實現父子進程間共享內存。

  • 常見的信號有哪些?:SIGINT,SIGKILL(不能被捕獲),SIGTERM(能夠被捕獲),SIGSEGV,SIGCHLD,SIGALRM

內存管理

  1. 虛擬內存的做用?
  2. 虛擬內存的實現?
  3. 操做系統層面對內存的管理?
  4. 內存池的做用?STL裏內存池如何實現
  5. 進程空間和內核空間對內存的管理不一樣?
  6. Linux的slab層,VAM?
  7. 夥伴算法
  8. 高端內存

進程調度

  1. Linux進程分爲兩種,實時進程和非實時進程;
  2. 優先級分爲靜態優先級和動態優先級,優先級的範圍;
  3. 調度策略,FIFO,LRU,時間片輪轉
  4. 交互進程經過平均睡眠時間而被獎勵;

死鎖

(1) 死鎖產生的條件;
(2) 死鎖的避免;

命令行

  • Linux命令 在一個文件中,倒序打印第二行前100個大寫字母
  
  
  
  
  • 1
cat filename | head -n 2 | tail -n 1 | grep '[[:upper:]]' -o | tr -d '\n'| cut -c 1-100 | rev
  • 與CPU,內存,磁盤相關的命令(top,free, df, fdisk)

  • 網絡相關的命令netstat,tcpdump等

  • sed, awk, grep三個超強大的命名,分別用與格式化修改,統計,和正則查找

  • ipcs和ipcrm命令

  • 查找當前目錄以及字母下以.c結尾的文件,且文件中包含」hello world」的文件的路徑

  • 建立定時任務

IO模型

  • 五種IO模型:阻塞IO,非阻塞IO,IO複用,信號驅動式IO,異步IO

  • select,poll,epoll的區別

select:是最初解決IO阻塞問題的方法。用結構體fd_set來告訴內核監聽多個文件描述符,該結構體被稱爲描述符集。由數組來維持哪些描述符被置位了。對結構體的操做封裝在三個宏定義中。經過輪尋來查找是否有描述符要被處理,若是沒有返回**
存在的問題:
1. 內置數組的形式使得select的最大文件數受限與FD_SIZE;
2. 每次調用select前都要從新初始化描述符集,將fd從用戶態拷貝到內核態,每次調用select後,都須要將fd從內核態拷貝到用戶態;
3. 輪尋排查當文件描述符個數不少時,效率很低;

poll:經過一個可變長度的數組解決了select文件描述符受限的問題。數組中元素是結構體,該結構體保存描述符的信息,每增長一個文件描述符就向數組中加入一個結構體,結構體只須要拷貝一次到內核態。poll解決了select重複初始化的問題。輪尋排查的問題未解決。**

epoll:輪尋排查全部文件描述符的效率不高,使服務器併發能力受限。所以,epoll採用只返回狀態發生變化的文件描述符,便解決了輪尋的瓶頸。
- 爲何使用IO多路複用,最主要的緣由是什麼?
- epoll有兩種觸發模式?這兩種觸發模式有什麼區別?編程的時候有什麼區別?
- 上一題中編程的時候有什麼區別,是在邊緣觸發的時候要把套接字中的數據讀乾淨,那麼當有多個套接字時,在讀的套接字一直不停的有數據到達,如何保證其餘套接字不被餓死(面試網易遊戲的時候問的一個問題,答不上來,印象賊深入)。

  1. select/poll/epoll區別
  2. 幾種網絡服務器模型的介紹與比較
  3. epoll爲何這麼快(搞懂這篇文章,關於IO複用的問題就信手拈來了)

線程池,內存池 本身動手實現一遍

Linux的API

  • fork與vfork區別
    fork和vfork都用於建立子進程。可是vfork建立子進程後,父進程阻塞,直到子進程調用exit()或者excle()。
    對於內核中過程fork經過調用clone函數,而後clone函數調用do_fork()。do_fork()中調用copy_process()函數先複製task_struct結構體,而後複製其餘關於內存,文件,寄存器等信息。fork採用寫時拷貝技術,所以子進程和父進程的頁表指向相同的頁框。可是vfork不須要拷貝頁表,由於父進程會一直阻塞,直接使用父進程頁表。

  • exit()與_exit()區別
    exit()清理後進入內核,_exit()直接陷入內核。

  • 孤兒進程與僵死進程

    1. 孤兒進程是怎麼產生的?
    2. 僵死進程是怎麼產生的?
    3. 僵死進程的危害?
    4. 如何避免僵死進程的產生?
  • Linux是如何避免內存碎片的

    1. 夥伴算法,用於管理物理內存,避免內存碎片;
    2. 高速緩存Slab層用於管理內核分配內存,避免碎片。
  • 共享內存的實現原理?
    共享內存實現分爲兩種方式一種是採用mmap,另外一種是採用XSI機制中的共享內存方法。mmap是內存文件映射,將一個文件映射到進程的地址空間,用戶進程的地址空間的管理是經過vm_area_struct結構體進行管理的。mmap經過映射一個相同的文件到兩個不一樣的進程,就能實現這兩個進程的通訊,採用該方法能夠實現任意進程之間的通訊。mmap也能夠採用匿名映射,不指定映射的文件,可是隻能在父子進程間通訊。XSI的內存共享實際上也是經過映射文件實現,只是其映射的是一種特殊文件系統下的文件,該文件是不能經過read和write訪問的。

兩者區別:

一、 系統V共享內存中的數據,歷來不寫入到實際磁盤文件中去;而經過mmap()映射普通文件實現的共享內存通訊能夠指定什麼時候將數據寫入磁盤文件中。注:前面講到,系統V共享內存機制實際是經過映射特殊文件系統shm中的文件實現的,文件系統shm的安裝點在交換分區上,系統從新引導後,全部的內容都丟失。

二、 系統V共享內存是隨內核持續的,即便全部訪問共享內存的進程都已經正常終止,共享內存區仍然存在(除非顯式刪除共享內存),在內核從新引導以前,對該共享內存區域的任何改寫操做都將一直保留。

三、 經過調用mmap()映射普通文件進行進程間通訊時,必定要注意考慮進程什麼時候終止對通訊的影響。而經過系統V共享內存實現通訊的進程則否則。注:這裏沒有給出shmctl的使用範例,原理與消息隊列大同小異。

  • 系統調用與庫函數(open, close, create, lseek, write, read)

  • 同步方法有哪些?

    1. 互斥鎖,自旋鎖,信號量,讀寫鎖,屏障
    2. 互斥鎖與自旋鎖的區別:互斥鎖得不到資源的時候阻塞,不佔用cpu資源。自旋鎖得不到資源的時候,不停的查詢,而然佔用cpu資源。
    3. 死鎖

其餘

  • ++i是不是原子操做
    明顯不是,++i主要有三個步驟,把數據從內存放在寄存器上,在寄存器上進行自增,把數據從寄存器拷貝會內存,每一個步驟均可能被中斷。

  • 判斷大小端

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
union un { int i; char ch; }; void fun() { union un test; test.i = 1; if(ch == 1) cout << "小端" << endl; else cout << "大端" << endl; }

設計模式

分佈式系統

部分問題只是列出思考的概要,去書中和實踐中找到這些問題的答案才能真正的消化。

PS:歡迎轉載,轉載請標明出處!

相關文章
相關標籤/搜索