从过完年到现在,我前前后后面了大概七八家做嵌入式的公司,从消费电子到工业控制都有,Offer拿了两三个,也踩了不少坑。这篇文章算是我对自己这段时间面试经历的一次梳理,把那些反复被问到、或者说真正能拉开差距的知识点整理出来。如果你想投嵌入式软件工程师、嵌入式Linux开发这类岗位,这篇文章应该能帮你省不少事。我尽量不讲废话,全部是实打实会考、会问的东西。
1. 面试前的技术栈梳理
1.1 嵌入式这个方向到底在考什么
先说一个很多人容易搞错的事情:嵌入式面试不像后端那样狂刷LeetCode,也不像前端那样抠框架源码,它考的是"底层功底 + 项目实战 + 调试能力"这三件事。你可以不会Redux,但你必须知道寄存器是怎么映射的;你可以不背HashMap源码,但你必须说清楚中断上下文和进程上下文的区别。
从我这几个月的面试经验来看,面试官的考察路径基本是固定的:先问C语言基础,确认你有扎实的编码功底;然后问操作系统和Linux,确认你能处理多任务和资源管理;接着问ARM体系结构和硬件接口,确认你写的代码是真能跑在板子上的;最后拿着你的项目往死里深挖,看你是真做过还是背了别人的项目。这是一条完整的链路,缺一环都会被问穿。
还有一个很多人忽略的点:嵌入式面试非常看重"调试思维"。面试官经常会抛出一个现象,比如"板子启动后串口没有输出",然后让你现场分析可能的原因。这种题没有标准答案,考的就是你遇到问题时的排查思路。
1.2 面试之前怎么快速摸底
我强烈建议你在投简历之前,先对着下面这份清单自查一遍,每项都能说出个一二三再去面试:
- C语言:指针、结构体、内存管理、链表、回调函数,能不能手写?
- 操作系统:进程线程区别、调度算法、同步互斥、死锁条件,能不能讲透?
- Linux:常用命令、Makefile、交叉编译、驱动开发流程、设备树,有没有实际用过?
- 硬件:GPIO、UART、SPI、I2C、PWM、ADC,原理和时序能不能画出来?
- ARM:Cortex-M和Cortex-A的区别、启动流程、中断控制器、存储器映射,心里有没有数?
- 项目:你写在简历上的每个项目,从总体架构到关键函数实现,能不能经得起连珠炮式的追问?
如果你现在看到这份清单觉得心虚,那我建议先花一到两周针对性补一补。面试不需要你每个方向都是专家,但基础方向绝对不能有明显的短板。
2. 嵌入式C语言与底层原理考点
2.1 指针和内存是永远绕不开的坎
C语言是嵌入式的母语,而指针和内存管理是C语言里最让面试官上头的两个话题。我统计了一下,几乎每场面试都有"const关键字修饰指针有几种写法"这种送分题,但真正能完整答出来的人不多。
const int *p; // p指向的内容不可变,p本身可变 int * const p; // p本身不可变,指向的内容可变 const int * const p; // 内容和指针都不可变这个题目看着简单,但如果你能主动延伸到"嵌入式里const变量放在哪个段""为什么const变量能节省RAM"这种层次,面试官对你的印象会立刻不一样。const修饰的变量通常存放在只读数据段(.rodata),在MCU上往往被链接到Flash地址空间,所以它不占用宝贵的RAM,这是嵌入式里一个很实用的优化点。
内存管理也是最容易被追问的话题。堆和栈的区别是基础,但面试官真正想听的是你知不知道嵌入式里栈溢出怎么排查、堆碎片怎么避免、动态分配在MCU上为什么是禁忌。我在面试中被问到"一个嵌入式系统里能不能用malloc",答案是"能用但尽量不要用",原因有三个:第一,MCU的堆通常很小,分配大块内存容易失败;第二,频繁分配释放会产生碎片,时间长了系统就废了;第三,malloc不是确定性的,实时性要求高的场景根本不敢用。替代方案是静态分配、内存池、环形缓冲区。
2.2 位操作和字节序是嵌入式基本功
嵌入式开发天天和寄存器打交道,位操作可以说是家常便饭。面试中常见的一个题目是"用宏定义实现将一个32位整数的第n位置1、清0、翻转",这个必须写得又快又对。
#define SET_BIT(x, n) ((x) |= (1U << (n))) #define CLEAR_BIT(x, n) ((x) &= ~(1U << (n))) #define TOGGLE_BIT(x, n) ((x) ^= (1U << (n)))追问环节经常是字节序(大小端)。我在一次面试中就被问过:"你写一个函数判断当前系统是大端还是小端",而且要求用两种方式实现。第一种是用指针强制转换判断,第二种是用联合体(union)判断,因为union的所有成员共享同一块内存起始地址。
int is_little_endian(void) { union { int i; char c; } u; u.i = 1; return u.c == 1; }更深一层的问题是"大小端转换怎么做",嵌入式中经常需要把收到的网络字节序数据转换为主机字节序,面试官希望听到的是你懂得用htons、ntohl这类函数,或者自己做移位转换的原理。我当时是这么答的:如果要手动实现,核心思路就是通过移位把字节重新排列,比如把一个32位数从小端转大端,可以((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x >> 8) & 0xFF00) | ((x >> 24) & 0xFF)。这个题答出来基本就是加分项。
2.3 结构体对齐与volatile的妙用
结构体字节对齐这个知识点,我几乎每次面试都被问到,而且每次都有人在这里翻车。核心规则其实就几条:结构体每个成员的偏移量必须是其自身大小的整数倍;结构体的总大小必须是最大成员大小的整数倍;可以通过#pragma pack或__attribute__((packed))改变对齐方式。
struct example { char a; // 偏移0 int b; // 偏移4 short c; // 偏移8 }; // 总大小12如果面试官问你"为什么编译器要对齐",你要答到点子上:因为CPU访问对齐数据是一次总线周期,不对齐的数据可能需要两次访问,牺牲空间换时间。而在嵌入式里,如果结构体要通过网络传输或者写入Flash,就必须用__attribute__((packed))取消对齐,否则不同编译器下结构体大小不一致,数据解析就会出错。
再说volatile,这个关键字在嵌入式面试中出现的频率高得吓人。面试官一般会问"volatile的作用是什么",标准答案是告诉编译器不要对被修饰的变量做优化,每次都必须从内存地址重新读取。然后他会追问三个典型场景:硬件寄存器映射(比如读取状态寄存器)、中断服务程序和主循环共享的全局变量、多线程环境下的共享标志。最后一个场景要小心,volatile只能保证读取的即时性,不能保证原子性,真正的线程安全还得靠锁或原子操作。我面试的时候主动补了这句话,明显看到面试官眼神亮了一下。
3. 操作系统与Linux核心知识
3.1 进程、线程和调度策略
现在的嵌入式设备几乎没有裸机跑的,RTOS或者Linux是标配。所以操作系统相关的问题你绕不开,尤其是进程线程的区别、任务调度、同步机制这些。进程是资源分配的最小单位,线程是CPU调度的最小单位,同一个进程内的线程共享地址空间和资源,但进程之间是隔离的。嵌入式Linux里fork、vfork、clone的区别也是高频题。
调度策略这里,面试官喜欢问实时操作系统和Linux普通进程的调度差异。RTOS常见的调度算法有优先级抢占式调度和时间片轮转调度,核心是保证高优先级任务能在确定时间内得到响应。比如FreeRTOS里,优先级数字越大优先级越高,同优先级任务按时间片轮转;而Linux的CFS(完全公平调度器)是基于虚拟运行时间来的,它追求的是公平性,不是实时性。如果你做的是Linux实时应用,就得了解PREEMPT_RT补丁或者使用实时调度策略(SCHED_FIFO、SCHED_RR)。
面试中还经常有个连环问:什么是优先级反转?怎么解决?这个必须好好准备,因为它是嵌入式面试的经典题。优先级反转就是高优先级任务被低优先级任务阻塞,而中优先级任务又抢占了低优先级任务的CPU,导致高优先级任务迟迟得不到执行。经典案例是火星探路者任务,就是优先权反转导致系统不断重置。解决办法有三个级别:优先级继承、优先级天花板,还有禁止中断或者使用互斥量而不是信号量。回答的时候能把火星探路者的案例讲出来,基本就能镇住场面。
3.2 同步互斥的机制对比
互斥量(Mutex)和信号量(Semaphore)的区别也是面试官很喜欢的一个点。我遇到过一个相当有水平的问法:"一个二值信号量和一个互斥量,你怎么选?"如果你只知道二值信号量是0和1,互斥量也是0和1,那这道题就废了。要答出以下几个关键区别:互斥量有所有权概念,谁上锁谁解锁,而信号量没有;互斥量支持优先级继承,能解决优先级反转,二值信号量不行;互斥量通常用于互斥访问共享资源,信号量更常用于任务同步和事件通知。从使用场景上说,如果只是任务的同步通知,信号量更轻量合适;如果是保护临界区资源,互斥量更加安全。
死锁也是必考题,四个必要条件——互斥、持有并等待、不可剥夺、循环等待——要背得滚瓜烂熟。而且面试官一定会问你"怎么避免死锁",你要能说出来:破坏互斥条件(不太现实)、破坏持有并等待(一次性申请所有资源)、破坏不可剥夺(申请不到就释放自己已有的)、破坏循环等待(给资源编号,按序申请)。我在项目里最常用的是"锁的顺序必须全局一致"这个原则,在所有代码里规定:如果同时需要两个互斥量,必须先锁A再锁B,从根源上消除循环等待的可能。
3.3 Linux驱动开发的入门问题
嵌入式Linux方向的岗位,驱动开发是重头戏。面试官一般不会一上来就问你复杂的驱动怎么写,而是从字符设备驱动的基本框架开始,让你说说open、read、write、ioctl这些系统调用在内核里是怎么被路由到驱动函数的。这里面的核心就是struct file_operations结构体,以及主设备号和次设备号的概念。
设备树(Device Tree)现在已经是ARM Linux的标配了,面试官十有八九会问"设备树的作用是什么"。你要回答:设备树是一种描述硬件资源的数据结构,它把硬件的寄存器地址、中断号、时钟频率等信息从内核源码中解耦出来,使得同一个内核镜像可以支持多种不同的硬件平台。追问通常是"你写过设备树节点吗",如果你能现场写出一个简单的GPIO按键节点,包括compatible属性、reg属性、interrupts属性,那这道题就稳了。
中断下半部机制也是Linux驱动的高频考点,包括软中断、tasklet、工作队列这三种机制的区别。软中断运行在中断上下文,不能睡眠;tasklet基于软中断实现,同一时刻一个tasklet只能在一个CPU上执行;工作队列运行在进程上下文,可以睡眠,适合做耗时操作。在面试中我习惯用一句话总结:中断上半部处理紧急的硬件操作,下半部处理耗时的数据逻辑,具体用哪个取决于你能不能睡觉(睡眠)。
4. 硬件基础与通信协议
4.1 常见通信接口你分得清吗
嵌入式的本质是软硬结合,所以面试一定会考察你对常用通信接口的掌握程度。最常问的是UART、SPI、I2C这三兄弟的区别。我从原理和面试话术两方面给你总结一下:UART是异步串行通信,全双工,靠起始位和停止位同步,通常就三根线——TX、RX、GND;SPI是同步串行通信,全双工,四根线——SCLK、MOSI、MISO、CS,速度比UART快很多,适合高速外设比如Flash芯片和屏幕驱动;I2C是半双工,两根线——SCL、SDA,通过设备地址寻址,支持多主机和总线仲裁,适合接传感器这类低速设备。
面试官特别喜欢问"I2C和SPI怎么选"。你要从速率、引脚数量、设备数量、全双工还是半双工、有无地址概念这几个维度去回答。比如你的板上要挂8个温湿度传感器,每个传感器只需要几KB/s的速率,那I2C更合适,因为两根线就够了,而且每个设备有自己的地址;如果只有一个SPI Flash要高速读写,那肯定选SPI,速率高且是全双工。
还有一个高频题是"串口通信中波特率是什么,波特率误差会导致什么问题"。波特率就是每秒传输的码元数,比如9600、115200。收发双方的波特率必须一致,否则采样点会逐步偏移,时间长了必然出现乱码或者数据错位。实际工程中,晶振精度、分频误差都会导致实际波特率和理论值有偏差,一般偏差在2%以内问题不大,超过就可能出问题。我遇到过一个特别坑的情况,板子上的晶振是12MHz,给UART分频到115200会有一个不小的误差,导致跑长了就开始乱码,最后换了时钟频率才解决。
4.2 中断处理与实时性分析
中断系统的理解深度,直接决定面试官把你归为"会用MCU"还是"懂MCU"。中断的流程是:外设产生中断事件 -> 中断控制器仲裁 -> CPU保存现场 -> 跳转到中断服务函数 -> 清中断标志位 -> 恢复现场并返回。面试官喜欢让你画出这个流程图,然后追问"为什么ISR里不能做耗时操作"。
答案核心是中断会打断当前正在执行的任务,如果ISR执行时间太长,其他中断就得不到及时响应,实时性就无法保证。所以正确做法是ISR里只做最紧急的事,比如读取数据到缓冲区、设置标志位,耗时逻辑放到主循环或者任务里处理。FreeRTOS里还专门有从ISR安全调用的API版本,比如xQueueSendFromISR,因为普通版本的API可能导致任务阻塞,而中断上下文里不能阻塞。
嵌套中断也是一个进阶考点。Cortex-M3/M4内核的NVIC支持中断嵌套,高优先级中断可以打断低优先级中断。但实际项目里,嵌套深度要控制,因为每次嵌套都要压栈更多的寄存器,栈空间消耗大,而且嵌套太深容易导致栈溢出。我一般建议裸机开发里减少中断嵌套,尽量用关中断+标志位的方式保护临界区。
4.3 ARM体系结构与启动流程
ARM相关的题目在嵌入式面试里分量不小。至少你要区分Cortex-M和Cortex-A两个系列:M系列是单片机微控制器,没有MMU,跑的是裸机或者RTOS,中断响应快,是STM32这类芯片的内核;A系列是应用处理器,有MMU,可以跑完整的Linux,是树莓派、手机SoC的内核。
启动流程也经常会问。STM32的启动过程大致是:从Flash取向量表,初始化SP和PC,执行SystemInit时钟初始化,然后跳到main函数。如果做的是Linux系统,启动过程则是:BootROM -> Bootloader(U-Boot)-> 内核 -> 挂载根文件系统 -> 用户进程。面试中能把两级启动都讲清楚的人不多,你如果都能说,会很加分。
存储器的层次结构也值得准备,从寄存器、Cache、SRAM、SDRAM到Flash,速度递减、容量递增、价格递减。面试官可能会问"Cache的作用是什么",其实它就是为了填补CPU和主存之间的速度差距,把经常访问的数据放到更快的存储器里。嵌入式里还有个经典优化思路是"Cache一致性"问题,DMA和CPU共享数据时,要注意Cache和内存里数据不一致,需要做Cache刷新或者Invalidate操作。能答到这一层,说明你是有实战经验的。
5. 项目经验的复盘与表达
5.1 怎么把项目讲出含金量
简历上写了项目,面试官就一定会深挖。我见过太多人项目写了一大堆"基于XX的XX系统",结果一被问就露馅。讲项目不是背流水账,而是要有结构、有细节、有深度。我建议大家按照下面这个框架来准备每个项目:
- 一句话背景:这个项目解决什么问题,面向什么场景?
- 硬件选型:为什么选这块板子、这个芯片,资源够不够?
- 系统架构:分几个模块,任务怎么划分,数据怎么流动?
- 核心难点:你遇到的最大问题是什么?怎么定位和解决的?
- 性能指标:实时性、功耗、稳定性这些有数据支撑吗?
比如我自己做过的一个STM32F4的音视频频谱分析系统,如果只是说"用ADC采样然后做FFT然后显示",那跟没说一样。我会准备到这一层:ADC采样率是多少,采样点数是多少,FFT做了多久,用的是什么库还是自己写的基2算法,结果怎么映射到LCD屏幕上,采样和显示之间用什么同步机制。面试官要验证的是你是否有真正的工程思维,而不只是会用DSP库调用。
5.2 引导面试官去问你擅长的方向
讲项目时有个小技巧,就是主动埋点,引导面试官问你想展示的内容。比如我在讲项目的时候,会顺带提一句"当时IMU的数据老是有毛刺,后来我在中断里加了均值滤波,在应用层加了滑动窗口滤波,才把数据平滑下来"。这句话其实埋了两个钩子:一个是你用过中断,另一个是你懂滤波算法。面试官顺着问任何一个,你都能展开讲,占住节奏。
但是埋点有个原则:一定要真实。你埋的每个点都必须是你真研究过的,不然面试官追问个两三轮你就崩了。宁可说自己没做过,也不要硬编。我见过太多人在项目细节上吹牛然后被问穿,场面非常尴尬,而且基本就宣告这场面试结束了。
5.3 开源项目参与经历的价值
现在的嵌入式面试越来越看重求职者有没有接触过开源项目。热词里也提到了一些嵌入式开源项目,这确实是简历上的加分项。面试官的逻辑很简单:如果你能读懂别人的开源代码,甚至提过PR,说明你有独立学习的能力和代码阅读能力。
我建议大家可以挑一两个知名度高、代码量适中的开源项目来读,比如RTOS内核(FreeRTOS、RT-Thread)、嵌入式GUI框架(AWTK、LVGL)、通信协议栈(lwIP、Snmp的嵌入式移植)。读源码不是从头到尾一行一行看,而是带着问题去看,比如"FreeRTOS的任务切换是怎么实现的",找到源码里vTaskSwitchContext对应的位置,分析一下PendSV异常处理流程,再对比网上别人画的流程图。这种经历在面试中聊起来,比你说"看过某某教程"要有说服力得多。
6. 笔试与手写代码高频题型
6.1 手写链表一定要过关
嵌入式笔试里,链表的出现频率高得离谱,尤其是单链表的反转、查找倒数第K个节点、判断是否有环这三道题。下面我把单链表反转的迭代写法和递归写法都写出来,你们直接背下来也行,但最好能理解:
// 迭代法 struct ListNode* reverseList(struct ListNode* head) { struct ListNode *prev = NULL, *curr = head, *next = NULL; while (curr != NULL) { next = curr->next; curr->next = prev; prev = curr; curr = next; } return prev; }递归版写法更简洁但不太好想:
struct ListNode* reverseListRecursive(struct ListNode* head) { if (head == NULL || head->next == NULL) { return head; } struct ListNode* newHead = reverseListRecursive(head->next); head->next->next = head; head->next = NULL; return newHead; }注意:笔试时除了写出代码,建议顺手把时间和空间复杂度写上。反转操作时间复杂度O(n),空间复杂度O(1),递归版空间复杂度是O(n),因为递归调用栈。有些公司要求你直接在纸上写递归版,就是为了看你能不能注意到这个点。
6.2 字符串和内存操作函数要能自己实现
嵌入式笔试很少让你直接调库函数,经常要求你手动实现strcpy、strcmp、memcpy这一类函数。市面上有大量参考实现,但我想提醒一个细节:memcpy不允许处理内存重叠,如果要处理重叠区域就得用memmove。这两个函数很多初学者会搞混,但面试官往往就喜欢在这里设陷阱。
void *my_memcpy(void *dest, const void *src, size_t n) { unsigned char *d = dest; const unsigned char *s = src; while (n--) { *d++ = *s++; } return dest; }如果面试官追问"如果src和dest有重叠怎么办",你就应该答:需要判断地址方向,如果dest在src后面且范围重叠,需要从后往前拷贝,这正是memmove的实现思路。答到这里,这个问题基本就满分了。
另一个高频题是"实现一个循环缓冲区"。这太像嵌入式工作里的真实需求了,串口接收、音频数据流都会用到,而且能考察你对取模运算、读写指针、空满判断的理解。建议自己提前写一遍:
typedef struct { uint8_t *buffer; uint32_t size; uint32_t head; uint32_t tail; } ring_buf_t;空的判断条件是head == tail,满的判断条件有几种实现方式,最简单的是保留一个空位,即(tail + 1) % size == head。如果你能在讲代码的时候说明"为什么要留一个空位",面试官会非常满意,因为这说明你清楚边界条件的坑。
6.3 嵌入式笔试的调试题
除了手写代码,很多公司还会出一些"找bug"或者"分析输出"的题目。典型的一种是考察"sizeof运算符和strlen的区别",这个太经典了,却总有人错。另一个常见的是考察静态变量的生命周期,比如:
int func(void) { static int count = 0; count++; return count; }然后问你连续调用三次的输出是多少。答案是1、2、3,因为static变量的初始化只执行一次,它存放在数据段,生命周期是整个程序运行期间。如果你能主动解释"这个变量的存储位置在.bss或.data段,而不是栈上",那就能比同龄人多展示一点底层知识。
还有一种笔试是给你一段中断处理代码,让你指出问题。比如下面这种:
int flag = 0; void ISR(void) { flag = 1; printf("interrupt\n"); } int main(void) { while(1) { if (flag) { flag = 0; /* 处理逻辑 */ } } }问题很明显:printf在ISR里执行,耗时且不可重入;flag没有加volatile,主循环可能永远看不到变化;主循环和ISR共享的变量需要用volatile修饰保证内存可见性。能找出这三个点,笔试这道题基本就稳了。
7. 高频场景题与排查思路
7.1 板子不干活,你怎么查
面试官非常喜欢从一个故障现象切入,考察你的实战排查能力。我被问到最多的一个场景是:"板子上电后程序不运行,串口没有打印,你怎么排查?"这道题没有标准答案,但你的回答要有逻辑层次。我是这么答的:
先量电源,确认各路电压是否正常,核心电压、IO电压有没有短路或跌落;然后确认时钟是否起振,用示波器量晶振引脚,确认时钟频率对不对;接着检查复位电路,复位引脚是不是一直处于复位状态;再看启动模式配置引脚,STM32的BOOT0和BOOT1是不是设置正确;最后考虑程序本身有没有跑飞,用调试器连上看看PC指针跑到了哪里,或者检查看门狗是不是一直在复位芯片。从电源到时钟到复位到启动模式到程序,这个排查顺序是通用的。
追问一般是"如果程序跑飞了怎么定位"。你可以说:使用硬件调试器(J-Link、ST-Link)在线调试,设置断点或HardFault中断;查看栈回溯来定位最后一次函数调用链;检查是否是数组越界、野指针或者栈溢出导致的异常。在工程上,我习惯在HardFault_Handler里把几个关键寄存器(PC、LR、PSP/MSP)的值存下来,用调试器查看,这样能直接定位到出错的汇编语句和对应的C代码行。
7.2 数据错乱和通信不稳定怎么处理
串口数据乱码是另一个常见的面试场景题。这道题我总结了一整套排查清单,你按顺序答,面试官基本挑不出毛病:波特率是否匹配,这个最先查;通信双方的参考地是否连接,GND没接好导致共模电压不对,这种现象很隐蔽;线的质量,杜邦线太长或接触不良;干扰问题,特别是电机、继电器这类大电流设备启动时会不会干扰通信;代码逻辑问题,比如接收中断丢数据、环形缓冲区满了、溢出标志没清除。
I2C通信偶尔失败也经常被拿出来问。常见的坑包括:上拉电阻阻值不合适导致上升沿太慢;总线上设备地址冲突;总线处于死锁状态,SDA被某个设备拉低不放。第三个问题很有意思,有一种情况是某个设备异常后一直抢占总线,可以把SCL翻转九次让总线释放,这是I2C协议的标准处理手段,面试时能说出这个细节说明你真调过I2C。
7.3 Flash和看门狗相关的老坑
Flash写入失败、擦除超时这些问题在实际项目里经常遇到,面试时也可能作为场景题出现。Flash操作的核心注意点有几个:写Flash之前必须先擦除,而且擦除以扇区/块为单位,不像写操作可以按字节;Flash擦写次数有限制,通常是十万次级别,频繁擦写同一块区域会导致损坏;擦写过程中不能断电,否则Flash可能进入不可预测状态;如果项目里用Flash存参数,建议使用磨损均衡和双备份设计,双备份的意思是写坏一块还有另一块能顶上。
看门狗(Watchdog)也是一大考点,面试官喜欢问"喂狗为什么不能放在中断里"。答案很典型:如果喂狗在中断里,即使主流程卡死了,中断还能正常触发,看门狗就永远不会被触发,失去保护作用。正确做法是喂狗放在主循环里,而且最好配合一个任务调度监视,比如检测到一个关键任务超过多久没执行就故意不喂狗,让系统复位。这个思路叫"结构化喂狗",比单纯在主循环里清狗计数器高级多了,面试官会喜欢。
8. 面试复盘与求职建议
8.1 几个对我帮助很大的准备方法
回顾这段时间的面试,我觉得有几件事是做了之后见效特别明显的。第一是整理自己的"八股文"笔记,不是从网上抄,而是每道题先自己答一遍,再去翻参考文档查漏补缺。比如"volatile有什么用"这道题,我先写自己的理解,再对比经典讲解,最后补充了嵌入式里寄存器映射和中断共享变量两个例子。这样整理过的内容,在面试中会形成肌肉记忆,回答起来自然流畅。
第二是刻意练习用项目讲知识点。面试官很少直接问"你讲一下中断",而是会让你"结合你的项目谈谈中断的使用"。如果你能顺手拈来地用自己项目里的实际场景去解释知识点,比干巴巴背概念有说服力得多。我准备面试的时候,每个核心知识点都准备了对应的项目案例,比如讲DMA就会提到我做的AD采集里用DMA搬运数据减少CPU占用,讲任务优先级的分配就提到我做的显示刷新和串口收发怎么划分。
第三是多做模拟面试。找一个懂技术但不一定懂你项目的朋友,让他拿着你的简历随机提问。这个过程中你会发现自己有很多"以为知道但说不清楚"的地方,这些就是复习的重点。比一个人闷头看效率高很多。
8.2 关于学习路线的建议
如果你还在校或者是刚入行,想走嵌入式这条路,我建议分三步走:先把C语言和单片机基础打牢,用STM32做一两个小项目,不贪多,但每个项目都要吃透;然后系统学习数据结构和操作系统原理,推荐结合FreeRTOS源码来学,看任务调度、消息队列、信号量具体是怎么实现的;再往后如果你往嵌入式Linux方向走,就要啃Linux应用编程和驱动开发,理解进程、线程、网络编程、设备树、字符驱动这一整套东西。
网上流传的各种嵌入式学习路线图我也看过很多,五花八门,但核心就这几板斧。需要注意的是,嵌入式分支很多,有偏向硬件的,有偏向底层驱动的,有偏向应用的,也有偏向AI的。你不可能样样精通,先在一个方向打出深度,再横向扩展。我在面试中见过不少人是"什么都会一点,但什么都不精",简历是很好看,但面试官深挖两轮就露馅了。这个行业其实很包容,你只要有某个方向能扛住三连问,就有机会。
8.3 关于薪资和Offer的选择
嵌入式这个方向薪资不像互联网那么疯狂,但目前看下来应届生和三年经验以内的岗位需求还是挺稳的,尤其是汽车电子、工业控制、物联网这几个赛道。面试谈薪的时候要注意,不要只看月薪,要问清楚13薪还是14薪、年终奖占比、加班情况、公积金基数这些。另外也不要忽略平台的价值,大公司可能薪资一般,但流程规范、项目复杂、能学到东西;小公司可能给得高,但可能没人带,全靠自己踩坑。
Offer比较的时候,我个人最看重的是项目方向和自己长期规划的匹配度。比如你未来想深耕Linux驱动,那去一个主要用STM32做裸机开发的公司,可能成长会比较慢;反过来,如果公司有大量的Linux和驱动业务,就算起薪低一点,成长空间也大得多。这个账要算长远一点。
最后再分享一个很实际的技巧:面试过程中如果被问住了,不要直接说"我不会",你可以说"这个我了解得不够深,但我目前的理解是这样的……然后说出一个大概方向"。大多数面试官不会因为你说不出完全正确的答案就直接否定你,他们更看重你的思维方式和学习意愿。但前提是你真的要有一些积累,连方向都说不出来就比较难了。希望大家都能拿到心仪的Offer,在嵌入式这条路上越走越稳。