又被百度撈起來了,能贏嗎?

大家好,我是小林。
之前有同學(xué)反饋說,有沒有 C++ 服務(wù)器開發(fā)的面試呀?
還真有,最近有 C++ 同學(xué)被百度從簡歷池?fù)破饋砻嬖嚵?,目前?jīng)歷了一二面,我把比較通用的面試問題抽離出來跟大家分享一波。
這次主要面試涵蓋的知識點(diǎn):
- MySQL:索引結(jié)構(gòu)、索引應(yīng)用、SQL調(diào)優(yōu)
- C++:特性、指針與引用、多態(tài)、sizeof、stl
- 計算機(jī)網(wǎng)絡(luò):tcp socket 編程、tcp 四次揮手過程
- 操作系統(tǒng):虛擬內(nèi)存、epoll、linux 線程、線程同步技術(shù)、Linux 命令
MySQL
SQL調(diào)優(yōu)是怎樣的一個歷程?
答:通過mysql 慢查詢?nèi)罩菊业铰?sql,然后通過 explain 去分析 sql 語句。根據(jù)生成的執(zhí)行計劃進(jìn)一步的做優(yōu)化, 比如是否是因?yàn)樗饕?dǎo)致沒有走索引,還是因?yàn)闆]有建立索引的導(dǎo)致沒有走索引,并且還可以考慮通過簡歷聯(lián)合索引來進(jìn)行覆蓋索引優(yōu)化,減少回表。
MySQL索引簡單講一講自己的理解?
答:MySQL索引常用的是B+樹,也有B樹和紅黑樹。B+樹對比B樹的好處在于,B+樹是只有葉子點(diǎn)有數(shù)據(jù),B樹是非葉子節(jié)點(diǎn)也會有數(shù)據(jù),B樹相對于B+樹,讀取數(shù)據(jù)時,系統(tǒng)I/O調(diào)用的次數(shù)更多;還有紅黑樹也可以用作索引,但是紅黑樹是二叉樹,當(dāng)數(shù)據(jù)多的時候,高度會越來越高,相對B+樹,系統(tǒng)I/O調(diào)用的次數(shù)更多了
那么在實(shí)際過程中,索引應(yīng)該怎么用?
答:索引常用的是主鍵索引和聯(lián)合索引,聯(lián)合索引的話是將兩個或者多個會一同查詢,且需要頻繁查詢的鍵組成聯(lián)合索引。
追問:還有嗎?
答:還有普通索引,對某個常用的字段也可以進(jìn)行普通索引。
操作系統(tǒng)
虛擬內(nèi)存是個什么東西?
答:為了使得程序每次都是從地址0開始,而不用去查找實(shí)際地址,所以設(shè)置一個虛擬內(nèi)存,使得程序可以都從0開始,如果用到了,再由虛擬內(nèi)存和物理內(nèi)存進(jìn)行一一映射。
eopll水平測發(fā)和邊緣測發(fā)的差距
答:(看到過,但是忘記了)對這個問題不是很清楚
補(bǔ)充:
epoll 支持兩種事件觸發(fā)模式,分別是邊緣觸發(fā)(*edge-triggered,ET*)**和**水平觸發(fā)(*level-triggered,LT*)。
這兩個術(shù)語還挺抽象的,其實(shí)它們的區(qū)別還是很好理解的。
- 使用邊緣觸發(fā)模式時,當(dāng)被監(jiān)控的 Socket 描述符上有可讀事件發(fā)生時,服務(wù)器端只會從 epoll_wait 中蘇醒一次,即使進(jìn)程沒有調(diào)用 read 函數(shù)從內(nèi)核讀取數(shù)據(jù),也依然只蘇醒一次,因此我們程序要保證一次性將內(nèi)核緩沖區(qū)的數(shù)據(jù)讀取完;
- 使用水平觸發(fā)模式時,當(dāng)被監(jiān)控的 Socket 上有可讀事件發(fā)生時,服務(wù)器端不斷地從 epoll_wait 中蘇醒,直到內(nèi)核緩沖區(qū)數(shù)據(jù)被 read 函數(shù)讀完才結(jié)束,目的是告訴我們有數(shù)據(jù)需要讀取;
舉個例子,你的快遞被放到了一個快遞箱里,如果快遞箱只會通過短信通知你一次,即使你一直沒有去取,它也不會再發(fā)送第二條短信提醒你,這個方式就是邊緣觸發(fā);如果快遞箱發(fā)現(xiàn)你的快遞沒有被取出,它就會不停地發(fā)短信通知你,直到你取出了快遞,它才消停,這個就是水平觸發(fā)的方式。
這就是兩者的區(qū)別,水平觸發(fā)的意思是只要滿足事件的條件,比如內(nèi)核中有數(shù)據(jù)需要讀,就一直不斷地把這個事件傳遞給用戶;而邊緣觸發(fā)的意思是只有第一次滿足條件的時候才觸發(fā),之后就不會再傳遞同樣的事件了。
如果使用水平觸發(fā)模式,當(dāng)內(nèi)核通知文件描述符可讀寫時,接下來還可以繼續(xù)去檢測它的狀態(tài),看它是否依然可讀或可寫。所以在收到通知后,沒必要一次執(zhí)行盡可能多的讀寫操作。
如果使用邊緣觸發(fā)模式,I/O 事件發(fā)生時只會通知一次,而且我們不知道到底能讀寫多少數(shù)據(jù),所以在收到通知后應(yīng)盡可能地讀寫數(shù)據(jù),以免錯失讀寫的機(jī)會。因此,我們會循環(huán)從文件描述符讀寫數(shù)據(jù),那么如果文件描述符是阻塞的,沒有數(shù)據(jù)可讀寫時,進(jìn)程會阻塞在讀寫函數(shù)那里,程序就沒辦法繼續(xù)往下執(zhí)行。所以,邊緣觸發(fā)模式一般和非阻塞 I/O 搭配使用,程序會一直執(zhí)行 I/O 操作,直到系統(tǒng)調(diào)用(如 read 和 write)返回錯誤,錯誤類型為 EAGAIN 或 EWOULDBLOCK。
一般來說,邊緣觸發(fā)的效率比水平觸發(fā)的效率要高,因?yàn)檫吘売|發(fā)可以減少 epoll_wait 的系統(tǒng)調(diào)用次數(shù),系統(tǒng)調(diào)用也是有一定的開銷的的,畢竟也存在上下文的切換。
select/poll 只有水平觸發(fā)模式,epoll 默認(rèn)的觸發(fā)模式是水平觸發(fā),但是可以根據(jù)應(yīng)用場景設(shè)置為邊緣觸發(fā)模式。
那么你在Linux環(huán)境下有調(diào)用過系統(tǒng)接口去創(chuàng)建過線程什么的嗎?
答:可以用 fork 創(chuàng)建進(jìn)行,用 phtread 創(chuàng)建線程。
img
那你覺得同步技術(shù)有哪些技術(shù)?
答:
- 首先是匿名管道,但是有個缺點(diǎn),所有文件都共享,并且取/寫只能一個操作;
- 緊接著是命名管道,可以用于兩個指定文件間進(jìn)行同步;
- 然后是信號量,我認(rèn)為信號量和鎖類似,通過信號量,進(jìn)程之間進(jìn)行間接通信;信號和信號量相類似。
- 還有互斥鎖、讀寫鎖、自選鎖
C++
C++特性介紹一下?
答:講了封裝繼承多態(tài).
- 封裝是將一些數(shù)據(jù)和函數(shù)封裝到類中,這樣外層調(diào)用類只會調(diào)用到設(shè)計者想讓他調(diào)用的方法;
- 繼承的話,我常是設(shè)計一個基類,然后分別設(shè)置子類去繼承基類的一些方法,尤其是虛函數(shù),針對不同子類的特點(diǎn)對虛函數(shù)進(jìn)行重寫。
- 繼承還有公有和私有兩種方法,公有繼承是將基類的成員都原封不動的繼承下來,私有繼承則會將其改為私有部分;多態(tài)的話,是有函數(shù)重載和之前提到的虛函數(shù),函數(shù)重載是可以使得相同的函數(shù)面對不同的參數(shù)個數(shù)或者類型進(jìn)行不同的方式實(shí)現(xiàn)。
講一下多態(tài)的理解
答:多態(tài)的話,我的理解是函數(shù)重載和虛函數(shù),函數(shù)重載的好處我認(rèn)為是同一個函數(shù)名可以對不同的參數(shù)類型或者參數(shù)個數(shù)進(jìn)行不同的實(shí)現(xiàn);虛函數(shù)我認(rèn)為是可以使得子類在繼承父類的時候,基于子類的特點(diǎn)重寫父類的一些函數(shù)。
實(shí)際上用的話,虛函數(shù)是怎么用的?
答:將子類指針賦給父類對象,然后通過父類對象調(diào)用子類的虛函數(shù),也可以通過作用域去調(diào)用父類的虛函數(shù)。
除了指針,你認(rèn)為引用可以實(shí)現(xiàn)嗎?
答:我認(rèn)為應(yīng)該可以
為什么呢,你對引用的理解是什么?
答:因?yàn)槲艺J(rèn)為引用其實(shí)相當(dāng)于變量的地址值,類似一個指針。
那么引用是不是可以理解為const的一個指針?
答:我認(rèn)為是可以的
現(xiàn)在有一個類,用g++去編譯它,編譯器會給它生成哪些函數(shù)?
答:默認(rèn)構(gòu)造函數(shù),析構(gòu)函數(shù),默認(rèn)拷貝構(gòu)造函數(shù)。
這時候用sizeof對這個類計算一下,得到的是多少?
答:1
為什么呢?
答:我就說了C++是固定地址的,如果是0的話,調(diào)用的時候會有地址沖突。
說到這個sizeof,你覺得它是函數(shù)嗎?
答:它是運(yùn)算符
運(yùn)算符的話,一般在什么時候給它定好?
答:編譯階段
現(xiàn)在定義一個函數(shù),函數(shù)的形參是B,是一個int數(shù)組,int b[10],現(xiàn)在傳入一個int c[5]實(shí)參那么此時sizeof(b)的大小是多少?
答:一個指針的大小, 指針的大小通常是4或8字節(jié),具體取決于操作系統(tǒng)和編譯器的位數(shù)。如果是 32 位操作系統(tǒng)則是 4 字節(jié), 64 位操作系統(tǒng)是 8 字節(jié)
sizeof和C的大小無關(guān)嗎?
答:我認(rèn)為是的
STL
STL用map去刪除元素的時候,用迭代器去刪除,要注意哪些東西?
答:
錯誤的寫法:
map<string,int> testMap;
for(auto it = testMap.begin(); it != testMap.end(); ++it)
{
if(it->second == xxx)
{
testMap.erase(it); //這里會出問題
}
}
錯誤原因:it指針被erase之后會失效,for循環(huán)中對it操作其結(jié)果都是不可預(yù)料的,可能造成程序崩潰。
正確的寫法:
map<string,int> testMap;
for(auto it = testMap.begin(); it != testMap.end();)
{
if(it->second == xxx)
{
testMap.erase(it++);
}
else
{
it++;
}
}
正確原因:新寫法的迭代器的自增從for頭部中取出,放在循環(huán)體中。it++返回了自增前的迭代器的一個臨時拷貝。然后這個臨時迭代器指向的內(nèi)容被刪除了,但是it本身已經(jīng)自增到下一個位置了,不受影響。
list其實(shí)是一個鏈表,deque是一個隊(duì)列,那么你認(rèn)為list和deque的區(qū)別是什么?
答:一個內(nèi)存空間是不連續(xù)的,一個是段連續(xù)的。
那么deque是不是可以用list去實(shí)現(xiàn)呢?
答:我認(rèn)為是的
計算機(jī)網(wǎng)絡(luò)
如果要實(shí)現(xiàn)一個TCP服務(wù)器要哪些(套接字)接口?
答:(一開始沒有聽到套接字三個字,給我干懵了,不知道是要什么接口,就直接答了不了解;然后面試官說你沒用過Socket編程嗎,我才反應(yīng)過來時套接字)先是用bind函數(shù)綁定一個套接字,然后進(jìn)行Listen監(jiān)聽,監(jiān)聽到連接請求后,調(diào)用connect函數(shù),先將socket放到半連接隊(duì)列,然后再通過三次握手到全連接隊(duì)列,最后返回一個新的socket進(jìn)行通信。(忘記了accept函數(shù),connet函數(shù)是客戶端的)
基于 TCP 協(xié)議的客戶端和服務(wù)端工作
listen函數(shù)的第二個參數(shù)是什么?
答:(忘記了)
補(bǔ)充:
Linux內(nèi)核中會維護(hù)兩個隊(duì)列:
- 半連接隊(duì)列(SYN 隊(duì)列):接收到一個 SYN 建立連接請求,處于 SYN_RCVD 狀態(tài);
- 全連接隊(duì)列(Accpet 隊(duì)列):已完成 TCP 三次握手過程,處于 ESTABLISHED 狀態(tài);
SYN 隊(duì)列 與 Accpet 隊(duì)列
int listen (int socketfd, int backlog)
- 參數(shù)一 socketfd 為 socketfd 文件描述符
- 參數(shù)二 backlog,這參數(shù)在歷史版本有一定的變化
在早期 Linux 內(nèi)核 backlog 是 SYN 隊(duì)列大小,也就是未完成的隊(duì)列大小。
在 Linux 內(nèi)核 2.2 之后,backlog 變成 accept 隊(duì)列,也就是已完成連接建立的隊(duì)列長度,所以現(xiàn)在通常認(rèn)為 backlog 是 accept 隊(duì)列。
但是上限值是內(nèi)核參數(shù) somaxconn 的大小,也就說 accpet 隊(duì)列長度 = min(backlog, somaxconn)。
怎樣設(shè)置一個套接字為非阻塞模式?
答:
默認(rèn)創(chuàng)建的 socket 都是阻塞模式的,在 Linux 平臺上,我們可以使用 fcntl() 函數(shù)或 ioctl() 函數(shù)給創(chuàng)建的 socket 增加 O_NONBLOCK 標(biāo)志來將 socket 設(shè)置成非阻塞模式。示例代碼如下:
int oldSocketFlag = fcntl(sockfd, F_GETFL, 0);
int newSocketFlag = oldSocketFlag | O_NONBLOCK;
fcntl(sockfd, F_SETFL, newSocketFlag);
ioctl() 函數(shù) 與 fcntl() 函數(shù)使用方式基本一致,這里就不再給出示例代碼了。
當(dāng)然,Linux 下的 socket() 創(chuàng)建函數(shù)也可以直接在創(chuàng)建時將 socket 設(shè)置為非阻塞模式,socket() 函數(shù)的簽名如下:
int socket(int domain, int type, int protocol);
給 type 參數(shù)增加一個 SOCK_NONBLOCK 標(biāo)志即可,例如:
int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
TCP四次揮手,什么時候處于CLOSE_WAIT狀態(tài)?
答:被動關(guān)閉方在調(diào)用 close 函數(shù),發(fā)送第三次揮手的時候, 也就是 fin 報文的時候, 被動關(guān)閉方會進(jìn)入到 close_wait 狀態(tài)。
雙方都可以主動斷開連接,斷開連接后主機(jī)中的「資源」將被釋放,四次揮手的過程如下圖:
客戶端主動關(guān)閉連接 —— TCP 四次揮手
- 客戶端打算關(guān)閉連接,此時會發(fā)送一個 TCP 首部
FIN標(biāo)志位被置為1的報文,也即FIN報文,之后客戶端進(jìn)入FIN_WAIT_1狀態(tài)。 - 服務(wù)端收到該報文后,就向客戶端發(fā)送
ACK應(yīng)答報文,接著服務(wù)端進(jìn)入CLOSE_WAIT狀態(tài)。 - 客戶端收到服務(wù)端的
ACK應(yīng)答報文后,之后進(jìn)入FIN_WAIT_2狀態(tài)。 - 等待服務(wù)端處理完數(shù)據(jù)后,也向客戶端發(fā)送
FIN報文,之后服務(wù)端進(jìn)入LAST_ACK狀態(tài)。 - 客戶端收到服務(wù)端的
FIN報文后,回一個ACK應(yīng)答報文,之后進(jìn)入TIME_WAIT狀態(tài) - 服務(wù)端收到了
ACK應(yīng)答報文后,就進(jìn)入了CLOSE狀態(tài),至此服務(wù)端已經(jīng)完成連接的關(guān)閉。 - 客戶端在經(jīng)過
2MSL一段時間后,自動進(jìn)入CLOSE狀態(tài),至此客戶端也完成連接的關(guān)閉。
每個方向都需要一個 FIN 和一個 ACK,因此通常被稱為四次揮手。
TCP四次揮手,什么時候處于TIME_WAIT狀態(tài)?
答:主動關(guān)閉方在收到第三次揮手 fin 報文的時候, tcp 連接就會處于 TIME_WAIT 狀態(tài). 會在這個狀態(tài)等待 2msl 的時間, 時間到了 ,就會進(jìn)入到 close 狀態(tài).
Linux 命令
linux 用top命令你一般關(guān)注什么?
答:
image-20240115145247592
- CPU利用率:關(guān)注系統(tǒng)整體的CPU利用率以及各個進(jìn)程的CPU占用情況,可以通過查看%CPU列來了解。
- 內(nèi)存使用:關(guān)注系統(tǒng)的內(nèi)存使用情況,包括總內(nèi)存、已使用內(nèi)存和空閑內(nèi)存等,可以通過查看內(nèi)存相關(guān)的統(tǒng)計信息來了解。
- 負(fù)載情況:關(guān)注系統(tǒng)的負(fù)載情況,包括1分鐘、5分鐘和15分鐘的負(fù)載平均值,可以通過查看load average來了解。
- I/O活動:關(guān)注系統(tǒng)的磁盤I/O活動情況,包括磁盤的讀寫速度、等待時間等,可以通過查看磁盤相關(guān)的統(tǒng)計信息來了解。
那你常用的linux命令 可以自己說一說?
答:
- 文件相關(guān)(mv mkdir cd ls)
- 進(jìn)程相關(guān)( ps top netstate )
- 權(quán)限相關(guān)(chmod chown useradd groupadd)
- 網(wǎng)絡(luò)相關(guān)(netstat ip addr)
- 測試相關(guān)(測試網(wǎng)絡(luò)連通性:ping 測試端口連通性:telnet)
算法
動態(tài)規(guī)劃算法可以大概講一講嗎?
答:只講了怎么做動態(tài)規(guī)劃的題,不知道應(yīng)該怎么講原理
項(xiàng)目
- 大概講一講項(xiàng)目
- 講一下簡歷里的技術(shù)難點(diǎn)
反問
部門是做什么?
面試官答:互聯(lián)網(wǎng)云平臺相關(guān)的
歷史好文:
