(嵌入式面经)第11章 20+公司面经杂谈(五):Momenta、瑞晟、OPPO、VIVO

本篇涉及的所有问题概要:大家可以试试在看参考答案前,提前尝试解答,以便明确自身知识点的不足部分!

1.中断里面通常可以睡眠吗,为什么?

2.什么是栈溢出,有什么方法检测?

3.MCU 与 MPU 的区别是什么?

4.讲一下Linux中断分为上半部和下半部的原因?

5.如果给线程访问加锁,是在驱动层实现还是应用层实现?

6.现在需要一个常量5来做一些运算,你会使用const还是define?为什么?

7.项目里的ADC多通道采集是具体采用什么方式?

8.说一下互斥锁,自旋锁的区别,在中断中可以用哪个锁?

9.怎么保证原子性操作?

10.专栏订阅奖励(支持模仿)——个人创新点问答:在你设计的FreeRTOS PLUS中提及修改任务控制块,增加优先级阶段继承属性,来进一步降低“优先级反转”的影响,展开说说?

---------------------------------------------------------------------------------------------------

1.中断里面通常可以睡眠吗,为什么?

在 Linux 内核或嵌入式 RTOS(如 FreeRTOS)中,中断处理程序(ISR) 不能进入 睡眠(sleep/wait) 状态。

主要有 两大原因

  1. 中断上下文的限制(缺乏调度支持)
  2. 中断设计的初衷——高实时性、不可阻塞

1. 中断上下文限制,缺乏调度支持

📌 什么是中断上下文?

  • 内核中的中断处理程序不属于某个特定进程,而是独立的执行实体,没有进程调度信息。
  • 没有进程的内核栈、调度信息、文件描述符 等数据结构,无法像普通进程一样被调度。
  • 缺少进程控制块(PCB),无法进行任务切换。

✅ 具体分析

  • 进程上下文(Process Context) → 进程有自己的 调度信息、内核栈、寄存器保存,可以被挂起并恢复。
  • 中断上下文(Interrupt Context)没有这些信息,如果 ISR 进入睡眠,就无法被调度器重新唤醒。

🚨 问题:

  • 进入睡眠后 找不到中断的上下文信息,唤醒后无法恢复,系统可能会崩溃。

2. 中断设计的初衷:高实时性,不可阻塞

📌 中断的核心任务

  • 中断的目的是 快速响应硬件事件,执行最小化的任务,然后尽快恢复系统运行
  • 如果中断中调用 sleep(),会导致 CPU 挂起整个系统,影响其他任务的执行。

✅ 为什么要避免阻塞?

  • 中断优先级比普通进程高,如果中断中进入睡眠,会导致 CPU 被占用,进程调度器无法调度其他任务。
  • 高优先级中断可能会阻塞低优先级进程,影响系统性能

3. 实例:为什么 ISR 不能调用 sleep()

📌 错误示例

