1. 程式人生 > >C++軟體工程師面試考點.md

C++軟體工程師面試考點.md

C++軟體工程師面試考察主要有C++基礎(最好也懂Java)、資料結構及簡單演算法、TCP、作業系統、網路程式設計、Linux基本操作和Shell程式設計、資料庫,設計模式和智力題也會涉及少量。

C++基礎

參考資料:《Effective C++》、《C++ Prime》、《STL原始碼剖析》

  • C和C++的區別?
  1. C是面向過程的語言,C++是面向物件的語言
  2. C++中new和delete是對記憶體分配的運算子,取代了C中的malloc和free
  3. C++中有引用的概念,C中沒有
  4. C++引入了類的概念,C中沒有
  5. C++有函式過載,C中不能
  6. C變數只能在函式的開頭處宣告和定義,而C++隨時定義隨時使用
  • C++和Java之間的區別?
  1. Java的應用在高層,C++在中介軟體和底層
  2. Java語言簡潔;取消了指標帶來更高的程式碼質量;完全面向物件,獨特的執行機制是其具有天然的可移植性。
  3. Java在web應用上具有C++無可比擬的優勢
  4. 垃圾回收機制的區別。C++ 用解構函式回收垃圾,Java自動回收,寫C和C++程式時一定要注意記憶體的申請和釋放。
  5. Java用介面(Interface)技術取代C++程式中的多繼承性
  • 什麼是面向物件?面向物件的幾大特性是什麼? 面向物件是一種基於物件的、基於類的的軟體開發思想。面向物件具有繼承、封裝、多型的特性。
  • 模型具體怎麼實現的?
  • 指標和引用的區別
  1. 指標儲存的是指向物件的地址,引用相當於變數的別名
  2. 引用在定義的時候必須初始化,指標沒有這個要求
  3. 指標可以改變地址,引用必須從一而終
  4. 不存在空應引用,但是存在空指標NULL,相對而言引用更加安全
  5. 引用的建立不會呼叫類的拷貝建構函式
  • new/delete與malloc/free的區別
  1. new是運算子,malloc是C語言庫函式
  2. new可以過載,malloc不能過載
  3. new的變數是資料型別,malloc的是位元組大小
  4. new可以呼叫建構函式,delete可以呼叫解構函式,malloc/free不能
  5. new返回的是指定物件的指標,而malloc返回的是void*,因此malloc的返回值一般都需要進行型別轉化
  6. malloc分配的記憶體不夠的時候可以使用realloc擴容,new沒有這樣的操作
  7. new記憶體分配失敗丟擲bad_malloc,malloc記憶體分配失敗返回NULL值
  • volatile關鍵字
  1. 訪問暫存器要比訪問記憶體要塊,因此CPU會優先訪問該資料在暫存器中的儲存結果,但是記憶體中的資料可能已經發生了改變,而暫存器中還保留著原來的結果。為了避免這種情況的發生將該變數宣告為volatile,告訴CPU每次都從記憶體去讀取資料。
  2. 一個引數可以即是const又是volatile的嗎?可以,一個例子是隻讀狀態暫存器,是volatile是因為它可能被意想不到的被改變,是const告訴程式不應該試圖去修改他
  • static關鍵字的作用
  1. 修飾全域性變數
  2. 修飾區域性變數
  3. 修飾全域性函式
  4. 修飾區域性函式
  5. 修飾類的成員變數、成員函式
  • static修飾全域性函式有什麼作用? 限制他的作用域只能在本檔案之內。

  • extern關鍵字作用 宣告一個外部變數。

  • const關鍵字的作用

  1. const修飾全域性變數
  2. const修飾區域性變數
  3. const修飾指標,const int *
  4. const修飾指標指向的物件, int * const
  5. const修飾引用做形參
  6. const修飾成員變數,必須在建構函式列表中初始化
  7. const修飾成員函式,說明該函式不應該修改非靜態成員,但是這並不是十分可靠的,指標所指的非成員物件值可能會被改變
  • define/const/inline的區別 本質:define只是字串替換,const參與編譯執行,具體的:
  1. define不會做型別檢查,const擁有型別,會執行相應的型別檢查
  2. define僅僅是巨集替換,不佔用記憶體,而const會佔用記憶體
  3. const記憶體效率更高,編譯器通常將const變數儲存在符號表中,而不會分配儲存空間,這使得它成為一個編譯期間的常量,沒有儲存和讀取的操作

本質:define只是字串替換,inline由編譯器控制,具體的:

  1. 行內函數在編譯時展開,而巨集是由前處理器對巨集進行展開
  2. 行內函數會檢查引數型別,巨集定義不檢查函式引數 ,所以行內函數更安全。
  3. 巨集不是函式,而inline函式是函式
  4. 巨集在定義時要小心處理巨集引數,(一般情況是把引數用括弧括起來)。
  • C++預定義巨集
巨集 描述
_LINE_ 這會在程式編譯時包含當前行號
_FILE_ 這會在程式編譯時包含當前檔名
_DATE_ 這會包含一個形式為 month/day/year
_TIME_ 這會包含一個形式為 hour:minute:second 的字串,它表示程式被編譯的時間。
這些巨集的例項:
#include <iostream>
using namespace std;
 
int main ()
{
    cout << "Value of __LINE__ : " << __LINE__ << endl;
    cout << "Value of __FILE__ : " << __FILE__ << endl;
    cout << "Value of __DATE__ : " << __DATE__ << endl;
    cout << "Value of __TIME__ : " << __TIME__ << endl;
 
    return 0;
}

當上面的程式碼被編譯和執行時,它會產生下列結果:

Value of __LINE__ : 6
Value of __FILE__ : test.cpp
Value of __DATE__ : Feb 28 2011
Value of __TIME__ : 18:52:48
  • C++建構函式能拋異常嗎?析構呢? 不能。

  • 建構函式為什麼不能定義為虛擬函式,解構函式為什麼可以?

  1. 虛擬函式的執行依賴於虛擬函式表。而虛擬函式表需要在建構函式中進行初始化工作,即初始化vptr,讓他指向正確的虛擬函式表。而在構造物件期間,虛擬函式表還沒有被初始化,將無法進行。
  2. 在類的繼承中,如果有基類指標指向派生類,那麼用基類指標delete時,如果不定義成虛擬函式,派生類中派生的那部分無法析構。
  3. 建構函式不要呼叫虛擬函式。在基類構造的時候,虛擬函式是非虛,不會走到派生類中,既是採用的靜態繫結。顯然的是:當我們構造一個子類的物件時,先呼叫基類的建構函式,構造子類中基類部分,子類還沒有構造,還沒有初始化,如果在基類的構造中呼叫虛擬函式,如果可以的話就是呼叫一個還沒有被初始化的物件,那是很危險的,所以C++中是不可以在構造父類物件部分的時候呼叫子類的虛擬函式實現。但是不是說你不可以那麼寫程式,你這麼寫,編譯器也不會報錯。只是你如果這麼寫的話編譯器不會給你呼叫子類的實現,而是還是呼叫基類的實現。
  • 有哪些記憶體洩漏?如何判斷記憶體洩漏?如何定位記憶體洩漏?

