• 正文
    • 理想汽車(C++一面)
    • C++11 新特性了解哪些內(nèi)容?
    • 智能指針介紹一下?
    • lambda表達式的原理是什么?
    • stl迭代器的失效情況你知道哪些?
    • unordered_map的底層結(jié)構(gòu)是什么?
    • linux程序崩潰怎么定位問題?
    • 你說到缺頁中斷,能介紹一下缺頁中斷么?
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

理想汽車今年年終獎不理想了?理想汽車面試分享

03/17 13:14
1954
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

圖解學習網(wǎng)站:https://xiaolincoding.com

大家好,我是小林。

之前我在公眾號說過 25 屆理想汽車的校招薪資,總包有 40w+,屬實是一線大廠梯隊的校招薪資了,跟騰訊、字節(jié)、阿里巴巴互聯(lián)網(wǎng)大廠是一個梯隊的薪資了,這么看理想汽車的薪資還是蠻“理想的?!?/p>

25 屆理想校招年薪是 16 薪,這跟以前不一樣,以前都是 14 薪為主,所以 25 屆的月薪 base 會比以前之前的少一些,但是整體的總包是差不多的。

比如有 24 屆訓(xùn)練營同學拿到了理想 offer,開的是 31k x 14(總包43.4w),base 就是 31k。

當時談到理想汽車今年校招給了 16 薪,不少讀者留言說,16 薪別想了,能拿滿 14 薪都偷笑了。

每個公司的年終獎都是沒辦法說一定能拿滿的,除了跟自己本身績效有關(guān)系之外,還跟公司當年的市場環(huán)境有很大的關(guān)系,甚至也跟所在部門給公司創(chuàng)作的價值也有關(guān)系。

簡單來說,公司超預(yù)期賺錢了,那么之前談的年終數(shù)很大概率能兌現(xiàn)成功,當然公司賺錢不及預(yù)期,年終數(shù)自然有可能會減少的,甚至可能直接沒有了。

理想汽車在 23 年銷量暴漲,23 年全年共交付了 37w+ 輛車, 同比增長182.2%,這一年理想成了交付量最高的中國新勢力車企。

既然 23 年市場反饋那么猛,年終獎自然只會多不會少呀,去年就有理想汽車的員工爆料拿到了「超高的年終獎」,能打達到?7-8 個月的水平,這比預(yù)期的 2 個月(14 薪)年終獎,翻了 3-4 倍啊,還是蠻給力的。

當時理想汽車 CEO 還為這次超額年終獎的事情,說了自己的想法,企業(yè)要做到賞罰分明,而且企業(yè)不能只學習華為的流程,而不學華為的利益分配。

只可惜新能源車市場還是太殘酷了,后來雷總的小米汽車也殺入了新能源汽車的賽道,理想的 MEGA 上市又被瘋狂吐槽,MEGA 新車市場表現(xiàn)也遠遠不及預(yù)期。

原本 24 年的銷量目標是 80 萬,結(jié)果最后調(diào)整為了 50 萬。最后 24 年理想汽車銷量是達到了 50 萬的目標,同比增長了 33.1%,但是與之前年初定的 80 萬目標少了 30萬。

因此,今年理想汽車的年終獎肯定是沒有去年超額年終獎的了。

根據(jù)脈脈網(wǎng)的爆料,今年理想汽車年終獎基本是 14 薪左右,也就是 2 個月年終獎,還真給之前留言的讀者說對, 拿不到 16 薪資。

關(guān)鍵是有些員工加入理想汽車的時候,是以 16 薪進來的,現(xiàn)在拿到是 14 薪,有一定的落差感,感覺之前談薪談了個寂寞。

如果之前是以 16 薪談的,那肯定是虧了的,因為相比 14 薪的,base 會低一些的,那最后既然是 14 薪,那還不如按 14 薪談總包。

所以啊,咱們談薪的時候,最好爭取 base 高點的方案,年終數(shù)得等到年底才能知道,而且能不能拿滿還另說,不可預(yù)知的事情太多,不如每個月拿到手的錢多一些更好。

好了,接下來聊聊理想汽車的面經(jīng),之前已經(jīng)分析過理想汽車的 Java 面經(jīng),這次我們看點不一樣的。

