網(wǎng)軟件工程師筆試復(fù)盤:基礎(chǔ)能力與場景結(jié)合)
前兩天整理移動硬盤翻到了當(dāng)年參加小鵬汽車2019春招車聯(lián)網(wǎng)軟件工程師筆試時留下的復(fù)盤文檔。那會兒正值春招補錄崗位掛靠在互聯(lián)網(wǎng)中心名字叫“車聯(lián)網(wǎng)軟件工程師”但看了筆試內(nèi)容就會發(fā)現(xiàn)它既沒有去考CAN總線解析也沒有考高精地圖和SLAM更多還是集中在軟件工程師的基本盤上。今天把這份筆試題的考察邏輯重新捋一遍也是給正在準(zhǔn)備車企軟件崗面試的朋友一個參考車聯(lián)網(wǎng)方向筆試題并不是什么“玄學(xué)”所有題目都指向同一個問題——你能不能在一個資源不算寬裕的車載環(huán)境里寫出穩(wěn)定、可維護、能上線的軟件。這份筆試如果落在今天來看題型其實不算特別新穎C/C、數(shù)據(jù)結(jié)構(gòu)、操作系統(tǒng)、網(wǎng)絡(luò)協(xié)議、算法編程一圈下來看起來和互聯(lián)網(wǎng)后端校招差別不大。但仔細做進去會發(fā)現(xiàn)它選的知識點非常克制偏向嵌入式場景和通信場景的交叉地帶。這也提醒我們準(zhǔn)備車聯(lián)網(wǎng)軟件工程師不只是刷LeetCode還要把Linux、網(wǎng)絡(luò)協(xié)議、系統(tǒng)資源這些“傳統(tǒng)地盤”撿起來。文章后面我會按當(dāng)年筆試涉及的知識塊逐一展開也會把我重做了一遍后想明白的細節(jié)寫出來。1. 互聯(lián)網(wǎng)中心招的“車聯(lián)網(wǎng)軟件工程師”和嵌入式開發(fā)有什么不同1.1 同一個“車聯(lián)網(wǎng)”標(biāo)簽不同崗位的權(quán)重差別很大很多人在投“車聯(lián)網(wǎng)軟件工程師”時會下意識把它等同于嵌入式軟件開發(fā)。實際上車企的軟件崗位分得很細車聯(lián)網(wǎng)方向也存在明顯的業(yè)務(wù)邊界。以當(dāng)年小鵬汽車互聯(lián)網(wǎng)中心掛出的崗位來說它要解決的更多是“車端如何和云端互通”“用戶如何通過手機與車輛交互”“車輛運行數(shù)據(jù)如何穩(wěn)定上報”這些問題。換句話說這個崗位處在傳統(tǒng)汽車電子和移動互聯(lián)網(wǎng)的交叉點上。這種定位直接影響了筆試選題。純嵌入式崗位大概率會考寄存器配置、中斷處理、I2C/SPI時序甚至讓你分析某塊SoC的啟動流程而互聯(lián)網(wǎng)中心的車聯(lián)網(wǎng)軟件工程師崗位更關(guān)心你能不能寫對網(wǎng)絡(luò)通信模塊、能不能處理并發(fā)上報、能不能在Linux環(huán)境下定位一個崩潰問題。所以筆試?yán)锍霈F(xiàn)大量C/C和Linux基礎(chǔ)但很少出現(xiàn)芯片級驅(qū)動題目也就不奇怪了。1.2 從崗位能力畫像反推筆試的底層邏輯如果仔細拆解崗位要承擔(dān)的工作會得到下面這張能力畫像能力維度為什么要考在筆試?yán)锏膶?yīng)題型C/C語言車機端應(yīng)用、T-Box通信模塊大量使用C/C指針、內(nèi)存、類相關(guān)選擇題和改錯題數(shù)據(jù)結(jié)構(gòu)與算法軟件工程的基本功決定代碼質(zhì)量鏈表、二叉樹、棧隊列的手寫與復(fù)雜度分析操作系統(tǒng)車機系統(tǒng)需要應(yīng)對多任務(wù)調(diào)度、資源緊張進程線程、死鎖、信號量、內(nèi)存管理計算機網(wǎng)絡(luò)車聯(lián)網(wǎng)的本質(zhì)是車與云端、手機之間的通信TCP/UDP、HTTP、MQTT概念題Linux操作車機系統(tǒng)普遍基于Linux/Android常用命令、簡單腳本編寫這個畫像并不要求每一塊都達到專家水平但要求每一塊都不能有明顯短板。筆試的篩選邏輯也很有意思它不通過偏題怪題來制造門檻而是通過“基礎(chǔ)題密度”來淘汰訓(xùn)練不足的人。很多人覺得車聯(lián)網(wǎng)工程師崗位很酷考前拼命去補自動駕駛、激光雷達、高精地圖結(jié)果一上筆試發(fā)現(xiàn)連struct內(nèi)存對齊都能做錯這就很可惜。1.3 “互聯(lián)網(wǎng)中心”這五個字透露出的關(guān)鍵信號崗位名稱里帶“互聯(lián)網(wǎng)中心”意味著這個團隊的工作模式更接近互聯(lián)網(wǎng)團隊有較快的迭代節(jié)奏講究代碼評審和工程質(zhì)量也會關(guān)注數(shù)據(jù)上報、服務(wù)端接口、App聯(lián)動這類偏上層的事情。筆試?yán)锍霈F(xiàn)“車輛狀態(tài)上報”“遠程控制指令怎么設(shè)計”這類題目其實就是在考察你有沒有互聯(lián)網(wǎng)軟件的思維習(xí)慣。我當(dāng)時還注意到一個細節(jié)題目里對內(nèi)存和性能的關(guān)注明顯比純互聯(lián)網(wǎng)后端崗位要高。原因是車機的硬件資源遠不如服務(wù)器那么充裕一行不規(guī)范的字符串處理代碼就可能在某個低配車機上引發(fā)卡頓甚至崩潰。所以筆試考內(nèi)存對齊、考動態(tài)內(nèi)存管理不是故意刁難而是這個崗位每天都會遇到這些事。2. 從崗位描述反推筆試范圍基礎(chǔ)能力與車聯(lián)網(wǎng)場景的權(quán)重2.1 崗位描述里那些關(guān)鍵字的優(yōu)先級當(dāng)年招聘信息里的關(guān)鍵字我現(xiàn)在還記得大概車聯(lián)網(wǎng)、軟件工程師、嵌入式、Linux、通信。把這些詞展開就是筆試考察范圍。校招筆試不會考你上一份實習(xí)做了什么它更傾向考察“你這個人有沒有獨立成長的基礎(chǔ)”。所以從崗位描述能直接推出好幾個可能考到的模塊如果寫了Linux大概率會考常用命令、進程概念、文件系統(tǒng)如果寫了通信大概率會考TCP/IP協(xié)議棧運氣好會碰到MQTT這類物聯(lián)網(wǎng)協(xié)議如果寫了嵌入式C語言的優(yōu)先級會直接拉到最高指針、內(nèi)存、結(jié)構(gòu)體這些跑不掉如果寫了軟件工程師數(shù)據(jù)結(jié)構(gòu)與算法基本是必考題。注意這里的權(quán)重分配基礎(chǔ)能力是主體車聯(lián)網(wǎng)場景是包裝。就像互聯(lián)網(wǎng)后端筆試會把題目包裝成“訂單系統(tǒng)”一樣車聯(lián)網(wǎng)筆試也喜歡把題目包裝成“車輛狀態(tài)上報”“遠程升級任務(wù)”。你看穿這點之后復(fù)習(xí)就不會跑偏。2.2 必考、??肌⒓臃挚键c的優(yōu)先級表結(jié)合當(dāng)年實際筆試的體感如果把考點排個優(yōu)先級大概是這樣的優(yōu)先級考點常見出題方式必考C語言基礎(chǔ)、指針、內(nèi)存選擇題、代碼找錯必考數(shù)據(jù)結(jié)構(gòu)鏈表、棧、隊列、樹手寫代碼或復(fù)雜度分析必考操作系統(tǒng)進程線程、同步、死鎖概念選擇題、問答??糒inux命令、shell腳本命令填空、場景腳本常考網(wǎng)絡(luò)協(xié)議TCP/UDP、HTTP問答、流程描述??妓惴}字符串、數(shù)組、簡單動態(tài)規(guī)劃在線編程題加分MQTT、JSON/Protobuf、OTA流程場景問答你可以發(fā)現(xiàn)車聯(lián)網(wǎng)特有的知識點通常不會單獨出一道大題而是作為背景嵌入到網(wǎng)絡(luò)或操作系統(tǒng)題目里。例如“車輛在弱網(wǎng)環(huán)境下上報位置應(yīng)該選TCP還是UDP”表面考網(wǎng)絡(luò)實際考的是你對可靠性和實時性的權(quán)衡。這種題目如果只背概念而不理解場景很容易答偏。2.3 為什么2019年春招的筆試會更偏重基礎(chǔ)那一年春招的節(jié)奏比秋招快留給筆試的時間窗口短面試官也需要通過一場筆試快速篩選出值得進入后續(xù)流程的人。所以筆試題目不會出得太難更不會為了一個冷門知識點讓大多數(shù)人交白卷。它的目的是把“基礎(chǔ)扎實、具備計算機系統(tǒng)觀”的人挑出來而不是找“百科全書式”的人。這也意味著備考重點應(yīng)放在高頻考點上。如果你花大量時間去背車聯(lián)網(wǎng)行業(yè)分析、研究每一個OEM的遠程升級方案反而可能錯過最核心的得分點。把基礎(chǔ)題練到肌肉記憶再去拓展車聯(lián)網(wǎng)場景題才是穩(wěn)妥的路。3. 基本盤拆解C/C、數(shù)據(jù)結(jié)構(gòu)、操作系統(tǒng)在筆試中的考法3.1 C/C的送分題與爭奪題C/C是當(dāng)年筆試?yán)锓至孔钪氐囊粔K。它考察的難點不在語法本身而在你是否真正理解內(nèi)存布局和對象生命周期。隨便舉幾個高頻點指針和引用的區(qū)別、const在C和C中的不同含義、static變量和全局變量的區(qū)別、結(jié)構(gòu)體內(nèi)存對齊、虛函數(shù)底層實現(xiàn)。這些知識點非?;A(chǔ)但能全答對的人并沒有想象中那么多。以結(jié)構(gòu)體內(nèi)存對齊為例題目往往問一個結(jié)構(gòu)體占多少字節(jié)struct Node { char a; int b; char c; };如果在32位系統(tǒng)上默認4字節(jié)對齊答案是12字節(jié)而不是6字節(jié)。很多人只算字段寬度忘了對齊填充。這個考點幾乎年年出現(xiàn)因為它在嵌入式通信中非常關(guān)鍵結(jié)構(gòu)體經(jīng)常用來解析CAN報文或者網(wǎng)絡(luò)幀如果對齊規(guī)則不清楚很容易踩到協(xié)議解析的坑。另一個值得寫的是淺拷貝和深拷貝。車聯(lián)網(wǎng)代碼里經(jīng)常會出現(xiàn)車輛信息結(jié)構(gòu)體的賦值如果只做淺拷貝動態(tài)分配的字符串就會被兩個對象同時持有析構(gòu)時就會double free。筆試?yán)锟赡懿粫屇銓懲暾a但會給出一個類讓你指出拷貝構(gòu)造和賦值運算符有什么問題。這類題邏輯不復(fù)雜前提是你平時真寫過、真崩過。C里還有個容易考的細節(jié)為什么構(gòu)造函數(shù)不能是虛函數(shù)而析構(gòu)函數(shù)推薦寫成虛函數(shù)。在車輛通信模塊中上層往往定義抽象接口下層有多種協(xié)議實現(xiàn)比如4G、Wi-Fi、藍牙。如果基類析構(gòu)函數(shù)不是虛函數(shù)delete基類指針時子類資源就無法釋放。這個點既考語言機制也考工程意識答到“資源釋放”層面通常就能拿高分。3.2 數(shù)據(jù)結(jié)構(gòu)題不只考手寫代碼數(shù)據(jù)結(jié)構(gòu)的選擇題通常是送分題比如棧和隊列的異同、哈希沖突的幾種解決方式、二叉搜索樹的查找復(fù)雜度、快排最壞時間復(fù)雜度。這部分只要刷過一遍基礎(chǔ)題基本不會丟分。容易拉開差距的還是手寫代碼題。鏈表反轉(zhuǎn)出鏡率很高。它看起來簡單但要在五分鐘內(nèi)寫對手不能生。參考答案思路如下struct ListNode* reverseList(struct ListNode* head) { struct ListNode *prev NULL, *curr head; while (curr) { struct ListNode *next curr-next; curr-next prev; prev curr; curr next; } return prev; }除了鏈表二叉樹層序遍歷、用兩個棧實現(xiàn)隊列也稱得上高頻。這些題不考技巧純粹考你平時有沒有動手寫過。如果沒有在筆試現(xiàn)場邊想邊寫非常容易卡殼。另一個值得注意的考點是復(fù)雜度分析。有些題目不要求你寫完整代碼但會問你“將哈希表改為紅黑樹后查找復(fù)雜度從O(1)變成多少”這需要你對數(shù)據(jù)結(jié)構(gòu)的底層實現(xiàn)有基本感知。3.3 操作系統(tǒng)與Linux嵌入式環(huán)境下的實操感操作系統(tǒng)題很少考特別抽象的理論更多是問進程和線程的區(qū)別、死鎖的四個必要條件、信號量和互斥鎖的使用場景。如果你有過多線程編程經(jīng)驗答起來會輕松很多。車機場景里多個應(yīng)用要同時訪問定位模塊、網(wǎng)絡(luò)模塊這里就是典型的同步與資源管理問題。當(dāng)年的題目里我最深的一道是“多個線程同時往一個日志緩沖區(qū)寫數(shù)據(jù)如何保證不互相覆蓋”。這個問題表面考線程安全實際考你對鎖的理解。正確的回答是先想到互斥鎖或自旋鎖然后再補充一句“鎖的粒度盡量小避免日志寫入過程中長時間持有鎖導(dǎo)致其他線程卡頓”。如果還能想到用無鎖環(huán)形緩沖區(qū)那就是加分中的加分了。Linux方面命令題是性價比最高的部分。下面這幾條建議你考前閉眼都能寫出來用top或free查看系統(tǒng)負載和內(nèi)存占用用ps -ef | grep 進程名查找進程PID用netstat -tunlp查看端口和網(wǎng)絡(luò)連接用find /var/log -name *.log | xargs grep xxx在日志目錄里查關(guān)鍵詞用tail -f實時跟蹤日志用kill -9 PID強制結(jié)束進程。筆試?yán)锟赡軙屇銓懸粋€簡單腳本比如“刪除一周前的日志文件”。很簡單#!/bin/bash find /var/log/vehicle -name *.log -mtime 7 -exec rm -f {} \;這其實也考察了find條件的組合能力。如果你只會ls和cd到這就露餡了。Linux不是一天練成的但常用命令突擊一兩天就能覆蓋大部分考點投入產(chǎn)出比很高。另一個常見問法是“如何查看某個進程的CPU和內(nèi)存占用”很多人會答ps但top -p PID才是更有針對性的答案面試官會更認可這種精確操作。4. 提分項拆解網(wǎng)絡(luò)通信與算法編程題怎么準(zhǔn)備4.1 TCP/UDP和車聯(lián)網(wǎng)場景的結(jié)合網(wǎng)絡(luò)題是車聯(lián)網(wǎng)軟件工程師筆試?yán)镒罹邖徫惶厣牟糠忠驗樗苷嬲齾^(qū)分“背過八股文”和“理解場景”?;A(chǔ)題大家都準(zhǔn)備過三次握手為什么不是兩次、四次揮手為什么需要TIME_WAIT、UDP和TCP各自適合什么場景。但真正有區(qū)分度的是把它放到車聯(lián)網(wǎng)語境里問。舉個例子題目可能會說車輛在行駛過程中需要每10秒上報一次GPS位置請問選用TCP、UDP還是MQTT更合適很多人一看到“上報”就寫UDP理由是實時性好。但如果你了解實際工程會發(fā)現(xiàn)位置上報也需要考慮數(shù)據(jù)完整性因為服務(wù)器端要用這些位置繪制軌跡、分析駕駛行為。完全丟包會導(dǎo)致軌跡斷裂。所以工程上常用MQTT QoS1既保證至少一次到達又比裸TCP連接重建的開銷小。再比如遠程控車指令這種操作要求可靠性和安全性。車門解鎖指令一旦丟失用戶可能被鎖在車外。所以它必須走加密的可靠通道HTTP/HTTPS或MQTT QoS2都可行。筆試答題時如果能寫出“可靠指令用TCP/HTTPS高頻低價值數(shù)據(jù)用UDP或MQTT QoS0”這種層次感閱卷人一眼就能看出你不只是背了協(xié)議區(qū)別。還包括HTTP和HTTPS的區(qū)別這個常規(guī)考點在車聯(lián)網(wǎng)里也有延伸車機請求云端接口證書校驗失敗應(yīng)該怎么辦如果直接忽略證書繼續(xù)請求就可能被中間人攻擊。好的回答應(yīng)該是“先判斷失敗原因再決定是否走安全策略涉及車輛控制類接口必須嚴(yán)格校驗”。這種安全意識在車聯(lián)網(wǎng)崗位里非??粗?。4.2 算法題的難度和取舍算法編程題通常是整個筆試?yán)镒詈臅r間的。當(dāng)年的題目難度基本在LeetCode中等偏下不太會出現(xiàn)競賽題。常見方向包括字符串處理、數(shù)組操作、鏈表、二分查找、簡單動態(tài)規(guī)劃。例如“合并兩個有序鏈表”“最長無重復(fù)子串”“兩數(shù)之和”這類。寫題時有個很重要的取舍拿到題目先不要急著寫代碼先跟出題人給的例子過一遍流程確認輸入范圍和邊界。比如合并兩個有序鏈表它考察的不只是邏輯還包括“有沒有處理空鏈表”“有沒有理清dummy節(jié)點”這些小節(jié)。代碼可以寫得樸素但一定要完整struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) { struct ListNode dummy; struct ListNode* tail dummy; dummy.next NULL; while (l1 l2) { if (l1-val l2-val) { tail-next l1; l1 l1-next; } else { tail-next l2; l2 l2-next; } tail tail-next; } tail-next l1 ? l1 : l2; return dummy.next; }如果運氣好碰到“最長無重復(fù)子串”可以用滑動窗口解決。關(guān)鍵是想清楚窗口什么時候收縮什么時候更新答案。這類題只要保持刷題手感臨時上手不會太難。還有一個方向也值得留意排序類題目。比如給一組車輛狀態(tài)記錄按時間戳排序你會優(yōu)先寫快速排序還是直接調(diào)用系統(tǒng)庫工程上當(dāng)然是調(diào)用系統(tǒng)庫更穩(wěn)妥但筆試?yán)锶绻鞔_要求“手寫排序”就必須能在紙上寫對partition過程。4.3 答題規(guī)范與調(diào)試技巧在線筆試平臺通常沒有代碼提示也不提供完整編譯調(diào)試環(huán)境。很多人寫完代碼后沒有測試用例的意識丟分常常不是思路不對而是“沒考慮到空輸入”。我的建議是寫完之后至少在腦子里跑三組用例——正常輸入、空輸入、單元素輸入。如果平臺支持本地編譯就先把代碼在本地跑一遍再貼上去穩(wěn)得多。另外注意輸入輸出的格式。??秃唾惔a這類平臺經(jīng)常要求你處理多行輸入如果scanf或cin用錯代碼邏輯再正確也可能超時或者讀不到數(shù)據(jù)。這些都是細節(jié)但細節(jié)決定筆試能不能進面。還有一個很實用的習(xí)慣在代碼開頭寫清楚思路注釋哪怕只有兩三行。如果線上判題沒給滿分后續(xù)人工復(fù)核時看到你在注釋里寫了“用快慢指針找中點再用歸并排序”也能給出印象分。5. 90分鐘作答的節(jié)奏控制與真實踩坑復(fù)盤5.1 我當(dāng)年的時間分配方案當(dāng)年筆試時長是90分鐘題目類型包括選擇題、填空題、簡答題、編程題。很多人到最后沒有做完不是題目量太大而是沒有控制節(jié)奏。我自己的時間分配習(xí)慣是這樣的先快速瀏覽整張試卷判斷題量比例接著用10到15分鐘解決所有選擇題和填空題給編程題留出40分鐘再用剩余20分鐘處理簡答題和檢查。選擇題和填空題是最容易拿分但最容易被拖時間的部分。有些C語言題很刁鉆比如“有符號和無符號比較”“整型提升”如果你不確定不要戀戰(zhàn)先做一個標(biāo)記回頭再來看。編程題必須留足時間因為一旦寫不完幾乎拿不到分?jǐn)?shù)。簡答題不要寫太長按照“定義、原因、場景、做法”四段式組織每條控制在三四行既清楚又不浪費時間。5.2 那些看著會做卻丟分的瞬間復(fù)盤當(dāng)時答題卡我發(fā)現(xiàn)最可惜的丟分點不是不會做而是“會做但沒寫全”。比如某道簡答題問“為什么車機遠程升級要設(shè)計多個版本回退機制”我光寫了OTA升級失敗會導(dǎo)致設(shè)備變磚但沒有展開說明版本校驗、A/B分區(qū)、升級包簽名這些工程細節(jié)。閱卷人想看的不是一句口號而是你真的了解一個軟件系統(tǒng)在真實設(shè)備上會遇到的邊界情況。編程題也踩過類似坑。我記得有一道題要求“輸出合并后的鏈表節(jié)點值”我寫完合并邏輯后忘記逐節(jié)點輸出直接輸出了頭節(jié)點指針平臺判題必然報錯。這種錯誤不是能力問題而是考場狀態(tài)下習(xí)慣性忽視“輸出格式”這四個字。還有一次是寫代碼之前沒初始化指針本地編譯器開啟了嚴(yán)格警告所以能發(fā)現(xiàn)但線上平臺可能直接編譯失敗。5.3 重新復(fù)盤后的心得整理完這份復(fù)盤我最大的感受是車聯(lián)網(wǎng)軟件工程師筆試的核心不是“卷難度”而是“考穩(wěn)定”。它希望篩出來的是能在有限時間內(nèi)把基礎(chǔ)題做對、把關(guān)鍵場景想清楚、把代碼寫得不出邊界問題的人。如果讓我重新準(zhǔn)備一次我會把復(fù)習(xí)順序調(diào)整為先快速刷一遍C語言和數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)題確保不犯低級錯誤再花兩三天把Linux命令和shell腳本練熟接著把TCP/UDP、MQTT、HTTP這些協(xié)議在車聯(lián)網(wǎng)場景里的應(yīng)用想透最后才是刷算法題。這個順序不是按分?jǐn)?shù)占比排的而是按“投入產(chǎn)出比”排的——基礎(chǔ)題和命令題背了就有分算法題則依賴長期積累沖刺階段性價比相對低。我還想特別提一點面試官很看重“排查問題”的思維方式。筆試?yán)锶绻霈F(xiàn)“車輛偶發(fā)不上報數(shù)據(jù)如何排查”這樣的場景題不要一上來就寫“我是后端工程師不擅長”。把鏈路拆成車端采集、網(wǎng)絡(luò)傳輸、云端接收三段每段列出可能的故障點和排查命令比如車端看進程是否存活、網(wǎng)絡(luò)層看TCP連接狀態(tài)、云端看日志和數(shù)據(jù)庫寫入。這種結(jié)構(gòu)化表達即使不是標(biāo)準(zhǔn)答案也能展現(xiàn)出你的系統(tǒng)思維。最后再分享一個簡單但有用的技巧筆試前把常見的結(jié)構(gòu)體對齊、三次握手、進程切換、鏈表反轉(zhuǎn)、滑動窗口這五類題各默寫一遍。字不用多關(guān)鍵是手要熟。很多時候你感覺自己“會”但真正動筆才發(fā)現(xiàn)缺了一個dummy節(jié)點。這份2019春招的筆試題已經(jīng)過去好幾年了但這類崗位要的東西其實一直沒變基礎(chǔ)扎實、懂場景、能落地。如果你正在準(zhǔn)備類似的車聯(lián)網(wǎng)軟件工程師崗位不妨把這份復(fù)盤當(dāng)作一份最小檢查清單逐項確認一下自己是否真的準(zhǔn)備好了。