記憶體洩漏型別:

  1. 堆記憶體洩漏 (Heap leak)。對記憶體指的是程式執行中根據需要分配通過malloc,realloc new等從堆中分配的一塊記憶體,再是完成後必須通過呼叫對應的 free或者delete 刪掉。如果程式的設計的錯誤導致這部分記憶體沒有被釋放,那麼此後這塊記憶體將不會被使用,就會產生Heap Leak.
  2. 系統資源洩露(Resource Leak).主要指程式使用系統分配的資源比如 Bitmap,handle ,SOCKET等沒有使用相應的函式釋放掉,導致系統資源的浪費,嚴重可導致系統效能降低,系統執行不穩定。

檢測記憶體洩漏:

  1. 在windows平臺下通過CRT中的庫函式進行檢測;
  2. 在可能洩漏的呼叫前後生成塊的快照,比較前後的狀態,定位洩漏的位置
  3. Linux下通過工具valgrind檢測
  • 全域性變數和區域性變數的區別
  • C++智慧指標
  • C++11新特性
  • 純虛擬函式的作用和實現方式
  • STL原始碼、vector、list、map、set
  • 位元組對齊的原則
  1. 從0位置開始儲存;
  2. 變數儲存的起始位置是該變數大小的整數倍;
  3. 結構體總的大小是其最大元素的整數倍,不足的後面要補齊;
  4. 結構體中包含結構體,從結構體中最大元素的整數倍開始存;
  5. 如果加入pragma pack(n) ,取n和變數自身大小較小的一個。
  • 空結構體的sizeof()返回值 答案是1

  • 靜態連線與動態連結的區別

  1. 靜態連結 所謂靜態連結就是在編譯連結時直接將需要的執行程式碼拷貝到呼叫處,優點就是在程式釋出的時候就不需要依賴庫,也就是不再需要帶著庫一塊釋出,程式可以獨立執行,但是體積可能會相對大一些。
  2. 動態連結 所謂動態連結就是在編譯的時候不直接拷貝可執行程式碼,而是通過記錄一系列符號和引數,在程式執行或載入時將這些資訊傳遞給作業系統,作業系統負責將需要的動態庫載入到記憶體中,然後程式在執行到指定的程式碼時,去共享執行記憶體中已經載入的動態庫可執行程式碼,最終達到執行時連線的目的。優點是多個程式可以共享同一段程式碼,而不需要在磁碟上儲存多個拷貝,缺點是由於是執行時載入,可能會影響程式的前期執行效能。
  • 多型是什麼?舉一個多型的例子

  • 多型性與虛擬函式表

  • 靜態多型和動態多型 多型分為靜態多型和動態多型。靜態多型是通過過載和模板技術實現,在編譯的時候確定。動態多型通過虛擬函式和繼承關係來實現,執行動態繫結,在執行的時候確定。

  • 重寫、過載與隱藏的區別 過載的函式都是在類內的。只有引數型別或者引數個數不同,過載不關心返回值的型別。 覆蓋(重寫)派生類中重新定義的函式,其函式名,返回值型別,引數列表都跟基類函式相同,並且基類函式前加了virtual關鍵字。 隱藏是指派生類的函式遮蔽了與其同名的基類函式,注意只要同名函式,不管引數列表是否相同,基類函式都會被隱藏。有兩種情況:(1)引數列表不同,不管有無virtual關鍵字,都是隱藏;(2)引數列表相同,但是無virtual關鍵字,也是隱藏。

  • 建構函式為什麼不能定義為虛擬函式,解構函式為什麼可以

  1. 虛擬函式的執行依賴於虛擬函式表。而虛擬函式表需要在建構函式中進行初始化工作,即初始化vptr,讓他指向正確的虛擬函式表。而在構造物件期間,虛擬函式表還沒有被初始化,將無法進行。
  2. 在類的繼承中,如果有基類指標指向派生類,那麼用基類指標delete時,如果不定義成虛擬函式,派生類中派生的那部分無法析構。
  3. 建構函式不要呼叫虛擬函式。在基類構造的時候,虛擬函式是非虛,不會走到派生類中,既是採用的靜態繫結。顯然的是:當我們構造一個子類的物件時,先呼叫基類的建構函式,構造子類中基類部分,子類還沒有構造,還沒有初始化,如果在基類的構造中呼叫虛擬函式,如果可以的話就是呼叫一個還沒有被初始化的物件,那是很危險的,所以C++中是不可以在構造父類物件部分的時候呼叫子類的虛擬函式實現。但是不是說你不可以那麼寫程式,你這麼寫,編譯器也不會報錯。只是你如果這麼寫的話編譯器不會給你呼叫子類的實現,而是還是呼叫基類的實現。
  • 必須在建構函式初始化式裡進行初始化的資料成員有哪些
  1. 常量成員,因為常量只能初始化不能賦值,所以必須放在初始化列表裡面
  2. 引用型別,引用必須在定義的時候初始化,並且不能重新賦值,所以也要寫在初始化列表裡面
  3. 沒有預設建構函式的類型別,因為使用初始化列表可以不必呼叫預設建構函式來初始化,而是直接呼叫拷貝建構函式初始化
  • 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的強制轉換表面上看起來功能強大什麼都能轉,但是轉化不夠明確,不能進行錯誤檢查,容易出錯。
  • 如何讓一個類不能例項化? 將類定義為抽象基類或者將建構函式宣告為private。
  • 如何讓main函式之前執行函式?
  1. C++中在main函式之前定義一個全域性物件,呼叫建構函式。
#include <Iostream>
using namespace std;

class TestClass{
public:
    TestClass();
};


TestClass::TestClass(){
    cout<<"TestClass"<<endl;
}

TestClass Ts;//定義個全域性變數,讓類裡面的程式碼在main之前執行

int main(){
    cout<<"main"<<endl;

    return 0;
}

輸出為

TestClass
main
  1. C語言中使用gcc的attribute關鍵字,宣告constructor和destructor。
#include <stdio.h>

void static __attribute__((constructor)) before_main(){
    printf("before main\n");
}

void static __attribute__((destructor)) after_main(){
    printf("after main\n");
}
int main(int argc, char const *argv[]){
    printf("main\n");
    return 0;
}

程式結果為

before main
main
after main
  • C++如何建立一個類,使得他只能在堆或者棧上建立?
  1. 只能在堆上生成物件:將解構函式設定為私有。 原因:C++是靜態繫結語言,編譯器管理棧上物件的生命週期,編譯器在為類物件分配棧空間時,會先檢查類的解構函式的訪問性。若解構函式不可訪問,則不能在棧上建立物件。
  2. 只能在棧上生成物件:將new 和 delete 過載為私有。 原因:在堆上生成物件,使用new關鍵詞操作,其過程分為兩階段:第一階段,使用new在堆上尋找可用記憶體,分配給物件;第二階段,呼叫建構函式生成物件。將new操作設定為私有,那麼第一階段就無法完成,就不能夠再堆上生成物件。