來看看理想汽車 C++ 崗的面經(jīng),是一面的面經(jīng),拷打了近 50 分鐘,從 C++11 新特性、智能指針、迭代器、STL原理、Linux 內(nèi)存、HTTPS、排序算法等底層原理進行的盤問。

你們覺得難度如何?

理想汽車(C++一面)

C++11 新特性了解哪些內(nèi)容?

類型推導(dǎo)與簡化語法

特性名稱 描述 示例
auto?關(guān)鍵字 自動推導(dǎo)變量類型,簡化復(fù)雜類型聲明(如迭代器)。 auto x = 42;?→?int x
decltype 推導(dǎo)表達式類型,保留?const?和引用屬性,適用于模板編程。 int i=1; decltype(i) j = i;?→?int j

右值引用與移動語義

右值引用(&& 區(qū)分左值/右值,支持資源高效轉(zhuǎn)移(如臨時對象)。 std::vector<int> v2 = std::move(v1);
移動構(gòu)造函數(shù)/賦值運算符 減少深拷貝開銷,通過資源轉(zhuǎn)移提升性能。 類中定義?ClassName(ClassName&& other) noexcept;
完美轉(zhuǎn)發(fā)(std::forward 保持參數(shù)原始類型,避免多次拷貝。 模板中使用?std::forward<T>(arg)

智能指針

std::unique_ptr 獨占所有權(quán),不可復(fù)制但可移動,替代?auto_ptr。 std::unique_ptr<int> ptr = std::make_unique<int>(10);
std::shared_ptr 共享所有權(quán),引用計數(shù)管理資源,線程安全。 std::shared_ptr<int> ptr = std::make_shared<int>(10);
std::weak_ptr 解決?shared_ptr?循環(huán)引用問題。 std::weak_ptr<int> w_ptr = s_ptr;

函數(shù)與模板增強

Lambda 表達式 匿名函數(shù),支持捕獲外部變量,簡化回調(diào)和算法。 std::sort(vec.begin(), vec.end(), [](int a, int b) { return a < b; });
變長參數(shù)模板 支持任意數(shù)量/類型的模板參數(shù),用于元編程和容器設(shè)計。 template<typename... Args> void func(Args... args);
constexpr?常量表達式 編譯時求值,優(yōu)化性能,允許函數(shù)在編譯期執(zhí)行。 constexpr int factorial(int n) { return n <=1 ? 1 : n*factorial(n-1); }

并發(fā)編程支持

std::thread 原生多線程支持,結(jié)合互斥鎖和原子操作實現(xiàn)同步。 std::thread t(func); t.join();
std::async?和?std::future 簡化異步任務(wù)管理,獲取異步操作結(jié)果。 auto future = std::async(func); int result = future.get();

智能指針介紹一下?

std::shared_ptr:是一種引用計數(shù)智能指針,允許多個shared_ptr對象共享同一個對象,并通過引用計數(shù)來管理內(nèi)存的釋放,當最后一個shared_ptr指向?qū)ο笪鰳?gòu)時,會釋放對象所占用的內(nèi)存。不適合處理循環(huán)引用的情況,可能導(dǎo)致內(nèi)存泄漏。

std::unique_ptr:是一種獨占式智能指針,確保只有一個指針可以指向一個對象,不能進行復(fù)制,但可以移動它的所有權(quán)。

std::weak_ptr:是用于解決std::shared_ptr可能導(dǎo)致的循環(huán)引用問題的智能指針。它允許引用shared_ptr所管理的對象,但不會增加引用計數(shù),不會影響對象的生命周期,避免循環(huán)引用導(dǎo)致的內(nèi)存泄漏。

shared_ptr的作用是什么?

在傳統(tǒng)的 C++ 編程中,使用?new?操作符分配的內(nèi)存需要手動使用?delete?操作符釋放,若忘記釋放或者在異常情況下無法執(zhí)行?delete?操作,就會造成內(nèi)存泄漏。

std::shared_ptr?可以自動處理內(nèi)存的釋放,當不再有?std::shared_ptr?指向該對象時,對象的內(nèi)存會被自動釋放。

