8.27  一面  1h多
先问实习内容和几个项目细节


1.谈谈你对设备驱动框架的理解

我认为设备驱动框架是操作系统制定的一套标准化分层开发架构,采用分层设计实现软硬件解耦。框架统一规定了设备注册、初始化、读写、中断等通用接口,开发者只需完成底层硬件适配即可。上层应用无需接触底层硬件细节,直接调用通用接口操控设备,极大提升代码可移植性与开发效率,也方便系统统一管理各类外设资源。

分层思想(重点)

  1. 上层:应用层只调用系统提供的标准读写、打开、关闭接口,完全不懂硬件寄存器、时序电路。
  2. 中层:驱动框架层操作系统内核提供统一骨架,定义固定函数接口、设备注册机制、中断管理、设备节点管理,规定驱动该怎么写、怎么挂载。
  3. 下层:硬件适配层开发者只需要按照框架规定,填充硬件寄存器配置、引脚初始化、时序读写、中断服务等底层硬件逻辑即可。

2.介绍计算机网络模型和各协议

一、两大模型

  1. OSI 七层模型(理论)应用层,表示层,会话层,传输层,网络层,数据链路层,物理层
  2. TCP/IP 五层模型(实际在用)应用层,传输层,网络层,数据链路层,物理层

数据链路层:ARP 把IP 地址转换成对应的MAC 物理地址