#include <iostream>
using namespace std;
 
// 第一個名稱空間
namespace first_space{
   void func(){
      cout << "Inside first_space" << endl;
   }
}
// 第二個名稱空間
namespace second_space{
   void func(){
      cout << "Inside second_space" << endl;
   }
}
int main ()
{
 
   // 呼叫第一個名稱空間中的函式
   first_space::func();
   
   // 呼叫第二個名稱空間中的函式
   second_space::func(); 
 
   return 0;
}
  • explict關鍵字的作用

資料結構與演算法

參考資料:《大話資料結構》、《資料結構浙大版》、《演算法設計與分析》、《演算法導論》、《劍指offer》、《LeetCode》、《組合數學》

連結串列

  • 輸出連結串列中倒數第k個節點
  • 找到連結串列環結點入口
  • 單鏈表的倒置

排序演算法

  • 手寫快速排序(快速排序的基準)
/*快速排序函式*/
//輸入:待排序的陣列,排序其實位置>=0,排序終止位置<=length(a) - 1
void QuickSortHelper(ElemType a[],int low,int high)
{
    if(low >= high)
        return;
    ElemType temp = a[low];  //儲存序列首位元素
    int i = low + 1;
    int j = high;
    while(i != j)
    {
        while(i < j && a[j] > temp)
            j--;
        while(i < j && a[i] < temp)
            i++;
        //交換兩個元素
        if(i < j)
        {
            swap(a,i,j);
        }
    }
    a[low] = a[i];
    a[i] = temp;

    QuickSortHelper(a,low,i -1);
    QuickSortHelper(a,i + 1,high);
}

/*快速排序*/
void QuickSort(ElemType a[], int n)
{
    QuickSortHelper(a,0,n-1);
}
  • 歸併排序
/*兩個有序子列的合併*/
void Merge(ElemType a[], ElemType temp[], int left_begin, int right_begin, int right_end)
{
    int left_end = right_begin - 1;
    int i = left_begin; //存放結果陣列的初始位置
    int num_element = right_end - left_begin + 1;
    //進行合併
    while (left_begin <= left_end && right_begin <= right_end)
    {
        if (a[left_begin] <= a[right_begin])
            temp[i++] = a[left_begin++];
        else
            temp[i++] = a[right_begin++];
    }
    while (left_begin <= left_end)
        temp[i++] = a[left_begin++];
    while (right_begin <= right_end)
        temp[i++] = a[right_begin++];

    //改變a中對應段的值
    for (int j = 0; j < num_element; j++)
    {
        a[right_end] = temp[right_end];
        right_end--;
    }
}

/*遞迴進行*/
void MSort(ElemType a[], ElemType temp[], int begin, int end)
{
    int mid;
    if (begin < end)
    {
        mid = (begin + end) / 2;
        MSort(a, temp, begin, mid);
        MSort(a, temp, mid + 1, end);
        Merge(a, temp, begin, mid + 1, end);
    }
}

/*歸併排序*/
void MergeSort(ElemType a[], int n)
{
    ElemType *temp = (int *)malloc(sizeof(int) * n);
    if (temp != NULL)
    {
        MSort(a, temp, 0, n - 1);
        free(temp);
    }
    else
        cout << "空間不足!" << endl;
}
  • 堆排序
/*調整為最大堆*/
void PercDown(ElemType a[], int p, int n){
    // 將N個元素的陣列中以a[p]為根的子堆調整為關於a[i]的最小堆
    int parent, child;
    int x;

    x = a[p]; //取出根節點的值

    for (parent = p; (parent * 2 + 1) < n; parent = child){
        child = parent * 2 + 1;
        if ((child != n - 1) && (a[child] < a[child + 1]))
            child++;
        if (x >= a[child])
            break;
        else
            a[parent] = a[child];
    }
    a[parent] = x;
}


/*堆排序*/
void HeapSort(ElemType a[], int n)
{
    for (int i = n / 2; i >= 0; i--)
        PercDown(a, i, n);    //建立一個最大堆
    for (int i = n - 1; i > 0; i--)
    {
        Swap(a, 0, i);  //交換最大最小元素,把最大元素給a[i]
        PercDown(a, 0, i); //剩下的i個元素調整為最大堆
    }
}

其他演算法

  • 給定N張撲克牌和一個隨機函式,設計一個洗牌演算法
void shuffle(int cards[],int n)
{
    if(cards==NULL)
        return ;
    srand(time(0));
    for(int i=0;i<n-1;++i)
    {
        //保證每次第i位的值不會涉及到第i位以前
        int index=i+rand()%(n-i);
        int temp=cards[i];
        cards[i]=cards[index];
        cards[index]=temp;
    }
}

雜湊表

  • 雜湊處理衝突的解決方法
  1. 開放地址法
  2. 連結串列法

二叉樹

  • 二叉樹的非遞迴前序遍歷,中序遍歷,後續遍歷,層序遍歷
  • 二叉樹的高度
  • 二叉樹的映象
  • 二叉樹的前k大個節點(堆排序)
  • 紅黑樹和平衡二叉樹

高階資料結構

演算法

  • 找到陣列中第一次出現1次的值
  • 揹包九講
  • 最長公共子序列(動態規劃)
  • 最長重複子序列(find和rfind的應用)
  • 找零錢問題(動態規劃&貪心演算法)
  • 排列組合問題(遞迴&回溯)

海量資料處理問題

網路技術(TCP/IP)

  • OSI七層網路模型
應用層
表示層
會話層
運輸層
網路層
資料鏈路層
物理層
  • TCP三次握手過程,為什麼需要三次?
  1. 首先Client向Server傳送請求報文段,同時同步自己的SYN(x),Client進入SYN_SENT狀態。
  2. Server接收到Client的請求報文段,返回CLient自己的seq(y)及ack(x+1),Server進入SYN_REVD狀態。
  3. CLinet收到Server的SYN+ACK包,向伺服器傳送一個序列號seq(x+1),確認號為ack(y+1),此包傳送完畢,Client和Server進入ESTABLISHED(TCP連線成功)狀態,完成三次握手。