std::shared_ptr?支持多個?std::shared_ptr?實例共享同一個對象的所有權(quán)。它通過引用計數(shù)機制來實現(xiàn)這一點,每個?std::shared_ptr?都會維護一個引用計數(shù),記錄有多少個?std::shared_ptr?共享同一個對象。當引用計數(shù)變?yōu)?0 時,對象的內(nèi)存會被釋放。

#include <iostream>
#include <memory>

class?MyClass?{
public:
? ? MyClass() { std::cout <<?"MyClass constructor"?<< std::endl; }
? ? ~MyClass() { std::cout <<?"MyClass destructor"?<< std::endl; }
};

int?main()?{
? ? std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
? ? std::shared_ptr<MyClass> ptr2 = ptr1;?// 共享所有權(quán)
? ? std::cout <<?"Use count: "?<< ptr1.use_count() << std::endl;?// 輸出 2
? ? ptr2.reset();?// 釋放 ptr2 的所有權(quán)
? ? std::cout <<?"Use count: "?<< ptr1.use_count() << std::endl;?// 輸出 1
? ??// 當 ptr1 離開作用域時,MyClass 對象的內(nèi)存會被釋放
? ??return0;
}

在這個例子中,ptr1?和?ptr2?共享同一個?MyClass?對象的所有權(quán),引用計數(shù)變?yōu)?2。當調(diào)用?ptr2.reset()?時,ptr2?釋放了對對象的所有權(quán),引用計數(shù)減為 1。當?ptr1?離開作用域時,引用計數(shù)變?yōu)?0,對象的內(nèi)存被釋放。

weak_ptr的作用是什么?如何和shared_ptr結(jié)合使用?

std::weak_ptr?主要用于輔助?std::shared_ptr?進行內(nèi)存管理,解決?std::shared_ptr?可能存在的循環(huán)引用問題,同時還可以用于觀察?std::shared_ptr?所管理對象的生命周期。

當兩個或多個?std::shared_ptr?相互引用形成循環(huán)時,會導(dǎo)致引用計數(shù)永遠不會降為 0,從而造成內(nèi)存泄漏。std::weak_ptr?不會增加所指向?qū)ο蟮囊糜嫈?shù),因此可以打破這種循環(huán)引用。

#include <iostream>
#include <memory>

class?B;

class?A?{
public:
? ? std::shared_ptr<B> b_ptr;
? ? ~A() { std::cout <<?"A destructor"?<< std::endl; }
};

class?B?{
public:
? ? std::weak_ptr<A> a_ptr; ?// 使用 std::weak_ptr 打破循環(huán)引用
? ? ~B() { std::cout <<?"B destructor"?<< std::endl; }
};

int?main()?{
? ? std::shared_ptr<A> a = std::make_shared<A>();
? ? std::shared_ptr<B> b = std::make_shared<B>();
? ? a->b_ptr = b;
? ? b->a_ptr = a;
? ??return0;
}

在上述代碼中,如果?B?類中的?a_ptr?也使用?std::shared_ptr,就會形成循環(huán)引用,導(dǎo)致?A?和?B?對象的內(nèi)存無法釋放。使用?std::weak_ptr?后,b->a_ptr?不會增加?A?對象的引用計數(shù),當?main?函數(shù)結(jié)束時,a?和?b?的引用計數(shù)降為 0,A?和?B?對象的內(nèi)存會被正確釋放。

另外,std::weak_ptr?可以用于觀察?std::shared_ptr?所管理對象的生命周期。通過?std::weak_ptr?的?expired()?方法可以檢查所指向的對象是否已經(jīng)被釋放。

#include <iostream>
#include <memory>

int?main()?{
? ? std::shared_ptr<int> shared = std::make_shared<int>(42);
? ? std::weak_ptr<int> weak = shared;

? ??if?(!weak.expired()) {
? ? ? ? std::cout <<?"Object is still alive."?<< std::endl;
? ? }

? ? shared.reset();
? ??if?(weak.expired()) {
? ? ? ? std::cout <<?"Object has been destroyed."?<< std::endl;
? ? }

? ??return0;
}

在這個例子中,weak?觀察?shared?所管理的對象。當?shared?釋放對象后,weak.expired()?返回?true,表示對象已經(jīng)被銷毀。

lambda表達式的原理是什么?

