(嵌入式面经)第11章 20+公司面经杂谈(二):影石insta360、汇川、星纵科技、金风科技

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

1. 在多线程中,其中一个线程调用在其他地方的函数时,函数的参数会存在哪里?

2. 写时复制技术有了解过吗,说下实际应用的案例?

3. 有没有遇到过栈爆炸的情况?

4.了解systick定时器吗,说说有什么作用?

5.有没有遇到SPI时钟不匹配或者乱序的问题,如何解决的?

6.CPU寄存器中哪几个比较关键?

7.FREERTOS线程切换中大概做了哪些工作?

8.看你有涉及内存管理,那你知道动态内存管理的方法,有哪些吗?

9. 在我们平常内存管理中,除了内存碎片,还会碰到什么难题吗?

10. 专栏订阅奖励(支持模仿)——个人创新点问答:你所设计的FreeRTOS PLUS中操作系统中是怎么去做/检测有没有内存泄漏的?

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

1. 在多线程中,其中一个线程调用在其他地方的函数时,函数的参数会存在哪里?

在多线程编程中,每个线程有自己独立的栈空间来存储函数的参数和局部变量。

因此,函数参数存储在调用该函数的线程的栈中,不会影响其他线程。

1. 线程栈的作用

多线程编程 中,每个线程都有自己的 独立栈,它的主要作用包括: ✅ 存储函数调用的局部变量(包括函数参数、局部变量、返回地址)。

维护函数调用状态(记录返回地址、执行上下文)。

支持递归调用(每次递归调用都会在栈上创建一个新的栈帧)。

2. 函数参数的存储

当线程调用一个函数时,函数的参数会被压入该线程的栈

  • 函数调用时,参数被存入栈,函数执行期间这些参数一直存在。
  • 函数返回后,栈上的参数会被销毁(栈帧弹出),释放给其他函数调用使用。

示例:线程调用函数,参数存储在栈上

value 的地址传递给 thread_func(),但 local_var 作为局部变量存储在线程的栈上。

3. 线程与栈的关系