🚨 在中断处理中调用 sleep()

  • msleep()会导致任务挂起,但中断上下文没有进程调度信息,无法恢复。
  • 可能导致系统崩溃Kernel Panic
  • 📌 正确做法:使用 workqueuetasklet

    中断处理程序中,只做必要的工作,然后将复杂任务交给其他线程(如 workqueue)。

    解决方案:使用 workqueue

  • ISR 只负责触发任务,不会阻塞中断
  • workqueue 运行在进程上下文,可以 sleep()
  • 结论

    使用 workqueuetasklet 处理复杂任务

    避免在 ISR 中使用 sleep(),改为调度后台线程处理

    2.什么是栈溢出,有什么方法检测?

    1. 什么是栈溢出?

    栈溢出(Stack Overflow)是指程序在运行过程中,函数调用栈空间超出系统分配的范围,导致程序崩溃或行为异常。

    由于栈空间是有限的,如果程序需要的栈空间超过了系统分配的最大限制,就会发生栈溢出

    2. 栈溢出的常见原因

    📌(1)函数调用层数过深

    无限递归/深度递归 会不断地向栈中压入新的栈帧,最终超过栈的最大空间,导致栈溢出。

    示例:递归导致的栈溢出

    由于 stack_overflow_function()没有终止条件,递归会无限增长,最终导致栈空间被耗尽,程序崩溃。

    📌(2)占用栈空间过大的局部变量

    如果一个函数中的局部变量过大,尤其是大数组结构体,也可能导致栈溢出。

    示例:局部变量过大导致栈溢出

    🚨 问题:

    • 局部数组 buffer 占用了 10MB 的栈空间,大于默认栈空间,导致栈溢出。

    3. 栈溢出的后果

    4. 栈溢出的检测方法

    📌(1)哨兵检测(Stack Sentinel)

    • 在栈的起始地址结束地址放置特定的标志值(哨兵值),然后在任务切换或关键点检查这些值是否被修改。
    • 如果哨兵值被改写,说明发生了栈溢出!

    示例:哨兵检测

    • 只适用于单任务环境,如果多个任务共用栈空间,可能检测不到问题。

    📌(2)栈指针范围检测

    • 在任务切换时检查 SP(栈指针) 是否越界
    • 如果 SP 低于分配的栈空间,说明栈溢出

    示例:FreeRTOS 栈溢出检测

    📌 FreeRTOS 需要在 FreeRTOSConfig.h 启用检测

    📌(3)使用 ulTaskCheckFreeStackSpace() 监测任务栈

    FreeRTOS 提供的栈检查 API

    📌 如果 freeStack 过小(如 <20),可能需要增加栈空间!

    📌(4)GCC 编译器保护(-fstack-protector

    • 启用 -fstack-protector 选项,可以在函数返回时检测栈溢出
    • 原理:编译器在栈上存放"Canary" 保护值函数返回时检查 Canary 是否被修改如果被篡改,则触发 stack smashing detected

    启用 -fstack-protector

    gcc -fstack-protector -o test test.c

  • 只能检测函数返回时的溢出
  • 不能检测无限递归
  • 5. 如何预防栈溢出

    (1)避免深度递归

    • 用循环代替递归
    • 尾递归优化

    (2)减少局部变量的栈空间

    • 大数组使用 malloc() 在堆上分配
    • 减少函数嵌套调用

    (3)合理分配栈大小

    • 在 FreeRTOS/Linux 任务创建时,合理设置 STACK_SIZE
    • 监测 uxTaskGetStackHighWaterMark() 确保栈足够

    (4)使用 -fstack-protector 进行编译器保护

    gcc -fstack-protector -o my_program my_program.c

    (5)启用 configCHECK_FOR_STACK_OVERFLOW

    结论

    🚀 防止栈溢出,需要合理分配栈大小、优化代码结构、并启用硬件和软件监测机制! 🎯

    3.MCU 与 MPU 的区别是什么?

    • MPU(Microprocessor Unit) 适用于复杂运算的大型程序,通常需要外挂 RAM 和 ROM
    • MCU(Microcontroller Unit) 适用于中小型控制程序内部集成 RAM 和 ROM,无需外挂存储器。

    1. MCU(微控制器)

    特点:

    • 集成度高:MCU 典型地将 CPU、RAM、ROM、GPIO、定时器、ADC、PWM、串口(UART/I2C/SPI) 等外设集成在一颗芯片内
    • 成本低:由于 MCU 的所有组件都集成在同一芯片中,硬件设计成本较低。
    • 低功耗:适用于低功耗设备(如 IoT 设备、工业控制、汽车电子)。
    • 高实时性:一上电即可运行,无需操作系统,也可以跑 RTOS(如 FreeRTOS)。
    • 典型应用
  • 传感器数据采集(如 STM32 采集温湿度传感器)
  • 电机控制(如 STM32 驱动步进电机)
  • 显示控制(如 LCD、OLED、数码管控制)
  • 无线通信模块(如 ESP8266 进行 Wi-Fi 通信)
  • 例子

    • STM32F4(Cortex-M4)
    • AVR ATmega328(Arduino 用的芯片)
    • GD32F3(国产 MCU)

    2. MPU(微处理器)

    特点:

    • 高性能:MPU 适用于复杂计算,如 AI、图像处理、多任务操作
    • 需要外挂 RAM 和 ROM
  • RAM 作为运行时存储(DDR3/DDR4)
  • ROM(Flash/eMMC)存储操作系统和程序
  • 运行完整的操作系统

  • Linux、Android、Windows CE、FreeBSD 等
  • 适用于需要 UI、多任务、复杂运算的设备

  • 智能手机、嵌入式 Linux 设备、工控机、网络路由器
  • 典型应用

  • 图像处理(如 AI 摄像头)
  • 多媒体播放(如智能电视)
  • 网络通信设备(如 OpenWRT 路由器)
  • 例子

    • 树莓派(Raspberry Pi)
    • NXP i.MX 6/8(Cortex-A 系列)
    • Allwinner H3/H5(电视盒子芯片)
    • Rockchip RK3399(安卓平板/开发板)

    3. STM32(MCU) vs 树莓派(MPU)

    4. 为什么导弹、工业控制用 MCU?

    1. 高实时性:MCU 上电即运行,而 MPU 需要加载操作系统,延迟高
    2. 功耗低:MCU 可以低功耗运行,而 MPU 需要较大功耗运行。
    3. 稳定可靠:MCU 运行裸机或 RTOS,没有 OS 崩溃的风险

    总结:

    • MPU 适合需要操作系统、复杂运算(如 AI、Linux 设备)
    • MCU 适合实时控制、低功耗应用(如 STM32 控制电机、传感器)

    4.讲一下Linux中断分为上半部和下半部的原因?

    在 Linux 内核中,中断处理被分为 上半部(Top Half)下半部(Bottom Half)

    主要目的是提高系统响应速度,减少中断禁用时间,避免影响系统的实时性。

    1. 为什么要分为上半部和下半部?

    中断处理的挑战:

    1.中断发生时,CPU 需要立即响应

  • 硬件设备(如键盘、网卡、定时器等)触发中断时,CPU 需要尽快执行中断处理程序(ISR),否则可能会错失重要事件。
  • 2.中断处理会影响其他任务

  • 如果中断处理时间过长,会导致CPU 不能执行其他任务,影响系统性能。
  • 3.中断是优先级最高的任务

  • 中断处理程序不能被进程调度,一旦中断处理时间过长,会影响整个系统的调度。
  • 4.解决方案:

    • 上半部(Top Half)快速执行关键任务,完成最小化的硬件操作,然后尽快返回,释放 CPU。
    • 下半部(Bottom Half):在稍后的合适时间,延迟执行较慢的任务,降低系统负担。

    2. 上半部(Top Half)

    主要作用:

    • 处理紧急的硬件相关任务,确保系统快速响应中断
    • 代码通常执行极少的操作,然后立即退出。

    典型任务:

    • 读取中断状态(ISR 读取寄存器)
    • 清除中断标志,防止重复触发
    • 将数据存入缓冲区
    • 触发下半部处理

    上半部的限制:

    ✅ 不能使用 sleep()(不能阻塞)

    ✅ 不能访问 用户空间(只能访问内核空间)

    ✅ 不能执行 耗时操作(如磁盘 I/O、复杂计算)

    3. 下半部(Bottom Half)

    主要作用:

    • 执行耗时的任务,但不影响中断响应速度。
    • 避免在中断上下文中执行过长的操作,以提高系统性能。

    典型任务:

    • 数据处理(如网络包解析)
    • 唤醒等待的进程
    • 调用文件系统、磁盘 I/O
    • 复杂计算

    Linux 实现下半部的方法:

    1. 软中断(SoftIRQ)适合高吞吐量任务(如网络协议栈、定时器)
    2. 任务队列(Tasklet)适合较短的任务,可以在多个 CPU 上并行执行
    3. 工作队列(Workqueue)适合能睡眠的任务,在进程上下文中执行

    4. 例子:网络数据包处理

    假设网卡收到数据包,触发中断:

    上半部(ISR)

  • 读取网络芯片寄存器,获取数据包地址
  • 触发 SoftIRQ,让下半部执行
  • 尽快返回,不影响其他中断
  • 下半部(SoftIRQ / Workqueue)

  • 解析数据包
  • 将数据交给 TCP/IP 协议栈
  • 唤醒用户态进程(如 socket 读取数据)
  • 总结

    🚀 结论:分离上半部和下半部可以减少 CPU 负担,提高系统的实时性和响应速度。

    5.如果给线程访问加锁,是在驱动层实现还是应用层实现?

    回答:可以在驱动层实现,也可以在应用层实现,具体取决于需求和并发控制的粒度。

    • 驱动层加锁:适用于多个进程/线程访问同一个驱动资源(如设备寄存器、共享内存)。
    • 应用层加锁:适用于多个线程访问同一资源,但不涉及内核驱动(如数据库、全局变量)。

    1. 在驱动层实现加锁(

    剩余60%内容,订阅专栏后可继续查看/也可单篇购买

    作者简介:仅用几个月时间0基础天坑急转嵌入式开发,逆袭成功拿下华为、vivo、小米等15个offer,面试经验100+,收藏20+面经,分享求职历程与学习心得。 专栏内容:这是一份覆盖嵌入式求职过程中99%问题指南,详细讲解了嵌入式开发的学习路径、项目经验分享、简历优化技巧、面试心得及实习经验,从技术面,HR面,AI面,主管面,谈薪一站式服务,助你突破技术瓶颈、打破信息差,争取更多大厂offer。

    全部评论

    相关推荐

    ###&nbsp;3.25一面-&nbsp;本科学的不是计科,主要是什么原因-&nbsp;本科的时候接触过计科的课程吗?学过什么相关的课-&nbsp;目前为止觉得挑战最大的一门课是什么?是如何解决的?-&nbsp;在项目中遇到的最大挑战是什么?是如何解决的?-&nbsp;有没有做过嵌入式相关的一些项目?具体有哪些?-&nbsp;有没有接触过arm的板子?-&nbsp;有没有在单片机上进行过开发?-&nbsp;有没有学过微机原理?数电模电?-&nbsp;对于linux的了解深吗?有没有学过内核相关的知识?-&nbsp;讲一下项目的背景,为什么要做这个项目?-&nbsp;这个项目框架有点大,除此之外有没有做过更小的一些项目?####&nbsp;反问环节部门加班强度大吗?**前几年比较大,这两年好一些**大概的上班时间?**早上9点半,6点下班,晚上最多加班到9点左右,但不强制加班,周末双休**暑期转正的hc多不多?**不卡暑期转正名额,只要符合要求都可以转正**部门晋升路径如何?**刚进公司都是O13工资,最高可以升到O19(专家级),主要看绩效,不和工龄绑定**对于我的建议:**多学习一下linux内核,选一个具体的方向进行钻研一堆问题我都回答说没有,本来以为要挂了,结果还能进二面###&nbsp;3.28二面自我介绍介绍项目背景项目架构,项目部件(某些功能是怎么实现的)项目看起来很大,你的工作是哪部分?有没有做过一些优化的工作?讲一下另外一个大模型的项目(可能不是他的领域,就没怎么问)###&nbsp;没有八股,直接反问环节**部门对实习生的培养:**&nbsp;根据base和岗位分配一个合适的方向,分配导师,两个月做一个小项目**部门转正率:**&nbsp;没法回答我,因为可能分配到不同部门,每个部门不一样**面试表现以及技术栈建议:**&nbsp;两个项目有点极端,一个很工程一个很研究,建议能把ai和底软结合起来**对实习生的要求:**&nbsp;没什么要求,能把自己做的东西说明白就行**后续流程推进:**&nbsp;两周内会有结果(感觉应该用不了这么久)###&nbsp;4.3&nbsp;hr面自我介绍一下本科是其他专业的,为什么要转计算机?高考的时候选专业是怎么想的?本科的专业,对你学习计算机有什么帮助吗?考研还是保研?分享一下考研的时候的一些经验对考研取得的成绩满意吗?中间gap了一年是怎么想的?考公和考研是怎么选择的?如何说服爸妈支持你的决定?考研一共花了多长时间?读研后遇到的最大的困难是什么?怎么解决的?哪个项目最满意?为什么?职业规划是怎么样的?手上有几个offer?不同offer如何选择?对于实习薪资的预期是多少?####&nbsp;反问环节oppo对实习生的培养规划大概多久能出结果?**两周内,结果到了4.26了还在泡池子,网站还是显示hr面试已完成**
    点赞 评论 收藏
    分享
    05-14 11:14
    已编辑
    门头沟学院 硬件开发
    查看6道真题和解析
    点赞 评论 收藏
    分享
    评论
    12
    49
    分享

    创作者周榜

    更多
    牛客网
    牛客企业服务