從本質(zhì)上講,Lambda 表達式是編譯器自動生成的一個匿名的函數(shù)對象(也稱為仿函數(shù))。當我們編寫一個 Lambda 表達式時,編譯器會創(chuàng)建一個未命名的類,這個類重載了函數(shù)調(diào)用運算符?operator()。

#include <iostream>

int?main()?{
? ? auto lambda = [](int?a,?int?b) {?return?a + b; };
? ??int?result = lambda(3,?4);
? ? std::cout <<?"Result: "?<< result << std::endl;
? ??return?0;
}

編譯器會將上述 Lambda 表達式轉(zhuǎn)換為類似下面的代碼:

#include <iostream>

// 編譯器生成的未命名類
class?__lambda_4_13?{
public:
? ? __lambda_4_13() =?default;
? ??inline?int?operator()(int?a,?int?b)?const?{
? ? ? ??return?a + b;
? ? }
};

int?main()?{
? ? __lambda_4_13 lambda;
? ??int?result = lambda(3,?4);
? ? std::cout <<?"Result: "?<< result << std::endl;
? ??return0;
}

stl迭代器的失效情況你知道哪些?

序列式容器(vector、deque),迭代器失效的情況

當在?vector?中插入元素時,如果插入操作導(dǎo)致容器的內(nèi)存重新分配(即插入后容器的容量不足,需要重新分配更大的內(nèi)存空間),那么所有指向?vector?的迭代器、指針和引用都會失效。因為重新分配內(nèi)存后,元素會被移動到新的內(nèi)存位置,例子代碼:

#include <iostream>
#include <vector>

int?main()?{
? ? std::vector<int> vec = {1,?2,?3};
? ? auto it = vec.begin();
? ? vec.push_back(4);?// 可能導(dǎo)致內(nèi)存重新分配
? ??// 此時 it 可能已經(jīng)失效
? ??// std::cout << *it << std::endl; // 未定義行為
? ??return?0;
}

當在?vector?中刪除元素時,指向被刪除元素的迭代器、指針和引用會失效,并且指向刪除位置之后的元素的迭代器、指針和引用也會失效。代碼如下:

#include <iostream>
#include <vector>

int?main()?{
? ? std::vector<int> vec = {1,?2,?3};
? ? auto it = vec.begin() +?1;
? ? vec.erase(vec.begin());?// 刪除第一個元素
? ??// 此時 it 失效
? ??// std::cout << *it << std::endl; // 未定義行為
? ??return?0;
}

在?deque?的中間插入元素時,所有迭代器、指針和引用都會失效,還有在?deque?的頭部或尾部插入元素時,指向元素的迭代器、指針和引用不會失效,但如果插入操作導(dǎo)致內(nèi)存重新分配,那么迭代器可能會失效。

#include <iostream>
#include <deque>

int?main()?{
? ? std::deque<int> deq = {1,?2,?3};
? ? auto it = deq.begin() +?1;
? ? deq.insert(deq.begin() +?1,?4);?// 在中間插入元素
? ??// 此時 it 失效
? ??// std::cout << *it << std::endl; // 未定義行為
? ??return?0;
}

刪除?deque?中間的元素時,所有迭代器、指針和引用都會失效,還有刪除?deque?頭部或尾部的元素時,指向被刪除元素的迭代器、指針和引用會失效。

#include <iostream>
#include <deque>

int?main()?{
? ? std::deque<int> deq = {1,?2,?3};
? ? auto it = deq.begin() +?1;
? ? deq.erase(deq.begin() +?1);?// 刪除中間元素
? ??// 此時 it 失效
? ??// std::cout << *it << std::endl; // 未定義行為
? ??return?0;
}

 

關(guān)聯(lián)式容器(setmap),迭代器失效的情況

set?在插入元素不會使任何迭代器、指針和引用失效,因為關(guān)聯(lián)式容器使用紅黑樹等平衡二叉搜索樹實現(xiàn),插入操作只是在樹中添加新節(jié)點,不會影響其他節(jié)點的內(nèi)存位置。

不過指向被刪除元素的迭代器、指針和引用會失效,其他迭代器、指針和引用不會失效。

#include <iostream>
#include <set>