需要三次的原因:防止已失效的報文段出現在本連線中。

  • TCP四次揮手的過程
  1. 客戶端傳送斷開TCP連線請求的報文,其中報文中包含seq序列號,是由傳送端隨機生成的,並且還將報文中的FIN欄位置為1,表示需要斷開TCP連線。(FIN=1,seq=x,x由客戶端隨機生成)
  2. 服務端會回覆客戶端傳送的TCP斷開請求報文,其包含seq序列號,是由回覆端隨機生成的,而且會產生ACK欄位,ACK欄位數值是在客戶端發過來的seq序列號基礎上加1進行回覆,以便客戶端收到資訊時,知曉自己的TCP斷開請求已經得到驗證。(FIN=1,ACK=x+1,seq=y,y由服務端隨機生成)
  3. 服務端在回覆完客戶端的TCP斷開請求後,不會馬上進行TCP連線的斷開,服務端會先確保斷開前,所有傳輸到A的資料是否已經傳輸完畢,一旦確認傳輸資料完畢,就會將回復報文的FIN欄位置1,並且產生隨機seq序列號。(FIN=1,ACK=x+1,seq=z,z由服務端隨機生成)
  4. 客戶端收到服務端的TCP斷開請求後,會回覆服務端的斷開請求,包含隨機生成的seq欄位和ACK欄位,ACK欄位會在服務端的TCP斷開請求的seq基礎上加1,從而完成服務端請求的驗證回覆。(FIN=1,ACK=z+1,seq=h,h為客戶端隨機生成) 至此TCP斷開的4次揮手過程完畢
  • 為什麼TCP建立連線需要三次握手,而斷開連線需要四次揮手? 因為當Server端收到Client端的SYN連線請求報文後,可以直接傳送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連線時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,”你發的FIN報文我收到了”。只有等到我Server端所有的報文都發送完了,我才能傳送FIN報文,因此不能一起傳送。故需要四步握手。

  • TIME_WAIT的意義,為什麼等於2MSL? MSL是最長報文段壽命,設定的目的是:

  1. 保證A傳送的最後一個ACK能夠到達B
  2. 防止已失效的報文段出現在本連線中
  • TCP頭部校驗的原理,安全嗎,可以仿造嗎 TCP校驗和是一個端到端的校驗和,由傳送端計算,然後由接收端驗證。其目的是為了發現TCP首部和資料在傳送端到接收端之間發生的任何改動。如果接收方檢測到校驗和有差錯,則TCP段會被直接丟棄。

  • TCP、UDP的區別?伺服器和客戶端建立的過程? TCP—傳輸控制協議,提供的是面向連線、可靠的位元組流服務。當客戶和伺服器彼此交換資料前,必須先在雙方之間建立一個TCP連線,之後才能傳輸資料。TCP提供超時重發,丟棄重複資料,檢驗資料,流量控制等功能,保證資料能順序地從一端傳到另一端。 UDP—使用者資料報協議,是一個簡單的面向資料報的運輸層協議。UDP不提供可靠性,它只是把應用程式傳給IP層的資料報傳送出去,但是並不能保證它們能到達目的地。由於UDP在傳輸資料報前不用在客戶和伺服器之間建立一個連線,且沒有超時重發等機制,不保證資料按順序傳遞,故而傳輸速度很快。

UDP程式設計的伺服器端一般步驟

  1. 建立一個socket,用函式socket();
  2. 設定socket屬性,用函式setsockopt();* 可選
  3. 繫結IP地址、埠等資訊到socket上,用函式bind();
  4. 迴圈接收資料,用函式recvfrom();
  5. 關閉網路連線;

UDP程式設計的客戶端一般步驟是

  1. 建立一個socket,用函式socket();
  2. 設定socket屬性,用函式setsockopt();* 可選
  3. 繫結IP地址、埠等資訊到socket上,用函式bind();* 可選
  4. 設定對方的IP地址和埠等屬性;
  5. 傳送資料,用函式sendto();
  6. 關閉網路連線;

TCP程式設計的伺服器端一般步驟是

  1. 建立一個socket,用函式socket();
  2. 設定socket屬性,用函式setsockopt(); * 可選
  3. 繫結IP地址、埠等資訊到socket上,用函式bind();
  4. 開啟監聽,用函式listen();
  5. 接收客戶端上來的連線,用函式accept();
  6. 收發資料,用函式send()和recv(),或者read()和write();
  7. 關閉網路連線;
  8. 關閉監聽;

TCP程式設計的客戶端一般步驟是

  1. 建立一個socket,用函式socket();
  2. 設定socket屬性,用函式setsockopt();* 可選
  3. 繫結IP地址、埠等資訊到socket上,用函式bind();* 可選
  4. 設定要連線的對方的IP地址和埠等屬性;
  5. 連線伺服器,用函式connect();
  6. 收發資料,用函式send()和recv(),或者read()和write();
  7. 關閉網路連線;
  • socket中的close是一次就關閉的嗎?半關閉狀態是怎麼產生的? 不是,當A傳送給B控制FIN的時候,A到B這個方向的連線就關閉了,這個時候處於半關閉的狀態,但是B到A這個方向的連線並沒有關閉,因為B要等到將資料全部發送完畢之後才會傳送FIN給A。

  • TCP擁塞控制 重點掌握慢開始、擁塞避免、快重傳、快恢復。

  • TCP流量控制,採用滑動視窗會用什麼問題? 流量控制是為了讓傳送方的傳送速率不要太快,要讓接收方來得及接收。 Nagle演算法:①當傳送方首都哦啊哦對第一個資料字元的確認後,再把傳送快取中的所有資料組裝成一個報文段傳送出去,同時繼續對隨後到到達的資料進行快取。②當到達的資料已達到傳送視窗大小的一半或已達到報文段的長度的時候就立即傳送一個報文段。 糊塗視窗綜合徵:就是由於傳送端和接收端上的處理不一致,導致網路上產生很多的小包,結果報文段包含了一個大大的頭部,攜帶資料很少。資料傳輸效率低。處理方法是等待視窗大小滿足一定的條件之後(能夠接收一個最大報文,或者緩衝區的一半),再來發送視窗通告,這樣就不會產生小報文。

    滑動視窗機制為端到端裝置間的資料傳輸提供了可靠的流量控制機制。然而,它只能在源端裝置和目的端裝置起作用,當網路中間裝置(例如路由器等)發生擁塞時,滑動視窗機制將不起作用。

  • 擁塞控制和流量控制的區別?

  1. 擁塞控制就是防止過多的資料注入到網路中,這樣可以使網路中的路由器或鏈路不會過載
  2. 流量控制往往是點對點通訊量的控制,是一個端到端的問題,流量控制要做的是抑制傳送端傳送資料的速率,以便接收端來得及接收。
  • TCP怎麼保證可靠性?
  1. 應用資料被分割成TCP認為最適合傳送的資料塊
  2. 超時重傳:當TCP發出一個段後,它啟動一個定時器,等待目的端確認收到這個報文段。如果不能及時收到一個確認,將重發這個報文段
  3. TCP給傳送的每一個包進行編號,接收方對資料包進行排序,把有序資料傳送給應用層
  4. 校驗和:TCP將保持它首部和資料的檢驗和。這是一個端到端的檢驗和,目的是檢測資料在傳輸過程中的任何變化。如果收到段的檢驗和有差錯,TCP將丟棄這個報文段和不確認收到此報文段
  5. TCP的接收端會丟棄重複的資料
  6. 流量控制:讓傳送方的傳送速率不要太快,要讓接收方來得及接收
  7. 擁塞控制:當網路擁塞時,減少資料的傳送
  • TCP滑動視窗協議
  1. “視窗”對應的是一段可以被髮送者傳送的位元組序列,其連續的範圍稱之為“視窗”
  2. “滑動”則是指這段“允許傳送的範圍”是可以隨著傳送的過程而變化的,方式就是按順序“滑動”
  • http協議與TCP協議的聯絡 TPC協議是傳輸層協議,主要解決資料如何在網路中傳輸,而HTTP是應用層協議,主要解決如何包裝資料。 Http協議是建立在TCP協議基礎之上的,當瀏覽器需要從伺服器獲取網頁資料的時候,會發出一次Http請求。Http會通過TCP建立起一個到伺服器的連線通道,當本次請求需要的資料完畢後,Http會立即將TCP連線斷開,這個過程是很短的。所以Http連線是一種短連線,是一種無狀態的連線。

  1. http1.1提供永久性連線,即1.0使用非持久連線
  2. http1.1增加host頭
  3. http1.1還提供了身份認證,狀態管理和cache快取機制等相關的請求頭和響應頭。
  • http請求方法有哪些?get和post的區別
  1. OPTIONS 返回伺服器針對特定資源所支援的HTTP請求方法,也可以利用向web伺服器傳送‘*’的請求來測試伺服器的功能性
  2. HEAD 向伺服器索與GET請求相一致的響應,只不過響應體將不會被返回。這一方法可以再不必傳輸整個響應內容的情況下,就可以獲取包含在響應小訊息頭中的元資訊。
  3. GET 向特定的資源發出請求。注意:GET方法不應當被用於產生“副作用”的操作中,例如在Web Application中,其中一個原因是GET可能會被網路蜘蛛等隨意訪問。Loadrunner中對應get請求函式:web_link和web_url
  4. POST 向指定資源提交資料進行處理請求(例如提交表單或者上傳檔案)。資料被包含在請求體中。POST請求可能會導致新的資源的建立和/或已有資源的修改。 Loadrunner中對應POST請求函式:web_submit_data,web_submit_form
  5. PUT 向指定資源位置上傳其最新內容
  6. DELETE 請求伺服器刪除Request-URL所標識的資源
  7. TRACE 回顯伺服器收到的請求,主要用於測試或診斷
  8. CONNECT HTTP/1.1協議中預留給能夠將連線改為管道方式的代理伺服器。

