平臺(tái)研發(fā)筆試復(fù)盤:操作系統(tǒng)與網(wǎng)絡(luò)核心考點(diǎn)解析)
1. 試卷整體印象與考察邏輯先說(shuō)個(gè)背景。2015年的阿里巴巴基礎(chǔ)平臺(tái)研發(fā)崗實(shí)習(xí)生招聘是我當(dāng)年參加過(guò)的最“硬核”的筆試之一。那個(gè)時(shí)期的基礎(chǔ)平臺(tái)團(tuán)隊(duì)還在做阿里內(nèi)部大規(guī)模分布式存儲(chǔ)、消息中間件、統(tǒng)一調(diào)度系統(tǒng)這類底層基礎(chǔ)設(shè)施所以筆試題基本不摻水分不考八股文式的死記硬背而是直接奔著一個(gè)問(wèn)題去的這個(gè)實(shí)習(xí)生來(lái)了之后能不能馬上看代碼、能不能理解線上系統(tǒng)崩潰時(shí)的核心邏輯、能不能在導(dǎo)師給了一堆底層源碼時(shí)讀得下去。整套試卷大概涵蓋操作系統(tǒng)、計(jì)算機(jī)網(wǎng)絡(luò)、Linux使用、C/C語(yǔ)言特性、數(shù)據(jù)結(jié)構(gòu)與算法、分布式系統(tǒng)常識(shí)六大塊題量不小時(shí)間壓力很明顯。我印象最深的倒不是某一道題本身而是整張卷子呈現(xiàn)出的一種態(tài)度——它不追求你把每個(gè)知識(shí)點(diǎn)背得多熟而是追求你在大腦疲勞的狀態(tài)下還能不能保持清晰的邏輯鏈條。比如一道看似考fork進(jìn)程的題目實(shí)際上是在考你對(duì)虛擬內(nèi)存、寫時(shí)拷貝、文件描述符繼承、緩沖區(qū)刷新的綜合理解任何一個(gè)環(huán)節(jié)沒(méi)打通都會(huì)答錯(cuò)。如果你現(xiàn)在準(zhǔn)備類似的崗位我的建議是別把精力花在背“面經(jīng)答案”上。基礎(chǔ)平臺(tái)研發(fā)對(duì)基本功的要求是實(shí)打?qū)嵉牟僮飨到y(tǒng)、網(wǎng)絡(luò)協(xié)議、C/C內(nèi)存模型這三塊怎么強(qiáng)調(diào)都不過(guò)分。本文后面我就按題型和知識(shí)點(diǎn)兩條線展開(kāi)結(jié)合我自己當(dāng)年的答題情況和后來(lái)參與校招面試看到的常見(jiàn)問(wèn)題寫一份有實(shí)際操作價(jià)值的復(fù)盤。2. 操作系統(tǒng)考點(diǎn)拆解從fork到內(nèi)存管理考的都是底層機(jī)制的理解2.1 進(jìn)程與線程2015年的卷子就已經(jīng)在問(wèn)線程模型了操作系統(tǒng)部分的題目現(xiàn)在看起來(lái)依然是經(jīng)典。進(jìn)程與線程的對(duì)比老生常談但阿里巴巴的題會(huì)多繞一層不只是問(wèn)“進(jìn)程和線程的區(qū)別是什么”而是給定一段多線程程序讓你分析輸出結(jié)果。這就把問(wèn)題從背概念拉到了真正理解線程調(diào)度和同步機(jī)制的水平上。我記得有一類題很典型題目給你一個(gè)共享變量幾個(gè)線程分別對(duì)它做自增操作每個(gè)線程循環(huán)10000次問(wèn)你最終值是多少。答案是“不確定”因?yàn)樽栽霾僮鞑皇窃有缘牡绻阒淮稹安淮_定”三個(gè)字基本拿不到分——題目后面會(huì)追問(wèn)為什么。這時(shí)候你需要說(shuō)出三個(gè)層面第一i在底層對(duì)應(yīng)load、add、store三條指令第二這三條指令之間可能發(fā)生線程上下文切換第三由于各線程的工作內(nèi)存和主內(nèi)存之間的一致性延遲一個(gè)線程的寫入可能覆蓋另一個(gè)線程的更新。這三層說(shuō)全了才算真正理解。另外一個(gè)考點(diǎn)是線程模型?;A(chǔ)平臺(tái)開(kāi)發(fā)經(jīng)常要寫高并發(fā)服務(wù)所以線程池原理、用戶態(tài)線程與內(nèi)核態(tài)線程的映射關(guān)系這類題目出現(xiàn)的頻率很高。我當(dāng)時(shí)遇到的是關(guān)于線程庫(kù)實(shí)現(xiàn)的題考點(diǎn)是“一對(duì)一模型、多對(duì)一模型、多對(duì)多模型”的優(yōu)缺點(diǎn)對(duì)比。答題時(shí)不要只列概念要結(jié)合場(chǎng)景說(shuō)。比如多對(duì)一模型在用戶態(tài)調(diào)度上下文切換開(kāi)銷小但一個(gè)線程阻塞會(huì)讓整個(gè)進(jìn)程阻塞一對(duì)一模型能利用多核但線程切換要進(jìn)內(nèi)核開(kāi)銷大。你能自己推導(dǎo)出這些結(jié)論比背課本強(qiáng)得多。2.2 虛擬內(nèi)存與分頁(yè)筆試?yán)镒钊菀妆缓雎缘碾[藏大boss虛擬內(nèi)存這部分2015年的試卷直接給了我一個(gè)下馬威。有一道題是關(guān)于頁(yè)面置換算法的考LRU。LRU本身不復(fù)雜但題目設(shè)置了一個(gè)對(duì)比給定一個(gè)頁(yè)面訪問(wèn)序列分別用FIFO和LRU計(jì)算缺頁(yè)次數(shù)問(wèn)你為什么LRU在有些場(chǎng)景下缺頁(yè)率反而比FIFO高或者一樣高。這個(gè)問(wèn)題其實(shí)是在提醒你任何算法都有局部的適用條件。LRU依賴時(shí)間局部性如果程序恰好是順序掃描大數(shù)組每次訪問(wèn)的頁(yè)面都是一次性的LRU和FIFO的表現(xiàn)可能差不多甚至因?yàn)長(zhǎng)RU維護(hù)成本高而更慢。做題歸做題后來(lái)我在實(shí)際項(xiàng)目中理解更深了。基礎(chǔ)平臺(tái)服務(wù)里內(nèi)存分配器、緩存系統(tǒng)、鎖機(jī)制處處都是“置換策略”的影子。你看Redis的近似LRU實(shí)現(xiàn)看Linux內(nèi)核的頁(yè)面回收算法本質(zhì)都是對(duì)“如何淘汰一個(gè)未來(lái)最不可能被訪問(wèn)的頁(yè)面”的折中。筆試?yán)锟糒RU不只是讓你背手寫鏈表加哈希表的LRU實(shí)現(xiàn)更是看你會(huì)不會(huì)在緩存設(shè)計(jì)時(shí)考慮scan-resistance的問(wèn)題。內(nèi)存對(duì)齊也是一個(gè)高頻考點(diǎn)?;A(chǔ)平臺(tái)研發(fā)寫底層代碼的機(jī)會(huì)多結(jié)構(gòu)體對(duì)齊直接影響內(nèi)存占用和訪問(wèn)性能。2015年那道題給了個(gè)結(jié)構(gòu)體含有char、int、short等幾個(gè)字段讓你求sizeof。答案是24還是16取決于平臺(tái)和編譯選項(xiàng)但核心是你要明白對(duì)齊規(guī)則每個(gè)成員按自身大小對(duì)齊結(jié)構(gòu)體總大小按最大對(duì)齊數(shù)對(duì)齊。這類題容易錯(cuò)在忽略了編譯器默認(rèn)的對(duì)齊策略我當(dāng)年就栽了一次所以建議你刷題時(shí)親自動(dòng)手用sizeof打印驗(yàn)證而不只是在紙上推導(dǎo)。2.3 進(jìn)程間通信與同步問(wèn)題筆試和實(shí)際工程之間的橋梁進(jìn)程間通信是基礎(chǔ)平臺(tái)研發(fā)的必修課筆試題也毫不客氣。管道、共享內(nèi)存、消息隊(duì)列、信號(hào)量、socket這些IPC方式要能橫向比較。我印象里有一道判斷題問(wèn)“管道是單向通信的”是否正確。這題比較基礎(chǔ)但擴(kuò)展出來(lái)就有東西了管道分匿名管道和命名管道匿名管道只能用于父子進(jìn)程而且默認(rèn)半雙工如果你要雙向通信得建兩個(gè)管道。這里有個(gè)細(xì)節(jié)很多人忽略——管道的數(shù)據(jù)傳輸是在內(nèi)核緩沖區(qū)中完成的大小有限制。當(dāng)時(shí)題目問(wèn)的就是“管道寫端寫數(shù)據(jù)阻塞的條件”答案是緩沖區(qū)滿。你沒(méi)有真正用管道寫過(guò)大量數(shù)據(jù)很難體會(huì)這個(gè)限制。同步問(wèn)題里哲學(xué)家就餐問(wèn)題是必考的。2015年的題目不是讓你背信號(hào)量解法而是給出一個(gè)具體實(shí)現(xiàn)讓你指出其中可能導(dǎo)致死鎖的代碼段。這就很真實(shí)了因?yàn)閷?shí)際工程里不會(huì)有人告訴你“我這里有死鎖”而是系統(tǒng)莫名其妙卡死你用gdb attach上去發(fā)現(xiàn)所有線程都在等一個(gè)永遠(yuǎn)等不到的鎖。答題思路要清晰死鎖的四個(gè)必要條件——互斥、持有并等待、非搶占、循環(huán)等待。你從這四個(gè)條件逐一去對(duì)照代碼定位問(wèn)題就快很多。我當(dāng)時(shí)在試卷上直接把這四條列在草稿紙上然后逐個(gè)打鉤排除效率很高。3. 計(jì)算機(jī)網(wǎng)絡(luò)與Linux考點(diǎn)紙上談兵不如真抓實(shí)測(cè)3.1 TCP三次握手與狀態(tài)遷移絕不能只背“三次”和“四次”網(wǎng)絡(luò)部分的題占了不少篇幅TCP協(xié)議是絕對(duì)核心。2015年考了三次握手、四次揮手、TIME_WAIT狀態(tài)、擁塞控制這些經(jīng)典考點(diǎn)。但是別以為把狀態(tài)圖背下來(lái)就萬(wàn)事大吉題目的問(wèn)法非常刁鉆。比如問(wèn)主動(dòng)關(guān)閉連接的一方在TIME_WAIT狀態(tài)需要等待多久為什么答案是2MSL大約是1到4分鐘取決于系統(tǒng)實(shí)現(xiàn)。為什么非要等2MSL因?yàn)橐WC最后一個(gè)ACK能夠到達(dá)對(duì)端如果ACK丟失對(duì)端會(huì)重發(fā)FIN主動(dòng)關(guān)閉方需要有時(shí)間來(lái)處理這個(gè)重發(fā)的FIN同時(shí)還要保證舊連接中的所有報(bào)文在網(wǎng)絡(luò)中徹底消失避免干擾新連接。這個(gè)知識(shí)點(diǎn)在基礎(chǔ)平臺(tái)的實(shí)際工作中非常重要。你開(kāi)發(fā)一個(gè)高并發(fā)短連接服務(wù)連接頻繁建立和關(guān)閉如果服務(wù)器作為主動(dòng)關(guān)閉方TIME_WAIT狀態(tài)的socket會(huì)大量堆積導(dǎo)致端口耗盡或者連接建立失敗。我自己做長(zhǎng)連接網(wǎng)關(guān)時(shí)遇到過(guò)幾次這樣的線上問(wèn)題最終方案都是調(diào)整tcp_tw_reuse和tcp_max_tw_buckets參數(shù)或者改造連接復(fù)用邏輯。筆試考TIME_WAIT本質(zhì)上就是在篩選有多少人真的寫過(guò)網(wǎng)絡(luò)服務(wù)而不只是看過(guò)《TCP/IP詳解》。還有一道關(guān)于TCP擁塞控制的題問(wèn)了慢啟動(dòng)和擁塞避免的區(qū)別。這里有個(gè)容易混淆的點(diǎn)慢啟動(dòng)是每收到一個(gè)ACK擁塞窗口增加一個(gè)MSS所以是指數(shù)增長(zhǎng)擁塞避免是每經(jīng)過(guò)一個(gè)RTT窗口加一是線性增長(zhǎng)。很多人的錯(cuò)誤在于把慢啟動(dòng)理解為“每次增加一點(diǎn)點(diǎn)的緩慢過(guò)程”但實(shí)際上慢啟動(dòng)名字叫slow增長(zhǎng)卻非常快。后來(lái)我自己調(diào)網(wǎng)絡(luò)性能的時(shí)候才真正有了體感慢啟動(dòng)在大帶寬高延遲的鏈路上可能要花很長(zhǎng)時(shí)間才能把窗口漲到目標(biāo)值所以出現(xiàn)了TCP Fast Open、初始窗口擴(kuò)大這些優(yōu)化手段。筆試考這個(gè)是想看你知不知道瓶頸在哪。3.2 select、poll、epoll基礎(chǔ)平臺(tái)的必考三兄弟2015年的試卷已經(jīng)考了I/O多路復(fù)用而且考得不淺。題目直接給了三種模型的對(duì)比表格讓你補(bǔ)充完整??嫉帽容^細(xì)的點(diǎn)包括select有FD_SETSIZE限制默認(rèn)1024poll沒(méi)有連接數(shù)限制但性能隨連接數(shù)線性下降epoll通過(guò)紅黑樹和就緒鏈表實(shí)現(xiàn)事件驅(qū)動(dòng)在連接數(shù)巨大但活躍連接很少的場(chǎng)景下優(yōu)勢(shì)明顯。這里我想多說(shuō)幾句關(guān)于邊緣觸發(fā)和水平觸發(fā)的區(qū)別因?yàn)檫@是實(shí)際開(kāi)發(fā)中最容易出問(wèn)題的地方。水平觸發(fā)是只要緩沖區(qū)還有數(shù)據(jù)可讀就會(huì)一直通知你邊緣觸發(fā)是只有當(dāng)有新數(shù)據(jù)到達(dá)時(shí)才通知一次。邊緣觸發(fā)模式下你必須一次性把數(shù)據(jù)讀完否則就會(huì)丟數(shù)據(jù)。解決辦法是配合非阻塞IO循環(huán)讀取直到read返回EAGAIN。我記得當(dāng)時(shí)筆試題里給了一段epoll邊緣觸發(fā)的代碼片段讓你判斷為什么活躍連接處理完后還有大量socket未讀取。答案就是緩沖區(qū)數(shù)據(jù)在最后一次read之后還有剩余但因?yàn)闆](méi)有新數(shù)據(jù)到達(dá)邊緣觸發(fā)不再通知于是這些數(shù)據(jù)一直躺在內(nèi)核緩沖里。這種坑是真正寫代碼時(shí)才能發(fā)現(xiàn)的問(wèn)題筆試考了其實(shí)是在幫你提前踩坑。3.3 Linux調(diào)試和性能命令筆試考得不深但工作中天天用Linux相關(guān)的題在筆試中占比不是最高但一旦出現(xiàn)就非常實(shí)用。2015年有一道題是給出一個(gè)進(jìn)程PID問(wèn)用什么命令查看這個(gè)進(jìn)程監(jiān)聽(tīng)的端口。答案是netstat -tlnp或者lsof -p PID | grep LISTEN。還有一道題是系統(tǒng)Load Average過(guò)高問(wèn)排查步驟這就完全是個(gè)開(kāi)放式問(wèn)題了。我的答題套路是自頂向下先看負(fù)載情況uptime再看CPU和內(nèi)存top/free然后用vmstat看上下文切換和等待隊(duì)列再用iostat看磁盤IO最后用perf或者strace定位到具體進(jìn)程和系統(tǒng)調(diào)用。這個(gè)排查順序不是拍腦袋定的而是一種從現(xiàn)象到原因逐步收斂的思路?,F(xiàn)在的筆試題越來(lái)越喜歡這種“場(chǎng)景題”因?yàn)檫@類問(wèn)題的答案能直接反映候選人的實(shí)戰(zhàn)經(jīng)驗(yàn)。如果你只是知道命令的名字不知道什么情況下用哪個(gè)命令回答起來(lái)會(huì)非常散亂。另外gdb的基礎(chǔ)使用在筆試中也出現(xiàn)過(guò)。比如查看core dump文件用什么命令、如何查看當(dāng)前線程的調(diào)用棧。核心答案是gdb ./program core進(jìn)入后thread apply all bt打印所有線程的堆棧?;A(chǔ)平臺(tái)研發(fā)的人不可能不跟crash打交道core文件就是我們破案的關(guān)鍵線索。這個(gè)技能用熟了很多疑難雜癥都能快速定位。4. 數(shù)據(jù)結(jié)構(gòu)與算法解題思路比代碼本身更重要4.1 鏈表和二叉樹基本功考察的重災(zāi)區(qū)算法部分的題目老實(shí)說(shuō)2015年不是特別難但勝在出題角度比較務(wù)實(shí)。鏈表反轉(zhuǎn)、判斷鏈表是否有環(huán)、二叉樹前中后序遍歷、最近公共祖先這些都是標(biāo)配。題目不難難點(diǎn)在于時(shí)間和空間復(fù)雜度有沒(méi)有達(dá)到最優(yōu)。我記得有一道鏈表題要求O(1)空間復(fù)雜度刪除單鏈表中的某個(gè)非尾節(jié)點(diǎn)。常規(guī)思路是找到前驅(qū)節(jié)點(diǎn)再刪除但這樣就不得不遍歷鏈表。標(biāo)準(zhǔn)解法是“偷梁換柱”——把當(dāng)前節(jié)點(diǎn)的值替換成下一個(gè)節(jié)點(diǎn)的值然后刪除下一個(gè)節(jié)點(diǎn)。這個(gè)解法雖然有點(diǎn)“作弊”的味道但完美契合了題目的約束。這類題目告訴你一個(gè)重要道理算法題先看限制條件再想數(shù)據(jù)結(jié)構(gòu)的特殊性質(zhì)。很多時(shí)候不是你想不到思路而是沒(méi)有把題目的約束讀透。二叉樹相關(guān)的題目中非遞歸遍歷是高頻考點(diǎn)因?yàn)樗疾斓牟恢皇恰皶?huì)遞歸”而是你能不能手動(dòng)模擬棧的過(guò)程。筆試時(shí)我建議對(duì)層序遍歷多上點(diǎn)心因?yàn)榛A(chǔ)平臺(tái)開(kāi)發(fā)中序列化和反序列化二叉樹、按層輸出等場(chǎng)景都很常見(jiàn)。有一道題是給定二叉樹的前序遍歷和中序遍歷結(jié)果讓你重建二叉樹。這題背后是分治思想前序遍歷的第一個(gè)節(jié)點(diǎn)是根中序遍歷中根的位置把序列分成左右子樹然后遞歸。理解了分治思想代碼寫起來(lái)就順理成章。4.2 動(dòng)態(tài)規(guī)劃和貪心筆試?yán)锏姆炙畮X動(dòng)態(tài)規(guī)劃在2015年的筆試中也占了一席之地。我記得有一道最長(zhǎng)公共子序列的題屬于經(jīng)典DP入門。難點(diǎn)在于你要能寫出狀態(tài)轉(zhuǎn)移方程并且能說(shuō)出為什么dp[i][j]的定義是這樣。后來(lái)我面試實(shí)習(xí)生時(shí)經(jīng)常發(fā)現(xiàn)一個(gè)現(xiàn)象很多人能把代碼背下來(lái)但問(wèn)為什么dp數(shù)組多開(kāi)一行一列就說(shuō)不清楚了。原因在于沒(méi)有真正理解“空串參與比較”這個(gè)設(shè)計(jì)。多開(kāi)一行一列是為了處理邊界條件讓i或j為0時(shí)dp[i][j]直接等于0不用特判。還有一道編輯距離的題這個(gè)在面試中出現(xiàn)的頻率極高。編輯距離的狀態(tài)轉(zhuǎn)移方程是如果字符相同dp[i][j]dp[i-1][j-1]不同則取插入、刪除、替換三種操作的最小值加1。筆試時(shí)我并不需要寫出完整代碼關(guān)鍵是畫出DP表格的推導(dǎo)過(guò)程。但實(shí)際工作中文本相似度計(jì)算、拼寫糾錯(cuò)、基因序列比對(duì)底層都是編輯距離算法。你把這個(gè)狀態(tài)轉(zhuǎn)移想明白了后來(lái)學(xué)習(xí)更復(fù)雜的最短編輯路徑、diff算法會(huì)輕松很多。貪心算法在筆試題里一般是和排序結(jié)合出現(xiàn)的。比如活動(dòng)選擇問(wèn)題按結(jié)束時(shí)間排序依次選擇下一個(gè)與當(dāng)前不沖突的活動(dòng)。這類題目的陷阱在于想當(dāng)然——面試者容易在看到“全局最優(yōu)”時(shí)認(rèn)為貪心可行但很多題目因?yàn)槿鄙儇澬倪x擇性質(zhì)正確答案是動(dòng)態(tài)規(guī)劃。所以在答貪心題時(shí)至少要能簡(jiǎn)單證明貪心策略的正確性哪怕只是一句話的直覺(jué)每一步選擇局部最優(yōu)且這個(gè)選擇不限制后續(xù)選擇那么最終就是全局最優(yōu)。4.3 海量數(shù)據(jù)處理實(shí)習(xí)生崗也敢考膽子不小2015年的試卷里有一道海量數(shù)據(jù)題給定一個(gè)很大很大的日志文件找出出現(xiàn)頻率最高的前100個(gè)IP。這在當(dāng)時(shí)還是很超前的考點(diǎn)因?yàn)楹A繑?shù)據(jù)處理一般是社招填空題的保留節(jié)目。不過(guò)這題放在基礎(chǔ)平臺(tái)研發(fā)的實(shí)習(xí)生筆試?yán)锊⒉贿`和畢竟那個(gè)崗位干的活就是處理海量數(shù)據(jù)。答題思路要分兩步第一步如果日志文件大到內(nèi)存裝不下需要哈希分片把大文件拆成多個(gè)可以裝進(jìn)內(nèi)存的小文件然后分別統(tǒng)計(jì)每個(gè)小文件的top100第二步對(duì)每個(gè)小文件的top100做外部排序或堆排序合并得到全局top100。如果用哈希分片要注意同一個(gè)IP必須hash到同一個(gè)小文件中否則統(tǒng)計(jì)結(jié)果會(huì)不準(zhǔn)確。還有個(gè)細(xì)節(jié)是哈希函數(shù)的選擇要盡可能使數(shù)據(jù)分布均勻避免某個(gè)小文件仍然過(guò)大。這種題目現(xiàn)在的面試?yán)飵缀醭闪藰?biāo)配但2015年能考出來(lái)說(shuō)明阿里的基礎(chǔ)平臺(tái)團(tuán)隊(duì)對(duì)候選人的要求一直很高。我會(huì)建議準(zhǔn)備此類題目的同學(xué)重點(diǎn)吃透四個(gè)字分而治之。不論是用哈希分片還是字典樹統(tǒng)計(jì)本質(zhì)都是把大問(wèn)題拆成可以獨(dú)立解決的小問(wèn)題最后再合并結(jié)果。5. 分布式系統(tǒng)與開(kāi)放性問(wèn)題沒(méi)有標(biāo)準(zhǔn)答案但看得出工程深度5.1 從CAP理論到一致性問(wèn)題基礎(chǔ)平臺(tái)的底層邏輯分布式系統(tǒng)的題目在2015年其實(shí)是加分項(xiàng)答得好壞直接決定你能不能進(jìn)下一輪面試。CAP理論是起點(diǎn)一致性、可用性、分區(qū)容錯(cuò)性三個(gè)最多滿足兩個(gè)。但光說(shuō)出這個(gè)結(jié)論是不夠的好的答案要能解釋“為什么不能三者兼得”。核心在于網(wǎng)絡(luò)分區(qū)時(shí)如果你選擇保持一致性就必須拒絕部分請(qǐng)求這犧牲了可用性如果你選擇保持可用性多個(gè)分區(qū)各自服務(wù)可能產(chǎn)生數(shù)據(jù)沖突這犧牲了一致性。這里我建議舉一個(gè)實(shí)際的例子。一個(gè)分布式KV存儲(chǔ)比如類似于早期版本的Tair如果某臺(tái)機(jī)器宕機(jī)了副本和其他節(jié)點(diǎn)失去通信。這時(shí)候系統(tǒng)有兩個(gè)選擇一是繼續(xù)對(duì)外提供服務(wù)但數(shù)據(jù)可能是不一致的二是停止服務(wù)等網(wǎng)絡(luò)恢復(fù)再做同步。前者是AP后者是CP。沒(méi)有絕對(duì)的好與壞只看業(yè)務(wù)場(chǎng)景。筆試題里有一道問(wèn)的是“在什么場(chǎng)景下選擇CP什么場(chǎng)景下選擇AP”我的回答是交易支付類選CP因?yàn)閷幙蓵簳r(shí)不可用也不能賬目出錯(cuò)商品瀏覽類選AP因?yàn)檎故旧晕⑴f一點(diǎn)沒(méi)關(guān)系但頁(yè)面不能打不開(kāi)。這種問(wèn)題沒(méi)有標(biāo)準(zhǔn)答案但能看出你有沒(méi)有真實(shí)的設(shè)計(jì)思考。5.2 一致性哈?;A(chǔ)平臺(tái)的經(jīng)典設(shè)計(jì)一致性哈希在2015年的題目里有專門的考察。題目是設(shè)計(jì)一個(gè)分布式緩存系統(tǒng)要求增加節(jié)點(diǎn)或刪除節(jié)點(diǎn)時(shí)盡可能少地影響已有數(shù)據(jù)映射。如果你只回答“對(duì)key取模”那基本就告別復(fù)試了因?yàn)槿∧T诠?jié)點(diǎn)變化時(shí)會(huì)導(dǎo)致絕大多數(shù)key重新映射造成緩存雪崩。一致性哈希的思路是把哈希值空間組織成一個(gè)虛擬的環(huán)每個(gè)節(jié)點(diǎn)映射到環(huán)上數(shù)據(jù)項(xiàng)也通過(guò)哈希映射到環(huán)上順時(shí)針找到的第一個(gè)節(jié)點(diǎn)就是存儲(chǔ)目標(biāo)。當(dāng)節(jié)點(diǎn)變化時(shí)只有該節(jié)點(diǎn)到前一個(gè)節(jié)點(diǎn)之間的數(shù)據(jù)需要遷移。但簡(jiǎn)單的一致性哈希有一個(gè)問(wèn)題節(jié)點(diǎn)數(shù)量少時(shí)哈希環(huán)上的節(jié)點(diǎn)分布可能非常不均勻。解決辦法是引入虛擬節(jié)點(diǎn)把每個(gè)物理節(jié)點(diǎn)復(fù)制成幾百個(gè)虛擬節(jié)點(diǎn)打散到環(huán)上。我在實(shí)際生產(chǎn)環(huán)境里手動(dòng)實(shí)現(xiàn)過(guò)一致性哈希虛擬節(jié)點(diǎn)數(shù)取150到200時(shí)效果比較好。筆試中你如果能畫出環(huán)的示意圖再解釋虛擬節(jié)點(diǎn)的作用這道題基本就拿下了。5.3 開(kāi)放設(shè)計(jì)題與軟技能考核除了技術(shù)知識(shí)點(diǎn)2015年的試卷還包含一些開(kāi)放性的設(shè)計(jì)題。比如讓你設(shè)計(jì)一個(gè)短網(wǎng)址服務(wù)或者讓你解釋“如何保證消息只被消費(fèi)一次”。這些題表面上沒(méi)有標(biāo)準(zhǔn)答案但考察的核心是兩方面需求澄清能力和邊界把控能力。以短網(wǎng)址服務(wù)為例如果你一上來(lái)就擺出Redis方案和MySQL方案說(shuō)明你缺少第二步——為什么不先問(wèn)清楚QPS是多少、數(shù)據(jù)量多大、需不需要自定義別名、過(guò)期時(shí)間是一年還是永久。大廠的筆試開(kāi)放性題目除非你完全接觸過(guò)該類系統(tǒng)否則很可能答偏。我的建議是答題前先把關(guān)鍵問(wèn)題列出來(lái)即使你沒(méi)有機(jī)會(huì)得到回答也要在答案中主動(dòng)寫出“這里我假設(shè)系統(tǒng)的QPS是xxx量級(jí)在這個(gè)假設(shè)下我的方案是……”。這種帶著假設(shè)和前提的方案陳述方式本身就是工程思維的一部分。還有一類關(guān)于“技術(shù)選型”的題目比如讓你對(duì)比Redis和MySQL的使用場(chǎng)景。答題時(shí)要避免籠統(tǒng)地說(shuō)“Redis快所以用它MySQL穩(wěn)定所以用它”而是要從數(shù)據(jù)模型、訪問(wèn)模式、持久化要求、一致性要求四個(gè)維度拆開(kāi)來(lái)看。把比較維度列清楚本身就證明你做過(guò)技術(shù)選型的功課。6. 備考建議和答題策略一份踩過(guò)坑之后整理的實(shí)戰(zhàn)清單6.1 時(shí)間投入與知識(shí)優(yōu)先級(jí)如果現(xiàn)在有同學(xué)要準(zhǔn)備類似的筆試我給你一個(gè)比較實(shí)用的時(shí)間分配建議。操作系統(tǒng)、網(wǎng)絡(luò)、C/C這幾塊占據(jù)六成以上的時(shí)間分布式和數(shù)據(jù)結(jié)構(gòu)的進(jìn)階題占三成剩下的時(shí)間用來(lái)了解最新的技術(shù)動(dòng)態(tài)。這不是拍腦袋定的比例而是我統(tǒng)計(jì)了這些年校招筆試題型的分布得出的結(jié)論?;A(chǔ)平臺(tái)研發(fā)崗尤其如此——你再怎么刷LeetCode如果fork的語(yǔ)義理解不透徹TCP揮手狀態(tài)圖畫不出來(lái)那些算法題拿到的分?jǐn)?shù)也補(bǔ)不上基礎(chǔ)題的窟窿。具體來(lái)說(shuō)操作系統(tǒng)要重點(diǎn)掌握進(jìn)程線程模型、上下文切換的代價(jià)、虛擬內(nèi)存與物理內(nèi)存的映射、用戶態(tài)與內(nèi)核態(tài)的切換條件、鎖的實(shí)現(xiàn)原理自旋鎖、互斥鎖、讀寫鎖。網(wǎng)絡(luò)方面要重點(diǎn)掌握TCP狀態(tài)機(jī)尤其是TIME_WAIT和CLOSE_WAIT、TCP擁塞控制的數(shù)據(jù)包行為、UDP與TCP選型權(quán)衡、HTTP/HTTPS的握手流程。C/C方面除了語(yǔ)法本身記得關(guān)注內(nèi)存布局、編譯器優(yōu)化對(duì)代碼行為的影響、RAII與智能指針的實(shí)現(xiàn)原理。這些知識(shí)點(diǎn)橫向覆蓋了筆試70%以上的出題范圍。6.2 答題節(jié)奏與失分陷阱2015年那次筆試我最大的教訓(xùn)是一道題卡太久了。那道題是一個(gè)復(fù)雜的狀態(tài)機(jī)分析題我花了將近二十分鐘去推演結(jié)果后面幾道本來(lái)可以輕松拿分的網(wǎng)絡(luò)題沒(méi)時(shí)間答仔細(xì)。后來(lái)總結(jié)出的答題順序是第一遍快速掃完全卷把有把握的題先做掉標(biāo)記出不確定的題最后再回頭啃硬骨頭。這個(gè)方法聽(tīng)起來(lái)很老套但真的很管用因?yàn)榛A(chǔ)平臺(tái)筆試題量通常偏大時(shí)間就是分。失分陷阱主要集中在幾個(gè)地方運(yùn)算符優(yōu)先級(jí)寫錯(cuò)、忽略int溢出、多線程共享變量未加鎖、TCP狀態(tài)遷移沒(méi)考慮超時(shí)重傳、動(dòng)態(tài)規(guī)劃的狀態(tài)定義不合理。筆試的客觀題往往是“看起來(lái)都會(huì)對(duì)答案錯(cuò)一半”。我建議刷題時(shí)準(zhǔn)備一個(gè)錯(cuò)題本但不要只記錄正確答案要把自己當(dāng)時(shí)錯(cuò)誤的思考路徑寫下來(lái)。比如我當(dāng)年經(jīng)常把“進(jìn)程上下文切換”和“線程上下文切換”的開(kāi)銷差異搞混后來(lái)我在錯(cuò)題本上寫了一句話線程切換雖然不切換地址空間但cache和TLB依然可能失效所以并不是“零開(kāi)銷”。這么一寫記憶就深刻很多。6.3 從筆試到面試如何把試卷上的內(nèi)容變成面試素材筆試結(jié)束不等于復(fù)習(xí)結(jié)束很多筆試題目其實(shí)會(huì)在面試中被追問(wèn)得更深。比如筆試題考了epoll的兩種觸發(fā)模式面試官可能會(huì)追問(wèn)你在實(shí)際項(xiàng)目中用過(guò)epoll嗎當(dāng)時(shí)怎么處理邊緣觸發(fā)下的數(shù)據(jù)半包問(wèn)題所以筆試后千萬(wàn)不要對(duì)完答案就扔一邊而是要把每一道不確定的題變成一個(gè)學(xué)習(xí)入口順著知識(shí)點(diǎn)一路擴(kuò)展下去。我自己的做法是給每道題做一個(gè)“一句話總結(jié)”。比如“LRU可以用雙向鏈表加哈希表實(shí)現(xiàn)關(guān)鍵是get和put的時(shí)間復(fù)雜度都是O(1)”再比如“TIME_WAIT不是bug是TCP可靠關(guān)閉的必要代價(jià)但可以通過(guò)連接復(fù)用和調(diào)參緩解”。這些一句話總結(jié)后來(lái)都成了我面試時(shí)的口頭表達(dá)素材。面試官通常會(huì)喜歡這種簡(jiǎn)潔有力的概括因?yàn)樗f(shuō)明你真的理解了而不是背了很長(zhǎng)的PPT。另外如果有條件的話筆試之后可以找一個(gè)同樣準(zhǔn)備面試的同學(xué)互相提問(wèn)。兩個(gè)人輪流講一個(gè)知識(shí)點(diǎn)講的時(shí)候要注意能不能讓對(duì)方聽(tīng)懂。如果對(duì)方聽(tīng)完之后能用自己的話復(fù)述出來(lái)說(shuō)明你講清楚了如果對(duì)方滿臉疑惑你需要回去再看看。把復(fù)雜的東西講簡(jiǎn)單是基礎(chǔ)平臺(tái)研發(fā)工程師非常重要的能力因?yàn)檫@類崗位經(jīng)常需要寫技術(shù)方案、做code review、向團(tuán)隊(duì)解釋系統(tǒng)設(shè)計(jì)表達(dá)本身就是工作的一部分。最后說(shuō)幾句實(shí)在的2015年那場(chǎng)筆試過(guò)去很多年了但每次回頭看都會(huì)發(fā)現(xiàn)基礎(chǔ)平臺(tái)研發(fā)崗考察的核心一直沒(méi)有變操作系統(tǒng)、網(wǎng)絡(luò)、語(yǔ)言底層、算法與數(shù)據(jù)結(jié)構(gòu)、系統(tǒng)設(shè)計(jì)思維。技術(shù)棧會(huì)更新框架會(huì)替換但底層原理的穩(wěn)定性驚人地高?,F(xiàn)在網(wǎng)上能找到的面試題資源比當(dāng)年豐富太多但我反而覺(jué)得信息過(guò)載容易讓人陷入刷題的舒適區(qū)忘了回到根本。我個(gè)人最推薦的一種準(zhǔn)備方式是自己動(dòng)手寫幾個(gè)小項(xiàng)目來(lái)驗(yàn)證知識(shí)點(diǎn)而不是只看書和刷題。比如要實(shí)現(xiàn)一個(gè)簡(jiǎn)單的線程池你自然會(huì)去思考任務(wù)隊(duì)列怎么加鎖、線程數(shù)量怎么定、空閑線程怎么回收要實(shí)現(xiàn)一個(gè)epoll高并發(fā)回聲服務(wù)器你自然會(huì)去理解水平觸發(fā)和邊緣觸發(fā)的差別要實(shí)現(xiàn)一個(gè)LRU緩存你自然會(huì)去設(shè)計(jì)哈希表和雙向鏈表的聯(lián)動(dòng)。這些項(xiàng)目不需要多大但“親手做過(guò)”和“看過(guò)答案”之間的差距在筆試和面試中會(huì)體現(xiàn)得非常明顯?;A(chǔ)平臺(tái)研發(fā)這份工作終究是一個(gè)比拼內(nèi)功的方向內(nèi)功到位的人遲早會(huì)跑出來(lái)。