int?main()?{
? ? std::set<int> s = {1,?2,?3};
? ? auto it = s.begin();
? ? auto it_to_delete = s.find(2);
? ? s.erase(it_to_delete);
? ??// 此時 it_to_delete 失效
? ??// std::cout << *it_to_delete << std::endl; // 未定義行為
? ? std::cout << *it << std::endl;?// 正常輸出
? ??return?0;
}

map?在插入元素不會使任何迭代器、指針和引用失效,原因與?set?。但是指向被刪除元素的迭代器、指針和引用會失效,其他迭代器、指針和引用不會失效。

#include <iostream>
#include <map>

int?main()?{
? ? std::map<int,?int> m = {{1,?10}, {2,?20}, {3,?30}};
? ? auto it = m.begin();
? ? auto it_to_delete = m.find(2);
? ? m.erase(it_to_delete);
? ??// 此時 it_to_delete 失效
? ??// std::cout << it_to_delete->second << std::endl; // 未定義行為
? ? std::cout << it->second << std::endl;?// 正常輸出
? ??return?0;
}

 

鏈表式容器(list),迭代器失效

在?list?插入元素不會使任何迭代器、指針和引用失效,因為鏈表的插入操作只是修改節(jié)點的指針,不會影響其他節(jié)點的內(nèi)存位置。但是,指向被刪除元素的迭代器、指針和引用會失效,其他迭代器、指針和引用不會失效。

#include <iostream>
#include <list>

int?main()?{
? ? std::list<int> lst = {1,?2,?3};
? ? auto it = lst.begin();
? ? auto it_to_delete = ++lst.begin();
? ? lst.erase(it_to_delete);
? ??// 此時 it_to_delete 失效
? ??// std::cout << *it_to_delete << std::endl; // 未定義行為
? ? std::cout << *it << std::endl;?// 正常輸出
? ??return?0;
}

unordered_map的底層結(jié)構(gòu)是什么?

底層結(jié)構(gòu)基于哈希表實現(xiàn)。

std::unordered_map?內(nèi)部維護了一個哈希桶數(shù)組,數(shù)組中的每個元素稱為一個哈希桶。每個哈希桶可以存儲一個或多個鍵值對。當多個鍵通過哈希函數(shù)計算得到相同的索引時,就會發(fā)生哈希沖突。

std::unordered_map?通常使用鏈地址法來解決哈希沖突。在鏈地址法中,每個哈希桶是一個鏈表,當發(fā)生哈希沖突時,新的鍵值對會被插入到對應(yīng)的鏈表或容器中。

linux程序崩潰怎么定位問題?

如果開啟了core dump文件的轉(zhuǎn)存,那么程序崩潰之后,就會生成 core dump 文件,這里面會有程序運行時的堆棧信息,然后我們可以用 gdp 工具去分析 core dump 文件。

gdb myapp core

在 GDB 中,使用?bt(backtrace)命令可以查看程序崩潰時的調(diào)用棧:

(gdb) bt

如果程序崩潰問題可以重現(xiàn),頁可以使用 GDB 直接調(diào)試程序。在編譯程序時,使用?-g?選項添加調(diào)試信息。例如:

gcc -g -o myapp myapp.c

使用 GDB 啟動程序并運行,當程序崩潰時,GDB 會停在崩潰的位置。

gdb?myapp
(gdb)?run

程序崩潰后,使用?bt?命令查看調(diào)用棧,找出問題所在的函數(shù)和代碼行。

有些程序會在崩潰時輸出錯誤信息到標準錯誤輸出(stderr),那么這時候直接去看錯誤日志就行了。

?cat error.log

linux的內(nèi)存是如何組織的?

操作系統(tǒng)設(shè)計了虛擬內(nèi)存,每個進程都有自己的獨立的虛擬內(nèi)存,我們所寫的程序不會直接與物理內(nèi)打交道。