(1)根據HTTP規範,GET用於資訊獲取,而且應該是安全的和冪等的 (2)根據HTTP規範,POST表示可能修改變伺服器上的資源的請求

  • http和https的區別?由http升級到https需要哪些操作? HTTP 指的是超文字傳輸協議,https 指的是超文字傳輸安全協議。HTTPS 就是將 HTTP 中的傳輸內容進行了加密,然後通過可靠的連線,傳輸到對方的機器上。加密的協議是 TLS,其前身是 SSL。

  • https具體怎麼實現?,怎麼確保安全性?

  • http中瀏覽器一個URL的流程,這個過程中瀏覽器做些什麼,URL包括哪三個部分?

  1. 瀏覽器向DNS伺服器查詢輸入URL對應的IP地址。
  2. DNS伺服器返回網站的IP地址。
  3. 瀏覽器根據IP地址與目標web伺服器在80埠上建立TCP連線
  4. 瀏覽器獲取請求頁面的html程式碼。
  5. 瀏覽器在顯示視窗內渲染HTML。
  6. 視窗關閉時,瀏覽器終止與伺服器的連線。 URL包括:①協議(或稱為服務方式);②存有該資源的主機IP地址(有時也包括埠號);③主機資源的具體地址,如目錄和檔名等。
  • http四個會話過程?
  1. 建立tcp連線
  2. 發出請求文件
  3. 發出響應文件
  4. 釋放tcp連線
  • 網頁解析的過程

  • 一個機器能使用的埠號上限是多少?為什麼?可以改變嗎?如果想要用的埠超過這個限制怎麼辦? 埠號最多是65535個,埠號2個位元組,16位,所以最大表示65535.不能改變

  • 對稱加密和非對稱加密 對稱加密(也叫私鑰加密)指加密和解密使用相同金鑰的加密演算法。有時又叫傳統密碼演算法,就是加密金鑰能夠從解密金鑰中推算出來,同時解密金鑰也可以從加密金鑰中推算出來。 非對稱加密演算法需要兩個金鑰:公開金鑰(publickey)和私有金鑰(privatekey)。公開金鑰與私有金鑰是一對,如果用公開金鑰對資料進行加密,只有用對應的私有金鑰才能解密;如果用私有金鑰對資料進行加密,那麼只有用對應的公開金鑰才能解密。因為加密和解密使用的是兩個不同的金鑰,所以這種演算法叫作非對稱加密演算法。

  • 數字證書的瞭解(高頻)

  • 客戶端為什麼信任第三方證書?

  • RSA加密演算法,MD5原理

  • 單條記錄高併發訪問的優化

  1. 正常情況:如果Socket Client 傳送的資料包,在Socket Server端也是一個一個完整接收的,那個就不會出現粘包和分包情況,資料正常讀取。
  2. 粘包情況:Socket Client傳送的資料包,在客戶端傳送和伺服器接收的情況下都有可能傳送,因為客戶端傳送的資料都是傳送的一個緩衝buffer,然後由緩衝buffer最後刷到資料鏈路層的,那麼就有可能把資料包2的一部分資料結合資料包1的全部被一起傳送出去了,這樣在伺服器端就有可能出現這樣的情況,導致讀取的資料包包含了資料包2的一部分資料,這就產生粘包,當然也有可能把資料包1和資料包2全部讀取出來。
  3. 分包情況:意思就是把資料包2或者資料包1都有可能被分開一部分發送出去,接著發另外的部分,在伺服器端有可能一次讀取操作只讀到一個完整資料包的一部分。
  4. 在資料包傳送的情況下,有可能後面的資料包分開成2個或者多個,但是最前面的部分包,黏住在前面的一個完整或者部分包的後面,也就是粘包和分包同時產生了。
  • 一個IP配置多個域名,靠什麼識別? 主機頭

  • 路由器的工作原理和作用,交換機的工作原理和作用

  • 對路由協議的瞭解與介紹。內部閘道器協議IGP包括RIP,OSPF和外網閘道器協議EGP和BGP

  • 路由協議使用的演算法

  • 伺服器攻擊(DDos攻擊)

作業系統

  • 什麼是臨界區?程序進入臨界區的排程原則是? 臨界區是一段對共享資源的保護程式碼,該保護程式碼在任意時刻只允許一個執行緒對共享資源訪問。 程序進入臨界區的排程原則是: ①如果有若干程序要求進入空閒的臨界區,一次僅允許一個程序進入。②任何時候,處於臨界區內的程序不可多於一個。如已有程序進入自己的臨界區,則其它所有試圖進入臨界區的程序必須等待。③進入臨界區的程序要在有限時間內退出,以便其它程序能及時進入自己的臨界區。④如果程序不能進入自己的臨界區,則應讓出CPU,避免程序出現“忙等”現象。

  • 互斥物件、臨界區和事件的區別? 互斥是一種用途非常廣泛的核心物件。能夠保證多個執行緒對同一共享資源的互斥訪問。 事件物件也是屬於核心物件,它的主要成員包括:1.使用計數 2.指明該事件是一個自動重置事件還是一個人工重置事件的布林值3.指明該事件處於已通知狀態還是未通知狀態的布林值。

