卓驭嵌入式底软面试经验&&零跑BMS软件工程师
卓驭
自我介绍
1介绍项目
2自己负责哪一部分的
3项目当中遇到的难点以及怎么解决的
4在项目当中学习到了什么
讲一下什么是优先级反转
定义
优先级反转是指:
一个高优先级任务等待一个被低优先级任务持有的资源,而此时中等优先级任务不断运行,导致高优先级任务无法获得资源,从而“反转”了原有的调度优先级逻辑。
例子
| 任务 | 优先级 | 说明 |
|---|---|---|
| Task_H | 高 | 要访问共享资源 A |
| Task_M | 中 | 与资源 A 无关,做其他事 |
| Task_L | 低 | 正在使用共享资源 A |
执行过程:
1.Task_L 获取了 互斥锁 Mutex_A;
2.此时 Task_H 准备运行,也要访问资源 A;
3.Task_H 被挂起,等待 Task_L 释放 Mutex;
4.在这期间,Task_M 不断运行;
5.Task_L 被饿死,不能释放 Mutex,Task_H 也永远等不到资源。
➡️ 高优先级任务 Task_H 被中优先级任务 Task_M 间接阻塞了
注意情况
在实时系统中,任务优先级的调度顺序非常关键;
如果高优先级任务不能及时运行,可能导致系统崩溃或硬件故障(如航天器控制、汽车安全系统);
历史上最著名的案例:
NASA “火星探路者”号因优先级反转导致系统频繁重启
如果指针执行++操作,指针移动了几个字节
指针执行 ++ 操作时,它会移动**sizeof(所指类型)**个字节。
1 | |
讲一下常量指针和指针常量
常量指针(const int *ptr)是指指向常量的指针,不能通过指针修改值,但可以改变指针的指向;指针常量(int *const ptr)是指自身是常量的指针,不能改变指向,但可以通过它修改指向地址上的值;如果写成 const int *const ptr,则指针和它指向的数据都不能修改。
讲一下静态变量和静态函数
静态变量
本质与存储特性
在 C 语言中,使用 static 修饰的变量具备静态存储期(Static Storage Duration),即:
编译时分配内存,程序整个生命周期内保持存在,直到程序结束才释放。
它与变量的作用域无关 —— 即使定义在局部作用域中,也不会在函数退出时被销毁。
作用域与可见性
若在函数内部声明,变量作用域仍然是该函数本地,但生命周期变成全局;
若在文件级作用域声明(即不在任何函数中),static 限定其链接属性为内部链接,使该变量只能在本文件中访问,防止外部符号冲突。
初始化行为
静态变量在程序加载时由系统初始化(未显式赋值时默认初始化为 0);
它只会在程序启动阶段初始化一次,即使函数被调用多次,也不会重复初始化。
典型应用场景
记忆函数执行状态或计数值(如单例、自增 ID);
限制全局变量的可见性,实现模块封装;
在驱动开发中,维护设备状态结构体的静态引用。
静态函数
本质:链接属性为内部链接
函数默认具有外部链接性(external linkage),可以被其他文件引用(通过 extern)。
而 static 修饰函数后,会将其链接属性修改为 内部链接(internal linkage),即:
编译器在链接阶段不会将该符号导出给其他编译单元(.o 文件),只能在本文件内解析。
作用域与可见性
静态函数的作用域限制在当前编译单元(源文件)内;
不能通过其他模块的 extern 声明来访问,提高封装性和模块隔离性;
编译器对静态函数有更多优化空间(如内联、去除未引用的函数)。
典型应用场景
实现模块私有函数,防止命名冲突;
在设备驱动、协议栈、库开发中,常用 static 函数将接口函数与实现函数解耦;
与 static 变量配合,实现静态私有状态封装(类比面向对象中的 private 成员)。
零跑BMS软件工程师
简历
我面的是BMS部门,对我的简历问的比较细,就问了一个八股文,经典就是这个项目背景是什么,你是怎么做的,遇到什么困难,学到什么。
1.你项目用到哪些驱动
2.你这个项目驱动是你自己写的吗
3.FreeRTOS是你自己移植的吗
ARM内核单片机上电到main函数之前做了什么工作
ARM Cortex-M 单片机上电后,核心会首先从向量表中读取初始 SP 和复位地址,进入启动代码中的 Reset_Handler。启动流程包括初始化堆栈、复制 .data 段到 RAM、清空 .bss 段、配置系统时钟(调用 SystemInit)、初始化 C/C++ 运行环境,最终跳转到 main()。这个流程由汇编启动文件和链接脚本共同驱动,是整个系统可靠启动的基础。
[1] 上电复位
↓
[2] CPU从向量表中取复位中断地址
↓
[3] 进入启动文件的 Reset_Handler(汇编写的 startup.s)
↓
[4] 初始化堆栈指针(MSP)
↓
[5] 初始化 .data 段(从 Flash 拷贝到 RAM)
↓
[6] 清空 .bss 段(全局未初始化变量清零)
↓
[7] 调用 SystemInit()(配置时钟、PLL、FPU等)
↓
[8] 调用 C 库初始化(可能包含构造函数)
↓
[9] 跳转到 main() 函数