有了虛擬內(nèi)存之后,它帶來了這些好處:

    第一,虛擬內(nèi)存可以使得進程對運行內(nèi)存超過物理內(nèi)存大小,因為程序運行符合局部性原理,CPU 訪問內(nèi)存會有很明顯的重復(fù)訪問的傾向性,對于那些沒有被經(jīng)常使用到的內(nèi)存,我們可以把它換出到物理內(nèi)存之外,比如硬盤上的 swap 區(qū)域。第二,由于每個進程都有自己的頁表,所以每個進程的虛擬內(nèi)存空間就是相互獨立的。進程也沒有辦法訪問其他進程的頁表,所以這些頁表是私有的,這就解決了多進程之間地址沖突的問題。第三,頁表里的頁表項中除了物理地址之外,還有一些標記屬性的比特,比如控制一個頁的讀寫權(quán)限,標記該頁是否存在等。在內(nèi)存訪問方面,操作系統(tǒng)提供了更好的安全性。

Linux 是通過對內(nèi)存分頁的方式來管理內(nèi)存,分頁是把整個虛擬和物理內(nèi)存空間切成一段段固定尺寸的大小。這樣一個連續(xù)并且尺寸固定的內(nèi)存空間,我們叫Page)。在 Linux 下,每一頁的大小為 4KB。

虛擬地址與物理地址之間通過頁表來映射,如下圖:

頁表是存儲在內(nèi)存里的,內(nèi)存管理單元?(MMU)就做將虛擬內(nèi)存地址轉(zhuǎn)換成物理地址的工作。

而當進程訪問的虛擬地址在頁表中查不到時,系統(tǒng)會產(chǎn)生一個缺頁異常,進入系統(tǒng)內(nèi)核空間分配物理內(nèi)存、更新進程頁表,最后再返回用戶空間,恢復(fù)進程的運行。

你說到缺頁中斷,能介紹一下缺頁中斷么?

缺頁中斷是指在虛擬內(nèi)存管理中,當程序試圖訪問的頁面(Page)不在物理內(nèi)存中,而在磁盤等外存上時,操作系統(tǒng)會產(chǎn)生一個中斷,將該頁面從外存調(diào)入物理內(nèi)存,以滿足程序的訪問需求。這種中斷就被稱為缺頁中斷。

對于缺頁中斷這種情況,linux操作系統(tǒng)是如何減少頁面的換入換出的呢,有哪些方式?

按需加載機制:僅在進程實際訪問內(nèi)存時才分配物理頁框,而非預(yù)先分配所有所需內(nèi)存。例如,mmap 和?malloc 僅建立虛擬地址映射,物理頁的分配延遲到首次訪問時觸發(fā)缺頁中斷,這樣可以減少不必要的物理內(nèi)存占用,避免過早的頁面換入換出。寫時復(fù)制:在進程?fork 時,父子進程共享只讀頁面;當任一進程嘗試寫入時,觸發(fā)缺頁中斷并復(fù)制新物理頁,避免不必要的內(nèi)存復(fù)制。內(nèi)存預(yù)?。何募成洌ㄈ?mmap)時,內(nèi)核在缺頁處理中預(yù)加載相鄰文件內(nèi)容到內(nèi)存,減少后續(xù)缺頁次數(shù)。內(nèi)存分配策略:優(yōu)先使用空閑頁框,避免頻繁觸發(fā)頁面回收。例如,通過伙伴系統(tǒng)和 Slab 分配器高效管理內(nèi)存碎片。

你了解數(shù)字證書么?

數(shù)字證書采用公鑰加密技術(shù),它將實體的身份信息與公鑰綁定在一起,并由權(quán)威的證書頒發(fā)機構(gòu)(CA)進行數(shù)字簽名。

具體來說,當一個實體申請數(shù)字證書時,它會生成一對密鑰,即公鑰和私鑰,然后將公鑰以及自身的身份信息提交給 CA。CA 會對這些信息進行驗證,驗證通過后,CA 使用自己的私鑰對這些信息進行簽名,生成數(shù)字證書。在數(shù)字證書中,包含了證書持有者的身份信息、公鑰、證書頒發(fā)機構(gòu)的信息、有效期等內(nèi)容。

數(shù)字證書的作用是接收方可以通過驗證數(shù)字證書來確認發(fā)送方的身份是否真實可靠。例如,在訪問銀行網(wǎng)站時,瀏覽器會驗證銀行網(wǎng)站的數(shù)字證書,以確保用戶連接的是真實的銀行網(wǎng)站,而不是假冒的釣魚網(wǎng)站。

https的握手過程說一下?

