面灵AI→

海康威视 嵌入式软件开发一面面经:static/inline、RTOS 优先级反转与网络编程 30 问

轮次
一面
时间
2026-09
来源
牛客网

《面试题目》

  1. static 这个关键字说一下。
  2. inline 的作用是?
  3. 结构体和联合体有什么差别?
  4. strlen 和 sizeof 有什么差别?
  5. 堆和栈有什么差别?
  6. 什么情况下会导致栈溢出?
  7. 链表和数组有什么差别?
  8. 如果要删除链表里面的一个节点,应该怎么做?
  9. Linux 和 RTOS 的系统有什么差别?
  10. 什么是优先级反转?
  11. 如果说手里有 20 个任务,怎么来给这 20 个任务定优先级高低?
  12. 刚才提到的数据采集任务,为什么要优先级高一点?
  13. 如果一个 RTOS 系统频繁出现任务响应超时,应该怎么排查?
  14. 还有没有其他的选择或者方式?
  15. 在 RTOS 系统里面怎么去排查栈溢出的问题?
  16. 线程和进程有什么差别?
  17. 如果我要结束一个线程,都有哪些方式?
  18. 那怎么去终止?
  19. 如果要用 UDP 来实现一个可靠的通信,应该用哪些方式?
  20. select 和 epoll 这两个有什么差别?
  21. 组播和广播有什么差别?
  22. 那怎么确定发出去的是广播的报文?
  23. 能挑一个项目,讲一下项目里印象深刻的、对你比较有挑战的一个问题吗?
  24. 前面这个问题定位的过程是什么样的?直接去查的手册吗?
  25. 你刚才讲的这三个内存模式有什么差别?
  26. 平时都会用到哪些 AI 工具?
  27. 在使用过程中,怎么判断 AI 输出的结果是可靠的,出现 AI 幻觉这种情况?
  28. 代码占比是多少?
  29. 手里现在有其他 offer 吗?

反问:

  1. 你那边有什么其他的问题要问吗?

《参考解析》

  1. static 要分三种语境答:修饰局部变量时改变的是生命周期(存到静态存储区,函数退出不销毁,但作用域仍限于函数内,因此天然不线程安全);修饰全局变量或函数时改变的是链接属性(仅本编译单元可见,避免重名污染);在 C++ 里还有修饰类成员的含义(属于类而非对象,静态成员函数没有 this)。面试官顺着问「未初始化的全局变量和局部变量的初始值是什么」时,答全局与静态变量在 .bss 段被清零、局部变量是栈上的不确定值——这也是很多随机 bug 的来源。

  2. inline 的真正作用是「允许」而非「强制」:它把函数定义放进头文件、让编译器在调用点展开,省掉函数调用开销(压栈、跳转、返回、寄存器保存)。代价是代码膨胀、指令 cache 命中率下降、调试困难。适用场景是短小且高频调用的小函数(如寄存器读写、位操作封装);递归函数、函数体很大、或需要通过函数指针调用的情况不适合。现代编译器会自行决定是否内联,加 inline 更像是给链接器和编译器的提示。

  3. 优先级反转是 RTOS 的必考核心题,要答出「怎么解决」:低优先级任务持有互斥资源,高优先级任务被它阻塞,而中优先级任务又抢占了低优先级任务,于是高优先级任务被中优先级任务间接拖延。解决方案两种——优先级继承(持有锁时临时把持锁任务提升到等待者的优先级,FreeRTOS 的 mutex 默认就是这个机制)和优先级天花板(锁被创建时就把持有者抬到可能访问该资源的所有任务中的最高优先级)。答题时补一句关键区别:优先级继承是被动、按需提升,天花板是静态设定、更可预测但可能过度提升。

  4. 「20 个任务怎么定优先级」考的是方法论而不是答案:可用的框架是先分级再排序——周期性硬实时任务(必须在截止时间内完成,如数据采集、控制环)最高;有明确时序约束的事件处理任务次之;交互与日志、上报类任务再次;后台批处理与统计最低。同一级内按截止时间紧、执行时间短、被依赖程度高的排前面(可以用截止时间单调调度 RMS 或最早截止优先 EDF 的直觉来讲)。数据采集被追问「为什么优先级高」时,答它的时序窗口最窄、丢一次数据就无法补偿,且它是后续所有处理的上游。

  5. RTOS 排障题要给出可操作的顺序:任务响应超时先分三类原因——任务本身优先级不够或被高优先级任务长期占用 CPU(先查各任务 CPU 占用与就绪队列)、临界区或关中断时间过长(查最大关中断时长)、以及资源竞争与阻塞(优先级反转、信号量误用、死锁)。工具侧用 RTOS 自带的运行时统计(任务栈水位、CPU 占用)、GPIO 翻转配示波器量中断到任务的实际延迟。栈溢出排查靠栈填充值(magic pattern)看水位、编译器或 MPU 的栈溢出检测、以及把可疑任务栈先临时调大做二分定位。

  6. 栈溢出的成因要成对讲:深递归、大局部数组(尤其把大 buffer 定义在函数内)、函数调用层次深导致累加、以及栈大小配置本身偏小、中断嵌套在同一栈上叠加。工程上的预防是以「最大调用深度 × 每层栈帧」估算并留余量、把大 buffer 改静态或堆、开栈保护与 MPU 检测。

  7. 网络编程这组题答出「边界与代价」就能加分:UDP 上做可靠传输得自己在应用层实现序号、确认与重传、超时估计(RTO)、去重与乱序重排、以及滑动窗口做流量控制,本质上是把 TCP 的一部分搬到应用层,换来的是可控性(能选重传策略、能做多路复用与低延迟取舍)。select 与 epoll 的差别是扫描方式与上限:select 每次调用都要把 fd 集合拷进内核并线性扫描、有 FD_SETSIZE 限制;epoll 用内核事件表加就绪链表,只返回就绪 fd,适合大量连接。组播与广播的判据在目的 MAC 与 IP 地址:广播用全 1 的 MAC 和受限广播地址(如 255.255.255.255)或子网广播地址,组播用 01:00:5E 开头的 MAC 和 D 类地址(224.0.0.0/4),抓包看目的地址即可确认。

  8. AI 工具那两个问题现在出现得越来越频繁:被问「用哪些 AI 工具」时给具体场景(代码补全、生成单测、查手册与报错、写文档),比报名字有用。被问「怎么判断 AI 输出可靠」时,答法要落在可验证性上——交叉验证(官方手册、数据手册、实际编译运行)、小范围试验后再合入、对 AI 生成的寄存器操作与并发代码格外警惕(这两类错误最难查)、以及对结果做单元测试与静态检查。面试官真正想听的是你有没有把 AI 当「需要复核的助手」而不是「答案」。项目难点类问题按现象→假设→验证→定位的链路讲,并说明为什么直接查手册够或不够。