互斥物件、事件物件與臨界區的比較:

  1. 互斥物件、事件物件都是屬於核心物件,利用核心物件進行執行緒同步,速度較慢,但可以在多個程序中的多個執行緒間可以進行同步。
  2. 臨界區屬於在使用者模式下,同步速度較快,但是很容易進入死鎖狀態,因為在等待進入臨界區時無法設定超時值。
  • 程序和執行緒的區別?程序和程式的區別? 程序是資源(CPU、記憶體等)分配的基本單位,它是程式執行時的一個例項。程式執行時系統就會建立一個程序,併為它分配資源,然後把該程序放入程序就緒佇列,程序排程器選中它的時候就會為它分配CPU時間,程式開始真正執行。 執行緒是程式執行時的最小單位,它是程序的一個執行流,是CPU排程和分派的基本單位,一個程序可以由很多個執行緒組成,執行緒間共享程序的所有資源,每個執行緒有自己的堆疊和區域性變數。執行緒由CPU獨立排程執行,在多CPU環境下就允許多個執行緒同時執行。同樣多執行緒也可以實現併發操作,每個請求分配一個執行緒來處理。

程序和程式的區別

  1. 程序是動態的,而程式是靜態的。
  2. 程序有一定的生命期,而程式是指令的集合,本身無“運動”的含義。沒有建立程序的程式不能作為1個獨立單位得到作業系統的認可。
  3. 1個程式可以對應多個程序,但1個程序只能對應1個程式。程序和程式的關係猶如演出和劇本的關係。
  • 一個程序可以建立多少個執行緒?和什麼有關? 一個程序可以建立的執行緒數由可用虛擬空間和執行緒的棧的大小共同決定

  • 殭屍程序?

  • 什麼是死鎖?死鎖產生的原因?死鎖四個必要條件?死鎖的解除、死鎖控制? 死鎖是指多個程序因競爭資源而造成的一種僵局(互相等待),若無外力作用,這些程序都將無法向前推進。

死鎖產生的原因

  1. 系統資源的競爭 系統資源的競爭導致系統資源不足,以及資源分配不當,導致死鎖。

  2. 程序執行推進順序不合適 程序在執行過程中,請求和釋放資源的順序不當,會導致死鎖。

死鎖的四個條件

  1. 互斥條件:一個資源每次只能被一個程序使用,即在一段時間內某 資源僅為一個程序所佔有。此時若有其他程序請求該資源,則請求程序只能等待。

  2. 請求與保持條件:程序已經保持了至少一個資源,但又提出了新的資源請求,而該資源 已被其他程序佔有,此時請求程序被阻塞,但對自己已獲得的資源保持不放。

  3. 不可剝奪條件:程序所獲得的資源在未使用完畢之前,不能被其他程序強行奪走,即只能 由獲得該資源的程序自己來釋放(只能是主動釋放)。

  4. 迴圈等待條件: 若干程序間形成首尾相接迴圈等待資源的關係 這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發生死鎖。

死鎖的避免與預防

  1. 死鎖避免的基本思想 系統對程序發出每一個系統能夠滿足的資源申請進行動態檢查,並根據檢查結果決定是否分配資源,如果分配後系統可能發生死鎖,則不予分配,否則予以分配。這是一種保證系統不進入死鎖狀態的動態策略。 理解了死鎖的原因,尤其是產生死鎖的四個必要條件,就可以最大可能地避免、預防和解除死鎖。所以,在系統設計、程序排程等方面注意如何讓這四個必要條件不成立,如何確定資源的合理分配演算法,避免程序永久佔據系統資源。此外,也要防止程序在處於等待狀態的情況下佔用資源。因此,對資源的分配要給予合理的規劃。

  2. 死鎖避免和死鎖預防的區別 死鎖預防是設法至少破壞產生死鎖的四個必要條件之一,嚴格的防止死鎖的出現,而死鎖避免則不那麼嚴格的限制產生死鎖的必要條件的存在,因為即使死鎖的必要條件存在,也不一定發生死鎖。死鎖避免是在系統執行過程中注意避免死鎖的最終發生。

死鎖的解除