傳統(tǒng)的 TLS 握手基本都是使用 RSA 算法來實現(xiàn)密鑰交換的,在將 TLS 證書部署服務(wù)端時,證書文件其實就是服務(wù)端的公鑰,會在 TLS 握手階段傳遞給客戶端,而服務(wù)端的私鑰則一直留在服務(wù)端,一定要確保私鑰不能被竊取。

在 RSA 密鑰協(xié)商算法中,客戶端會生成隨機密鑰,并使用服務(wù)端的公鑰加密后再傳給服務(wù)端。根據(jù)非對稱加密算法,公鑰加密的消息僅能通過私鑰解密,這樣服務(wù)端解密后,雙方就得到了相同的密鑰,再用它加密應(yīng)用消息。

我用 Wireshark 工具抓了用 RSA 密鑰交換的 TLS 握手過程,你可以從下面看到,一共經(jīng)歷了四次握手:

TLS 第一次握手

首先,由客戶端向服務(wù)器發(fā)起加密通信請求,也就是 ClientHello 請求。在這一步,客戶端主要向服務(wù)器發(fā)送以下信息:

    (1)客戶端支持的 TLS 協(xié)議版本,如 TLS 1.2 版本。(2)客戶端生產(chǎn)的隨機數(shù)(Client Random),后面用于生成「會話秘鑰」條件之一。(3)客戶端支持的密碼套件列表,如 RSA 加密算法。

TLS 第二次握手

服務(wù)器收到客戶端請求后,向客戶端發(fā)出響應(yīng),也就是 SeverHello。服務(wù)器回應(yīng)的內(nèi)容有如下內(nèi)容:

    (1)確認 TLS 協(xié)議版本,如果瀏覽器不支持,則關(guān)閉加密通信。(2)服務(wù)器生產(chǎn)的隨機數(shù)(Server Random),也是后面用于生產(chǎn)「會話秘鑰」條件之一。(3)確認的密碼套件列表,如 RSA 加密算法。(4)服務(wù)器的數(shù)字證書。

TLS 第三次握手

客戶端收到服務(wù)器的回應(yīng)之后,首先通過瀏覽器或者操作系統(tǒng)中的 CA 公鑰,確認服務(wù)器的數(shù)字證書的真實性。

如果證書沒有問題,客戶端會從數(shù)字證書中取出服務(wù)器的公鑰,然后使用它加密報文,向服務(wù)器發(fā)送如下信息:

    (1)一個隨機數(shù)(pre-master key)。該隨機數(shù)會被服務(wù)器公鑰加密。(2)加密通信算法改變通知,表示隨后的信息都將用「會話秘鑰」加密通信。(3)客戶端握手結(jié)束通知,表示客戶端的握手階段已經(jīng)結(jié)束。這一項同時把之前所有內(nèi)容的發(fā)生的數(shù)據(jù)做個摘要,用來供服務(wù)端校驗。

上面第一項的隨機數(shù)是整個握手階段的第三個隨機數(shù),會發(fā)給服務(wù)端,所以這個隨機數(shù)客戶端和服務(wù)端都是一樣的。

服務(wù)器和客戶端有了這三個隨機數(shù)(Client Random、Server Random、pre-master key),接著就用雙方協(xié)商的加密算法,各自生成本次通信的「會話秘鑰」。

TLS 第四次握手

服務(wù)器收到客戶端的第三個隨機數(shù)(pre-master key)之后,通過協(xié)商的加密算法,計算出本次通信的「會話秘鑰」。

然后,向客戶端發(fā)送最后的信息:

    (1)加密通信算法改變通知,表示隨后的信息都將用「會話秘鑰」加密通信。(2)服務(wù)器握手結(jié)束通知,表示服務(wù)器的握手階段已經(jīng)結(jié)束。這一項同時把之前所有內(nèi)容的發(fā)生的數(shù)據(jù)做個摘要,用來供客戶端校驗。

至此,整個 TLS 的握手階段全部結(jié)束。接下來,客戶端與服務(wù)器進入加密通信,就完全是使用普通的 HTTP 協(xié)議,只不過用「會話秘鑰」加密內(nèi)容。