(1)每个线程有独立栈

  • 每个线程在创建时分配独立的栈空间,用于存储局部变量和函数参数。
  • 即使多个线程调用同一函数,它们的局部变量和参数互不干扰
  • (2)线程栈的大小

    • 默认栈大小(Linux pthread):通常为 8 MB
    • 可调整栈大小

    4. 栈帧和函数调用

    函数调用时栈的变化

    每次函数调用都会创建一个新的栈帧,包含:

  • 函数参数
  • 局部变量
  • 返回地址
  • 示例:函数调用栈帧

    执行 funcB() 时,栈帧变化如下:

    | funcB() 栈帧 | // 进入 funcB() | funcA() 栈帧 | // 调用 funcA(),创建新栈帧,存储参数 5 | 局部变量 x |

    funcA() 执行完毕,其栈帧会被销毁,返回 funcB()

    5. 线程栈的线程安全性

    (1)为什么线程的栈是线程安全的?

    每个线程的栈是独立的,不会影响其他线程

    函数参数和局部变量在各自线程的栈上,互不干扰

    除非主动共享数据(如全局变量),否则栈上的变量是线程安全的

    (2)非线程安全的情况

    使用全局变量可能导致线程安全问题

    解决方案:使用互斥锁(Mutex)

    6. 结论

    函数的参数存储在调用线程的栈中,函数返回后该栈帧会被销毁

    每个线程拥有独立栈空间,确保线程调用相同函数时互不干扰

    局部变量和参数是线程安全的,但共享数据需要额外同步机制

    2. 写时复制技术有了解过吗,说下实际应用的案例?

    1. 什么是写时复制?

    写时复制(Copy-On-Write, COW) 是一种 内存优化技术,其核心思想是: ✅ 延迟数据复制,只有当数据被修改时才进行真正的拷贝。

    多个进程/线程可以共享相同的内存区域,避免不必要的复制,节省资源

    🚀 典型应用场景

    • 进程创建(Linux fork()
    • 字符串、数据结构(C++ std::string、智能指针)
    • 虚拟内存管理(内存页共享)

    2. 为什么需要写时复制?

    传统拷贝 方式中,当一个进程 调用 fork() 创建子进程 时,整个进程的内存都会被复制一遍

    这会 浪费大量内存,尤其是当子进程 只是读取数据,而不修改时,这部分拷贝完全没必要。

    写时复制的解决方案

    1. 初始时,父子进程共享相同的内存页(只读)
    2. 只有当子进程或父进程尝试修改数据时,才触发真正的内存拷贝(COW 机制)
    3. 这样只有被修改的页面才会被复制,避免不必要的拷贝,提高性能

    示例(写时复制)

    如果 B 修改 [ 共享页 2 ]

    🚀 只复制修改的页,而不是整个进程的内存,节省内存资源!

    3. Linux fork() 机制与 COW

    fork() 的默认行为

    • 在 Linux 中,fork()不会立即复制整个父进程的内存页,而是通过写时复制(COW)技术,父子进程共享相同的内存页
    • 只有在 父进程或子进程尝试写入数据 时,才会触发内存页的复制

    示例(C 语言 fork()

  • varfork()不会立即复制,父子进程 共享相同的内存页
  • 子进程修改 var = 20,触发 COW,复制 var 变量的内存页
  • 父进程仍然保持 var = 10,因为它没有修改该变量
  • 4. 写时复制在 C++ STL 容器中的应用

    在 C++ 中,某些数据结构(如 std::stringstd::vector)也使用 写时复制(COW) 来优化性能。

    C++ std::string 的 COW

    • str2 = str1不会立即复制内存,而是共享 str1 的数据
    • str2[0] = 'X'触发 COWstr2获得独立副本,避免修改 str1

    C++11 之后,标准库已移除 std::string 的 COW,改用 std::move() 进行高效拷贝。

    5. 写时复制的优缺点

    优点

  • 减少不必要的内存拷贝(只在写入时复制)。
  • 提高内存利用率(多个进程/对象共享相同数据)。
  • 提高性能(避免频繁拷贝,大幅提升 fork() 性能)。
  • 缺点

    1. 增加了额外的逻辑开销(需要标记哪些数据是共享的)。
    2. 多线程环境可能需要额外的锁机制(避免多个线程同时修改)。

    6. 写时复制 vs 深拷贝 vs 浅拷贝

    COW 适用于只读多、写入少 的场景,避免不必要的拷贝,提高效率

    结论

    写时复制(Copy-On-Write, COW)是一种优化内存使用的技术,主要用于进程 fork()、C++ STL、数据库等场景

    通过 COW,多个对象可以共享相同数据,只有在修改数据时才真正分配内存,避免不必要的复制

    COW 适用于"读多写少" 的场景,如 fork() 进程创建、共享 std::string 数据、数据库事务

    3. 有没有遇到过栈爆炸的情况?

    1. 什么是栈爆炸?

    栈爆炸(Stack Overflow) 发生在 程序调用栈空间被耗尽 时,通常导致 程序崩溃

    🚀 常见原因

  • 递归调用没有终止条件(无限递归)。
  • 递归深度过大(栈帧过多,占用栈空间过大)。
  • 局部变量过大(单个函数栈帧消耗过多内存)。
  • 多线程程序,每个线程栈大小有限
  • 2. 栈爆炸的典型场景

    (1)递归没有终止条件

    如果递归函数没有合适的终止条件,它会一直递归调用自己,直到栈空间耗尽。

    示例

  • 每次 recursive_function() 调用自身,都会在 栈上分配新的栈帧
  • 递归不会终止,最终超出系统分配的栈空间,导致栈爆炸(Stack Overflow)
  • 修正方法

    (2)递归层次过深

    即使递归有终止条件,但如果 递归深度过大,依然会导致栈空间不足。

    示例

  • 大多数系统默认的 栈大小 只有 几 MB,比如 Linux 默认 8MB。
  • 100,000 次递归调用,每次调用都会消耗栈空间,最终导致溢出。
  • 修正方法

    • 使用循环代替递归(尾递归优化)

    • 增大栈大小(Linux ulimit -s 命令)

    (3)局部变量过大

    局部变量 存储在栈上,如果分配的变量过大,会导致栈空间耗尽。

    示例

  • 默认栈大小可能只有 8MB,一次性分配 1MB 局部数组,多个调用可能导致溢出。
  • 局部数组应改为 malloc() 申请堆内存
  • 修正方法

    (4)多线程栈空间不足

    在多线程程序中,每个线程都有自己的 独立栈(通常比主线程小,如 1MB)。

    示例

  • 线程栈大小一般较小(1MB),创建多个线程或者局部大数组可能导致 栈空间耗尽
  • 局部变量过大可能导致单个线程崩溃
  • 修正方法

    3. 如何防止栈爆炸?

    (1)优化递归

    • 确保递归有终止条件
    • 使用循环代替深度递归(尾递归优化)
    • 使用动态编程减少递归深度

    (2)避免大局部变量

    • 使用 malloc() 分配大数组,而不是栈上分配
    • 释放不再需要的堆内存,避免泄漏

    (3)增加栈大小

    • Linux 通过 ulimit -s unlimited 取消限制
    • pthread 线程手动设置更大栈空间

    (4)检测栈使用

    • 在嵌入式系统中,使用 栈监视工具 检查栈溢出
    • 使用 GDB 断点跟踪递归调用栈。

    结论

    栈爆炸(Stack Overflow)通常由递归过深、局部变量过大、多线程栈不足导致

    避免无限递归,使用尾递归优化、动态编程或循环替代递归

    合理管理内存,避免大数组分配在栈上,必要时增加栈空间

    4.了解systick定时器吗,说说有什么作用?

    1. 什么是 SysTick?

    SysTick(System Timer)是 Cortex-M4 内核 内置的 24 位递减定时器,嵌入在 NVIC(Nested Vectored Interrupt Controller) 中。

    它的主要特点包括:✅ 24-bit 递减计数器(最大计数 2^24 - 1 = 16,777,215)。

    计数完毕触发中断,可用于 周期性任务(如 OS 时基)。

    固定时钟源(通常是 SYSCLK 或内部时钟)。

    2. SysTick 的主要作用

    (1)普通定时器

    • SysTick 可以用作普通定时器,例如 精确延时、定时触发任务

    (2)OS 时基

    • FreeRTOS、RT-Thread 等实时操作系统(RTOS) 需要一个 固定的时钟节拍(Tick),SysTick 常用于 提供 OS 心跳,驱动 任务调度

    示例

    SysTick 周期性触发中断 --> 增加系统 Tick 计数器 --> 触发任务调度

    3. SysTick 计时原理

    SysTick 计数方式为 向下递减

    1. 当 SysTick 计数器(CURRENT)从 Reload 递减到 0 时:触发 SysTick 中断。计数器 自动重新加载Reload 值,继续递减。
    2. SysTick 计数频率 = 系统时钟(SYSCLK) / 预分频系数

    4. SysTick 寄存器

    SysTick 主要有以下 三个寄存器

    配置寄存器

    5. SysTick 配置示例

    示例:使用 SysTick 实现 1ms 定时

  • SysTick 每 1ms 触发一次中断
  • SysTick_Counter 作为计数器,用户可以使用 Get_SysTick() 获取当前时间。
  • 6. SysTick 在 RTOS(FreeRTOS)中的作用

    (1)为什么 RTOS 需要 SysTick?

    • RTOS 任务调度 依赖于 时基(Tick Timer),SysTick 作为 时钟源RTOS 提供固定节拍
    • 任务切换发生在 SysTick 中断 里,每次触发时会判断是否需要 切换任务

    (2)SysTick 在 FreeRTOS 中的配置

    FreeRTOS 需要重定义 SysTick_Handler

    配置 FreeRTOS 的时钟

    7. SysTick 的优缺点

    优点

    1. 内核自带,无需额外硬件,占用资源少。
    2. 适用于定时任务、OS 时基、

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

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

    全部评论

    相关推荐

    2025-12-16 22:19
    已编辑
    南昌市第三中学 Java
    个人背景:27届本科 江西普通一本院校个人经历:小厂->用友->蔚来->美团->腾讯不知不觉已经有了五段实习经历,也快在外面漂泊一年半了,在今年也完成了两年前自己想进大厂的目标,可能在别人看来确实就是一段比较传奇的过程,一步一步都在向上走,也会有很多人来问我相关学习实习的一些问题,我看到了也会尽量去回复,但现在我想给大家说的并不是千篇一律的学习路线,而是我认为更为重要的——勇气与抉择。下面我来分享一下这些年的心路历程最初学习背景:我跟很多人一样,都是刚进入大学才开始接触计算机,也刚刚拥有自己的电脑,在刚开始学习的过程没有任何人来帮助我,给予我相关的指导,完全是自己摸索出来的一条学习路线,不会有如今这样有很多完善好的速成路线,而家里人都在想让我考研,似乎本科以我的学历就业是不现实的。我也很早意识到了学历对于我的限制,所以萌生出了大一就开始实习的想法,但这个想法在当时基本上是不存在。所有人都在抨击我(这里感兴趣的话可以看我最早发的帖子),有的人说本科想进大厂痴人说梦,有的人劝我以我的学历考研才是上策,有的人说我屁都不懂就来卷,总之我很难说去看到有支持的。我大一的时候还没卷成如今这样很多大一实习,当我想找到是否有跟我一样下定决心一步一步往上走的人,我当时是没有找到的,要么是秋招的哀嚎,要么就直接是零实习进大厂(现在我知道,这里所谓的普通学历0实习进大厂的水分有很多,排除真正意义上的运气和实力,其他基本上全是造假作弊,大家自己心知肚明,也要放平心态)这就导致了一个没有先例的情况,很多人也都是拿没有先例来抨击我,包括家里人也不支持我去实习,可能很多人的积极性就会下降,但我从来不会信所谓的不可能,如果没有先例,那我就会是第一个,他们不行,是因为他们没能力,他们坚持不下去。勇气是很重要的,当你发现你身边没有人像你一样,就很少会有人相信你,看好你,但好在,我不在乎。最初实习阶段:在最初3000沟通只有零星几个面试的时候,那感觉确实很不好受,沉没成本太大,得到的正反馈却太少,当时基本上都是一天学八个小时从来不间断,没有周末没有节假日,甚至过年我都在学习,这就导致我现在都会因为我周末偶尔休息的时候会有负罪感,我感觉已经是种病了,我也知道我也可以休息会但控制不了。当时我出去实习口袋里有1w块(这是我高中三年加大一一年存下来的,基本上是很抠很抠,一个月生活费有时候有一千多有时候就五六百,但也算得上是成功攒了一点钱)但第一次总会是很害怕,担心租房被骗,担心工作能力不行,担心被公司坑,担心学校原因导致不能实习等等,基本上在前面几段实习是根本不攒钱的,代课已经花了一万多,加上租房来回,基本上只能说堪堪不负支出,后来远赴北京,作为一个南方人,有很多不适应的地方,但现在回过头来一想,已经在北京呆了一年多了。我知道很多人要么担心学校因素,要么担心赚的还没花的多,种种因素导致了实习的困难,我也很害怕,我的钱会不会最终全部打水漂,学校会不会爆雷,我以后还能顺利实习吗等等。但对于我来说,我能对自己狠下心,我能接受通勤时间一个半小时只为节省那么几百块的房租钱,我能控制自己的消费的欲望,我能每个月大把大把把钱给代课,这可能就是我能够初期实习顺利的原因,这需要勇气,也需要对自己狠。实习中的抉择:在有了两段实习经历后,我的目标就朝着大厂进发,在去蔚来的中途,我oc了七八家中小厂公司,这里面不乏一些待遇极其优越的公司(有一家我真的差点就去了),但我最终还是都拒了,因为我清楚的明白想往上走的,只有公司title会帮你说话,没有人有义务理解你的困难你的坚持,好在最后去了蔚来,也算如愿以偿。从蔚来到美团倒是没有过多纠结,因为在最开始的梦中情厂就是美团,但从美团去腾讯这个决定或许是我人生中的转折点。美团多次挽留我,帮我沟通问hr,基本上就是一定能转暑期然后成功转正,仿佛这年薪40w的工作已经触手可得,所以在拿到腾讯offer的那一刻并没有多高兴,因为我意识到这可能是我此生最接近大厂的一次机会,可能大部分人都会选择留在美团,我也认为这一定是一个好的选择。我能够走到如今,是永远相信自己的判断,我的每一步都是在赌一个好的未来,只不过,这次赌注大了点而已,或许未来我再也进不了这些所谓的大厂,但我赌的不是选择错对,我赌我不后悔。所谓信念支撑:都说人要为自己而活,但我或许做不到,毕竟我身处人情社会,有许多爱我的人在等着我成长,我也不能接受因为能力而再次放弃一段感情,最近喜欢一段歌词:爱我的人相信我我一直在努力改变所有失败为你们而存在爱我的人感谢你你们的爱就算人生不是精彩我也要勇敢的姿态最后的最后,我想给大家传递的从来都不是一个普通学历进入大厂的意气风发,我想给大家传递的,是一股相信自己能够向上的信念和可能性。在没有打比赛能力,没有开源能力,没有学历等各个限制下,我帮大家试出了一条能够向上的路。如果没有先例,那我会是第一个。我们不需要弄虚作假,只靠自己一步一步脚踏实地,哪怕慢一点,不赌自己是否成功,只赌自己不后悔,问心无愧。最后送给大家,也送给自己一段话结束2025:生活可能没你想的那么好,也不会像你想的那么糟,人的脆弱和坚强,都超乎了你的想象,有时候可能脆弱的一句话就泪流满面,有时候你发现自己咬咬牙已经走了很长的路了
    等闲_:感觉咱们双非的同学都有一个共性,想证明双非也是能进大厂的,我之前所有的标签都喜欢带着双非,仿佛这样可以像别人证明自己的实力,现在我却不再想证明双非到底能不能进大厂,我的生活的所有者是我自己,享受生活,接受结果
    2025年终总结
    点赞 评论 收藏
    分享
    评论
    7
    21
    分享

    创作者周榜

    更多
    牛客网
    牛客网在线编程
    牛客网题解
    牛客企业服务