一旦檢測出死鎖,就應立即釆取相應的措施,以解除死鎖。死鎖解除的主要兩種方法:

  1. 搶佔資源。從一個或多個程序中搶佔足夠數量的資源,分配給死鎖程序,以解除死鎖狀態。

  2. 終止(或撤銷)程序。終止(或撤銷)系統中的一個或多個死鎖程序,直至打破迴圈環路,使系統從死鎖狀態解脫出來。

  • Windows記憶體管理方式
  1. 分頁儲存 使用者程式的邏輯地址空間被劃分成若干固定大小的區域,稱為“頁”或者“頁面”,相應地,記憶體物理空間也分成相對應的若干個物理塊,頁和塊的大小相等。提高了記憶體的利用率。
  2. 分段儲存 將使用者程式地址空間分成若干個大小不等的段,每段可以定義一組相對完整的邏輯資訊。提高程式的邏輯性。
  3. 段頁式儲存 兩者結合。作業的地址空間首先被分成若干個邏輯分段,每段都有自己的段號,然後再將每段分成若干個大小相等的頁。
  • 一個程式從開始執行到結束的完整過程(四個過程) 預處理,編譯,彙編,連結。
  1. 原始碼.c 檔案先經過前處理器,生成一箇中間檔案.i 檔案,這個階段有兩個作用,一是把include的標頭檔案內容進行替換,二是處理巨集定義。
  2. .i 檔案經過編譯生成彙編.s 檔案
  3. .s 的彙編檔案經過彙編器生成.obj 的目標檔案
  4. .obj 經過連結器和 lib(靜態連結庫) dll(動態連結庫)檔案生成 exe 可執行程式
  • 標頭檔案在編譯過程中的作用?(網易遊戲) 標頭檔案並不參加連結和編譯。編譯器第一步要做的就是簡單的把標頭檔案在包含它的原始檔中展開。不知你是否能理解這句話。也就是標頭檔案裡面有什麼內容,通通把它移到包含這個標頭檔案的原始檔裡。(我覺得這是個很重要的概念,可以幫助我們簡化理解編譯連結的過程,包括理解標頭檔案中定義靜態變數或靜態函式是怎麼回事)。編譯器經過這一步轉換後剩下什麼呢?就是一堆cpp檔案了。而標頭檔案已經不再是編譯器需要關心的東西了。編譯器接下來就要處理這一堆cpp檔案了。 所以第一個階段是預處理階段,在正式的編譯階段之前進行。預處理階段將根據已放置在檔案中的預處理指令來修改原始檔的內容。如#include指令就是一個預處理指令,它把標頭檔案的內容新增到.cpp檔案中。 第二個階段編譯、優化階段。

  • 為何不能在標頭檔案中定義? 防止多重定義。

  • 程序間通訊的方法?

  1. 管道pipe:管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的程序間使用。程序的親緣關係通常是指父子程序關係。
  2. 命名管道FIFO:有名管道也是半雙工的通訊方式,但是它允許無親緣關係程序間的通訊。
  3. 訊息佇列MessageQueue:訊息佇列是由訊息的連結串列,存放在核心中並由訊息佇列識別符號標識。訊息佇列克服了訊號傳遞資訊少、管道只能承載無格式位元組流以及緩衝區大小受限等缺點。
  4. 共享儲存SharedMemory:共享記憶體就是對映一段能被其他程序所訪問的記憶體,這段共享記憶體由一個程序建立,但多個程序都可以訪問。共享記憶體是最快的 IPC 方式,它是針對其他程序間通訊方式執行效率低而專門設計的。它往往與其他通訊機制,如訊號兩,配合使用,來實現程序間的同步和通訊。
  5. 訊號量Semaphore:訊號量是一個計數器,可以用來控制多個程序對共享資源的訪問。它常作為一種鎖機制,防止某程序正在訪問共享資源時,其他程序也訪問該資源。因此,主要作為程序間以及同一程序內不同執行緒之間的同步手段。
  6. 套接字Socket:套解口也是一種程序間通訊機制,與其他通訊機制不同的是,它可用於不同及其間的程序通訊。
  7. 訊號 ( sinal ) : 訊號是一種比較複雜的通訊方式,用於通知接收程序某個事件已經發生。
  • 執行緒同步方法?
  1. 鎖機制
    1. 互斥鎖:提供了以排它方式阻止資料結構被併發修改的方法。
    2. 讀寫鎖:允許多個執行緒同時讀共享資料,而對寫操作互斥。
    3. 條件變數:可以以原子的方式阻塞程序,直到某個特定條件為真為止。對條件測試是在互斥鎖的保護下進行的。條件變數始終與互斥鎖一起使用。
  2. 訊號量機制:包括無名執行緒訊號量與有名執行緒訊號量
  3. 訊號機制:類似於程序間的訊號處理。 執行緒間通訊的主要目的是用於執行緒同步,所以執行緒沒有象程序通訊中用於資料交換的通訊機制。
  • 執行緒建立的方式有幾種?

  1. 先來先去服務
  2. 短作業(程序)優先排程演算法SJ§F
  3. 輪轉法
  4. 多級反饋佇列演算法
  1. 最優頁面置換演算法
  2. 最近未使用頁面置換演算法(NRU)
  3. 先進先出頁面置換演算法(FIFO)及其改進
  4. 時鐘頁面置換演算法(clock)
  5. 最近最少使用頁面置換演算法(LRU)
  6. 工作集演算法
  • 布隆過濾器的優點與缺點

  • 布隆過濾器處理大規模問題時的持久化,包括記憶體大小首先、磁碟換入換出問題

  • 檔案讀寫使用的系統呼叫

  • 執行緒池的瞭解、優點、排程處理方式和保護任務佇列的方式 於是為了避免一個程式需要大量建立執行緒時的不必要浪費,也就是最好的去避免執行緒建立與執行緒銷燬的時間浪費,此時執行緒池就出現了。執行緒池的實現就是在初始的時候建立一些執行緒(業界通常認為建立CPU核心數的兩倍為最佳,也有說是兩倍+1),建立的執行緒為掛起狀態(就緒),當我們有任務要處理的時候,我們就啟用一個就緒的執行緒去完成任務,完成任務後,執行緒又變為就緒態進行繼續等待任務的到來。這樣過程使得每個執行緒一次建立,多次使用,如果你的程式並沒有多次任務處理,使得執行緒池中的執行緒長時間處於就緒態,此時就建議你直接使用一個執行緒就好,不必使用執行緒池。

  • 執行緒池怎麼建立?

  • 怎麼回收執行緒

  • 多執行緒同步(專案中可能會問)

  • mencache

  • 異常和中斷的區別

  • 如何保證執行緒安全?

Linux

  1. 阻塞IO
  2. 非阻塞IO
  3. IO複用
  4. 訊號驅動IO
  5. 非同步IO
  • Linux程序管理

  • Linux核心的程序排程

  • fork返回值是什麼?

  • 什麼是虛擬記憶體?

  1. EXT4:使用廣泛
  2. XFS:XFS 檔案系統是擴充套件檔案系統的一個擴充套件,XFS 檔案系統有一些缺陷,例如它不能壓縮,刪除大量檔案時效能低下。
  3. btrfs:有很多好用的功能,例如寫複製、擴充套件校驗、快照、清洗、自修複數據、冗餘刪除以及其它保證資料完整性的功能。
命令 作用
pwd 顯示當前目錄
rm 刪除
touch 生成檔案
cat 讀取指定檔案的內容並列印到終端輸出
mkdir 新建目錄make directory
file 檢視檔案型別
whereis,which,find 和 locate 查詢
chown 改變檔案所有者
df 檢視磁碟容量
wc 計數工具
tr 刪除一段文字資訊中的某些文字。或者將其進行轉換
join 連線兩個檔案
paste 它是在不對比資料的情況下,簡單地將多個檔案合併一起,以Tab隔開
  1. grep命令用於列印輸出文字中匹配的模式串,它使用正則表示式作為模式匹配的條件。
  2. sed用於過濾和轉換文字的流編輯器。
  3. AWK是一種用於處理文字的程式語言工具。
  • Linux是如何避免記憶體碎片的
  1. 夥伴演算法,用於管理實體記憶體,避免記憶體碎片;
  2. 快取記憶體Slab層用於管理核心分配記憶體,避免碎片。
  • 檔案許可權的檢視與修改?
  1. 檔案許可權檢視ls -l,檢視檔案所有者,所屬組,其他的檔案許可權,rwx為777
  2. 修改使用chmod命令
  • Linux如何開啟一個檔案?如何處理一個正在動態增長的檔案?
  • IO複用的三種方法,poll、epoll和select的特點和區別 select,poll,epoll都是IO多路複用的機制。I/O多路複用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而非同步I/O則無需自己負責進行讀寫,非同步I/O的實現會負責把資料從核心拷貝到使用者空間。
  1. select:是最初解決IO阻塞問題的方法。用結構體fd_set來告訴核心監聽多個檔案描述符,該結構體被稱為描述符集。由陣列來維持哪些描述符被置位了。對結構體的操作封裝在三個巨集定義中。通過輪尋來查詢是否有描述符要被處理,如果沒有返回。 存在的問題:
    1. 內建陣列的形式使得select的最大檔案數受限與FD_SIZE;
    2. 每次呼叫select前都要重新初始化描述符集,將fd從使用者態拷貝到核心態,每次呼叫select後,都需要將fd從核心態拷貝到使用者態;
    3. 輪尋排查當檔案描述符個數很多時,效率很低;
  2. poll:通過一個可變長度的陣列解決了select檔案描述符受限的問題。陣列中元素是結構體,該結構體儲存描述符的資訊,每增加一個檔案描述符就向陣列中加入一個結構體,結構體只需要拷貝一次到核心態。poll解決了select重複初始化的問題。輪尋排查的問題未解決。
  3. epoll:輪尋排查所有檔案描述符的效率不高,使伺服器併發能力受限。因此,epoll採用只返回狀態發生變化的檔案描述符,便解決了輪尋的瓶頸。
  • 為什麼使用IO多路複用,最主要的原因是什麼?

  • epoll有兩種觸發模式?這兩種觸發模式有什麼區別?程式設計的時候有什麼區別?

  • 上一題中程式設計的時候有什麼區別,是在邊緣觸發的時候要把套接字中的資料讀乾淨,那麼當有多個套接字時,在讀的套接字一直不停的有資料到達,如何保證其他套接字不被餓死(面試網易遊戲的時候問的一個問題,答不上來,印象賊深刻)。

  • GDB除錯

  • Linux程序和執行緒如何建立、退出?程序退出的時候,自己沒有釋放的資源(如記憶體沒有free)會怎樣?