對于https中的加密算法,rsa和ecc橢圓加密,這兩者有什么區(qū)別么?

    RSA 加密算法:國際標準算法,應(yīng)用較早的算法之一,普遍性更強,相比 ECC 算法的適用范圍更廣,兼容性更好,一般采用2048位的加密長度,服務(wù)端性能消耗較高。ECC 加密算法:橢圓加密算法,新一代算法趨勢主流,一般采用256位加密長度(相當于 RSA 3072 位加密強度)更安全,抗攻擊性更強,相比 RSA 算法加密速度快,效率更高,服務(wù)器資源消耗更低。

排序算法介紹一下?

    冒泡排序:通過相鄰元素的比較和交換,每次將最大(或最?。┑脑刂鸩健懊芭荨钡阶詈螅ɑ蜃钋埃?。時間復(fù)雜度:最好情況下O(n),最壞情況下O(n^2),平均情況下O(n^2)。
    空間復(fù)雜度:O(1)。插入排序:將待排序元素逐個插入到已排序序列的合適位置,形成有序序列。時間復(fù)雜度:最好情況下O(n),最壞情況下O(n^2),平均情況下O(n^2),空間復(fù)雜度:O(1)。選擇排序(Selection Sort):通過不斷選擇未排序部分的最?。ɑ蜃畲螅┰?,并將其放置在已排序部分的末尾(或開頭)。
    時間復(fù)雜度:最好情況下O(n^2),最壞情況下O(n^2),平均情況下O(n^2),空間復(fù)雜度:O(1)??焖倥判颍≦uick Sort):通過選擇一個基準元素,將數(shù)組劃分為兩個子數(shù)組,使得左子數(shù)組的元素都小于(或等于)基準元素,右子數(shù)組的元素都大于(或等于)基準元素,然后對子數(shù)組進行遞歸排序。時間復(fù)雜度:最好情況下O(nlogn),最壞情況下O(n^2),平均情況下O(nlogn),空間復(fù)雜度:最好情況下O(logn),最壞情況下O(n)。歸并排序(Merge Sort):將數(shù)組不斷分割為更小的子數(shù)組,然后將子數(shù)組進行合并,合并過程中進行排序。時間復(fù)雜度:最好情況下O(nlogn),最壞情況下O(nlogn),平均情況下O(nlogn)??臻g復(fù)雜度:O(n)。堆排序(Heap Sort):通過將待排序元素構(gòu)建成一個最大堆(或最小堆),然后將堆頂元素與末尾元素交換,再重新調(diào)整堆,重復(fù)該過程直到排序完成。時間復(fù)雜度:最好情況下O(nlogn),最壞情況下O(nlogn),平均情況下O(nlogn)??臻g復(fù)雜度:O(1)。

大文件排序應(yīng)該怎么辦?

可以采用外部排序的方式。外部排序主要分為兩個階段:

1. 分段排序(生成初始歸并段):將大文件分割成多個較小的片段,這些片段的大小要能夠適應(yīng)內(nèi)存的限制,接著把每個片段加載到內(nèi)存中,使用內(nèi)部排序算法(如快速排序、歸并排序等)對其進行排序,最后將排序好的片段寫回到磁盤中,這些片段就被稱為初始歸并段。2. 歸并操作:將多個初始歸并段逐步合并成一個有序的大文件。在歸并過程中,每次從各個歸并段中讀取一部分數(shù)據(jù)到內(nèi)存中,比較這些數(shù)據(jù)并將最小(或最大)的元素依次寫入輸出文件,同時不斷從歸并段中補充數(shù)據(jù),直到所有歸并段的數(shù)據(jù)都被處理完畢。

理想汽車

理想汽車

理想汽車致力于為家庭打造更安全、更便捷、更舒適的智能電動車,產(chǎn)品包括理想L9(全尺寸六座SUV)、理想L8(中大型六座SUV)、理想L7(中大型五座SUV)。自研增程電動系統(tǒng)、魔毯空懸、智能駕駛、智能空間。

理想汽車致力于為家庭打造更安全、更便捷、更舒適的智能電動車,產(chǎn)品包括理想L9(全尺寸六座SUV)、理想L8(中大型六座SUV)、理想L7(中大型五座SUV)。自研增程電動系統(tǒng)、魔毯空懸、智能駕駛、智能空間。收起

查看更多

相關(guān)推薦