网络层:IP 分配主机地址(

                 A 类私有10.0.0.0 ~ 10.255.255.255

                B 类私有172.16.0.0 ~ 172.31.255.255

                C 类私有(最常用)192.168.0.0 ~ 192.168.255.255

传输层:TCP、UDP

应用层:HTTP/HTTPS 网页访问、FTP/SFTP 文件传输、DNS 域名解析、SSH 远程登录 Linux

3.有了解过用户态的驱动开发吗

用户态驱动就是把硬件操作逻辑放在应用层用户空间实现,不侵入内核,不用编写内核模块。

串口控制机械臂直接打开 /dev/ttyUSBx 读写数据,应用层收发指令,纯用户态


4.介绍dma

DMA 直接存储器访问

不经过 CPU,直接让外设和内存之间高速搬运数据,解放 CPU

DMA 是直接存储器访问,能够在外设与内存之间不经过 CPU直接完成数据搬运,大幅减轻主控压力,提升传输效率,在串口、ADC、屏幕等大批量数据传输场景经常使用。

DMA 和中断区别?

中断是少量数据、事件通知;DMA 是批量高速搬运数据


5.有用gdb调试过什么具体问题吗

系统报错只能告诉我们程序崩溃了,但无法定位具体代码行、无法查看变量、无法看到函数调用栈

而 GDB 可以查看崩溃现场、查看堆栈、追踪变量、断点调试,尤其能解决段错误、死循环、死锁、逻辑异常这类系统不会提示的问题。

程序卡死、死循环


6.有了解日志打印这块具体的实现过程吗

定义日志等级,自动获取代码位置信息、获取标准时间戳、实现可变参数格式化

统一日志格式拼接,指定输出目的地、做多线程安全处理、封装对外调用宏函数、底层最终调用

日志打印具体实现分为十步,首先定义日志等级做日志过滤,借助编译器内置宏自动获取代码文件、行号与函数信息;调用系统时间接口拼接时间戳;通过可变参数解析格式化内容,组合成标准日志格式;支持控制台和本地文件两种输出方式;加入互斥锁解决多线程日志乱序问题;再实现日志文件分割管理,最后封装成简易调用宏,底层依靠文件读写系统调用完成最终输出。


7.我的硬件调试经历,主要是哪个方向的

的硬件调试主要偏向嵌入式 Linux 机器人方向,擅长总线通信调试、机械臂运动硬件调试、视觉相机设备调试,以及上下位机整体软硬件联调,擅长用仪器排查时序、通信、硬件电路与数据交互类问题。


8.确认fpga的水平(后面看)

FPGA 是能反复改写硬件电路的高速可编程芯片,主打快、稳、实时强,多用于机器人、工控、AI 硬件本地化部署。


9.有了解过用户态程序中性能优化的手段吗,有了解过内存池吗
 

用户态 = 应用程序代码(C/C++/Go/Java 都算),不涉及内核。

1. 计算优化

  • 减少循环嵌套、减少重复计算
  • 用更高效的算法(O (n) → O (logn))
  • 避免频繁的函数调用、虚函数开销

2. 内存优化(最重要)

  • 减少内存拷贝(零拷贝)
  • 避免频繁 malloc/free(用内存池)
  • 提高缓存命中率(局部性原理)
  • 对齐内存、减少内存碎片

3. I/O 优化

  • 异步 I/O 代替阻塞 I/O
  • 批量读写、减少系统调用
  • 使用缓冲区、减少磁盘 / 网络访问次数

4. 并发 / 锁优化

  • 减少锁范围、用无锁数据结构
  • 用原子操作代替互斥锁
  • 协程 / 线程池避免频繁创建销毁

5. 系统调用优化

  • 减少 read/write/open 等系统调用(非常耗性能)
  • 用户态批量处理,不要频繁进内核态

1.为什么要用内存池?

  • malloc/new系统调用,频繁申请释放非常慢
  • 频繁申请会产生内存碎片

2. 内存池解决什么问题?

  1. 提速:避免频繁系统调用
  2. 减少碎片:连续内存,利用率更高
  3. 可控:自己管理,避免内存泄漏
  4. 稳定:高并发场景不抖动

(抖动 = 程序运行速度忽快忽慢、帧率不稳、响应时快时慢、卡顿波动)

10.c和c++区别

C 是面向过程的结构化语言,简洁轻量,多用于底层硬件开发;

C++ 在 C 基础上扩展面向对象特性,支持封装继承多态,开发大型复杂项目更合适,语法更丰富,工程化能力更强。

  1. 头文件
    • C:#include <stdio.h>
    • C++:#include <iostream>,用std::cout打印
  2. 输入输出
    • C:printf / scanf
    • C++:cout / cin
  3. 字符串
    • C:char[] 字符数组
    • C++:string 类,好用不用手动处理结束符
  4. 内存管理
    • C:malloc / free
    • C++:new / delete,自动调用构造析构
  5. 函数
    • C:不支持函数重载
    • C++:支持函数重载、默认参数
  6. 结构体
    • C:结构体不能放函数
    • C++:结构体 = 类,可写成员函数
  7. 关键字
    • C++ 独有:class、public、private、protected、this、inline、template、namespace

    8. 编程思想最大差距

  • C:怎么做(步骤)拆成一个个函数,按顺序执行
  • C++:是谁做(对象)封装、继承、多态三大特性

11.介绍RAII

在构造函数里申请资源,在析构函数里自动释放资源,依靠对象生命周期自动管理资源,不用手动写释放代码。

RAII 是 C++ 独有的资源管理机制,利用类的构造函数获取资源、析构函数释放资源,依靠对象生命周期自动管理内存、锁、文件、网络等资源,无需手动释放,从语法层面杜绝资源泄漏。

12.智能指针(前面已介绍)

常见问题

  1. 野指针:智能指针生命周期结束自动置空,大幅减少野指针
  2. 内存泄漏:RAII 自动释放,基本杜绝
  3. 循环引用:两个 shared_ptr 互相持有 → 计数永远不为 0 → 泄漏,用 weak_ptr 打破
  4. 能不能混用裸指针和智能指针?不建议,容易二次释放崩溃

智能指针基于 C++ RAII 思想封装裸指针,实现堆内存自动管理;日常优先用unique_ptr,多线程多对象共享资源用shared_ptr,搭配weak_ptr解决循环引用问题。


13.用c语言能否实现面向对象

C 是面向过程语言,无class、无继承多态原生关键字,靠结构体 + 函数指针模拟 OOP 三大特性。

1.怎么模拟

  1. 封装,struct存成员属性,把操作该数据的函数指针放进结构体,外部只调用接口,隐藏内部实现。

  2. 继承,把子类结构体第一个成员放父类结构体,实现内存布局复用,达成继承效果。

  3. 多态,通过函数指针数组 / 重写函数指针,不同结构体绑定不同实现函数,调用同一接口执行不同逻辑,模拟虚函数多态。

14.如何用c语言实现RAII

C 语言没有原生构造析构,无法原生实现 RAII,只能手动模拟

  1. 编写资源初始化函数模拟构造,完成内存、句柄、锁等资源申请;
  2. 编写统一资源释放函数模拟析构;
  3. 通过 goto 设置统一释放入口,保证函数正常退出、异常返回时必然执行释放逻辑
  4. 借助代码作用域划分生命周期,复刻 C++ RAII

15.c可以调用c++吗,如何实现

C 可以调用 C++,但必须用 extern "C" 修饰 C++ 接口,禁止使用 C++ 特有的语法(类、重载、模板),对外暴露纯 C 风格函数;若涉及 C++ 对象,则用 void 指针隐藏对象实现,让 C 语言可以正常调用。
  

// 必须加 extern "C",让C能找到
#ifdef __cplusplus
extern "C" {
#endif

// 纯 C 风格接口
int add(int a, int b) {
    return a + b;
}

#ifdef __cplusplus
}
#endif
#include <stdio.h>

// 声明 C++ 暴露的函数
int add(int a, int b);

int main() {
    int res = add(10, 20);
    printf("结果:%d\n", res);
    return 0;
}


16.经典接雨水原题(看)
   

9.16  二面  1.5h  


1.介绍实习内容中的整体方案,之后问了非常多问题,代码框架、必要性、细节实现、适用范围、对外接口、提交的代码量、具体遇到的技术难题


2.问完这段问下一段实习内容,每个点都会问的很细


3.继续问在校竞赛项目,确认团队人数继续问项目中的具体实现


4.分享其中一段有软硬结合协调的经历


5.介绍gdb使用经历,平时调试的主要手段有


6.了解设备树吗,有哪些技术点,如何实现这种驱动或设备注册加载的
    

设备树 (Device Tree):Linux 内核用来描述板级硬件资源的树形文本文件,后缀.dts.dtsi。作用:把硬件信息从内核代码剥离,不用改内核源码、重新编译内核,只改设备树就能适配不同硬件。

以前:平台硬件差异全写在内核 C 代码里,换一块板子就要改代码、编译内核,臃肿难维护。

现在:硬件引脚、中断、时钟、寄存器地址、总线、电源全部写进设备树,内核统一解析匹配驱动


7.一道二叉树的题,最下层的两节点之间可能会出现空节点,计算二叉树的最大宽度(包含空节点)

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