資料庫

  • 儲存過程 我們常用的關係型資料庫是MySQL,操作資料庫的語言一般為SQL語句,SQL在執行的時候需要要先編譯,然後執行,而儲存過程(Stored Procedure)是一組為了完成某種特定功能的SQL語句集,經編譯後儲存在資料庫中,使用者通過指定儲存過程的名字並給定引數(如果該儲存過程帶有引數)來呼叫執行它。 一個儲存過程是一個可程式設計的函式,它在資料庫中建立並儲存。它可以有SQL語句和一些特殊的控制結構組成。當希望在不同的應用程式或平臺上執行相同的函式,或者封裝特定功能時,儲存過程是非常有用的。資料庫中的儲存過程可以看做是對面向物件方法的模擬,它允許控制資料的訪問方式。 儲存過程的優點:
  1. 儲存過程增強了SQL語言的功能和靈活性:儲存過程可以用流控制語句編寫,有很強的靈活性,可以完成複雜的判斷和較複雜的運算。
  2. 儲存過程允許標準組件式程式設計:儲存過程被建立後,可以在程式中被多次呼叫,而不必重新編寫該儲存過程的SQL語句。而且可以隨時對儲存過程進行修改,對應用程式原始碼毫無影響。
  3. 儲存過程能實現較快的執行速度:如果某一操作包含大量的Transaction-SQL程式碼或分別被多次執行,那麼儲存過程要比批處理的執行速度快很多。因為儲存過程是預編譯的。在首次執行一個儲存過程時,優化器對其進行分析優化,並且給出最終被儲存在系統表中的執行計劃。而批處理的Transaction-SQL語句在每次執行時都要進行編譯和優化,速度相對要慢一些。
  4. 儲存過程能減少網路流量:針對同一個資料庫物件的操作(如查詢、修改),如果這一操作所涉及的Transaction-SQL語句被組織成儲存過程,那麼當在客戶計算機上呼叫該儲存過程時,網路中傳送的只是該呼叫語句,從而大大增加了網路流量並降低了網路負載。
  5. 儲存過程可被作為一種安全機制來充分利用:系統管理員通過執行某一儲存過程的許可權進行限制,能夠實現對相應的資料的訪問許可權的限制,避免了非授權使用者對資料的訪問,保證了資料的安全。
  • 索引 索引(Index)是幫助MySQL高效獲取資料的資料結構;在資料之外,資料庫系統還維護著滿足特定查詢演算法的資料結構,這些資料結構以某種方式引用(指向)資料,可以在這些資料結構上實現高階查詢演算法,提高查詢速度,這種資料結構,就是索引。 B-Tree 索引:最常見的索引型別,大部分引擎都支援B樹索引。 HASH 索引:只有Memory引擎支援,使用場景簡單。 R-Tree 索引(空間索引):空間索引是MyISAM的一種特殊索引型別,主要用於地理空間資料型別。 Full-text (全文索引):全文索引也是MyISAM的一種特殊索引型別,主要用於全文索引,InnoDB從MySQL5.6版本提供對全文索引的支援。

  • 事物是什麼? 事務(Transaction)是併發控制的基本單位。所謂的事務,它是一個操作序列,由一條或者多條sql語句組成,這些操作要麼都執行,要麼都不執行,它是一個不可分割的工作單位。

  • acid特性?

  1. 原子性(Atomicity):指整個資料庫事務是不可分割的工作單位。只有事務中所有的資料庫操作都執行成功,整個事務的執行才算成功。事務中任何一個sql語句執行失敗,那麼已經執行成功的sql語句也必須撤銷,資料庫狀態應該退回到執行事務前的狀態。
  2. 一致性(Consistency):事務應確保資料庫的狀態從一個一致狀態轉變為另一個一致狀態。一致狀態的含義是資料庫中的資料應滿足完整性約束,也就是說在事務開始之前和事務結束以後,資料庫的完整性約束沒有被破壞
  3. 隔離性(Isolation):隔離性也叫做併發控制、可序列化或者鎖。事務的隔離性要求每個讀寫事務的物件與其它事務的操作物件能相互分離,即該事務提交前對其它事務都不可見,這通常使用鎖來實現多個事務併發執行時,一個事務的執行不應影響其他事務的執行。
  4. 永續性(Durability):表示事務一旦提交了,其結果就是永久性的,也就是資料就已經寫入到資料庫了,如果發生了宕機等事故,資料庫也能將資料恢復。
  • 資料庫中的“主屬性”、“碼”、“主碼”的區別是什麼?
  1. 在資料庫的表(關係)中能夠用於唯一區分開每個記錄(元組)的屬性或屬性的集合,我們稱之為碼(候選碼)。
  2. 當我們指定其中一個用來區分開每個記錄(元組)的碼為主碼。
  3. 主屬性是指包含在候選碼中的屬性。 換句話說:主碼和碼的關係就像班長和班長候選人之間的關係。 每個班長候選人,我們可稱之為主屬性,只不過在資料庫中,候選碼可能是多個屬性共同組成的。

設計模式

參考資料:《設計模式之禪》,簡要了解一下即可

  • 23種設計模式 單例模式程式的實現

軟體工程

  • 軟體的健壯性是指什麼? 健壯性是指程式可以適應正常和非正常的執行環境,都可以正確地執行;隨著業務量的增加,不會出現阻塞和不可用的情況。
  • 影響程式健壯性的因素
  1. 沒有容錯控制
  2. 執行耗時的操作
  3. 執行復雜的任務
  4. 資料不一致
  5. 演算法效率低
  6. 不能應對大流量衝擊
  • 什麼是正確性
  1. 不可重入的任務被重入
  2. 沒有前置狀態判定
  3. 沒有遵守“受理—處理—關閉”AHC模型
  4. 沒有遵守“申請—前置審批—審批—善後—完成”RPC模型
  5. 資料庫複製造成資料延遲

智力題

  • 100層樓,2個雞蛋,雞蛋從某一個臨界樓層丟下會摔碎,請設計方案,能用最小的次數找到臨界樓層,3個雞蛋呢?(華為一面)
  • 用3,4,5,6,7,8組成不重複的4位奇數,按照從小到大的順序排序,第62個數字是? 首先是奇數的話,末位只能是3,5,7中的一種,則有$C_{3}^{1}$種方法。前面3個數是$A_{5}^{3}$排列方法,那麼總的方法數為$C_{3}^{1}×A_{5}^{3} = 180$種方法,組成的