龙旗科技Linux驱动开发 一面试题
1. 做一个自我介绍,重点讲一下你的项目经历
参考回答方式:
- 基本信息:我叫XXX,XX大学XX专业,预计XX年毕业,主修嵌入式系统方向
- 技术背景:熟练掌握C/C++、Linux系统编程、数据结构算法,有X年的学习/实习经验
- 项目经历: 项目一:基于STM32的智能监测系统,负责传感器驱动和数据采集模块,使用DMA+RTOS实现多任务调度,解决了实时性问题项目二:嵌入式通信系统,设计了可靠的通信协议,实现了CRC校验和超时重传机制
- 个人特点:学习能力强,能快速上手新技术,对底层驱动开发有浓厚兴趣,能吃苦耐劳
控制在2-3分钟
2. volatile关键字的作用是什么?什么场景下使用?
答案要点:
1. volatile的作用
- 告诉编译器该变量可能被程序外部因素改变(硬件、中断、其他线程),每次访问必须从内存读取
- 禁止编译器优化:不会把多次读取优化为一次,不会调整读写顺序,不会使用寄存器缓存
2. 典型使用场景
- 硬件寄存器访问:
volatile uint32_t *reg = (uint32_t *)0x40000000;硬件可能随时改变寄存器值 - 中断修改的全局变量:中断中修改flag,主程序轮询flag,必须声明为volatile
- 多线程共享变量:保证可见性(但不保证原子性)
- 状态寄存器轮询:
while(!(status_reg & READY_BIT));防止被优化为死循环
3. volatile的局限性
- 不保证原子性,
count++仍然不是线程安全的 - 不能代替锁,多线程同步还需要互斥锁
- 不能防止指令重排序(需要内存屏障)
3. memcpy和memmove的区别?如何实现安全的内存拷贝?
答案要点:
1. 核心区别
- memcpy不处理内存重叠,源和目标重叠时结果未定义
- memmove处理内存重叠,通过判断拷贝方向保证正确性
- 性能上memcpy稍快,因为不需要判断重叠
2. 内存重叠问题
- 如果src在前,dest在后,从前往后拷贝会覆盖未拷贝的源数据
- memmove解决:dest<src从前往后拷贝,dest>src从后往前拷贝
3. 简单实现
void *my_memmove(void *dest, const void *src, size_t n) {
char *d = (char *)dest;
const char *s = (const char *)src;
if (d == s || n == 0) return dest;
if (d < s) {
for (size_t i = 0; i < n; i++) d[i] = s[i];
} else {
for (size_t i = n; i > 0; i--) d[i-1] = s[i-1];
}
return dest;
}
4. 驱动开发应用
- 内核空间拷贝不确定是否重叠时用memmove更安全
- 用户空间和内核空间拷贝要用copy_from_user/copy_to_user
- DMA传输要注意缓存一致性,可能需要flush cache
4. 什么是大端和小端?如何判断系统字节序?
答案要点:
1. 定义
- 大端(Big-Endian):高位字节存储在低地址,如0x12345678在内存中是
12 34 56 78 - 小端(Little-Endian):低位字节存储在低地址,如0x12345678在内存中是
78 56 34 12 - 网络字节序是大端,x86/ARM通常是小端
2. 判断方法
int is_little_endian() {
int num = 1;
char *p = (char *)#
return (*p == 1); // 低位字节在低地址则为小端
}
3. 字节序转换
- 网络编程:htonl/htons(主机序转网络序)、ntohl/ntohs(网络序转主机序)
- 手动转换:
(val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24)
4. 实际应用
- 文件格式(BMP、JPEG)有固定字节序
- 网络协议(TCP/IP)使用大端
- 驱动读写硬件寄存器要查数据手册确认字节序
5. 二叉树的三种遍历方式?手写中序遍历
答案要点:
1. 三种遍历
- 前序(Pre-order):根-左-右
- 中序(In-order):左-根-右(二叉搜索树中序遍历结果有序)
- 后序(Post-order):左-右-根(常用于释放树内存)
2. 中序遍历(递归)
typedef struct TreeNode {
int val;
struct TreeNode *left, *right;
} TreeNode;
void inorder(TreeNode *root) {
if (root == NULL) return;
inorder(root->left);
printf("%d ", root->val);
inorder(root->right);
}
3. 中序遍历(非递归)
void inorder_iterative(TreeNode *root) {
TreeNode *stack[100];
int top = -1;
TreeNode *curr = root;
while (curr != NULL || top >= 0) {
while (curr != NULL) {
stack[++top] = curr;
curr = curr->left;
}
curr = stack[top--];
printf("%d ", curr->val);
curr = curr->right;
}
}
6. cons
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
嵌入式面试八股文全集 文章被收录于专栏
这是一个全面的嵌入式面试专栏。主要内容将包括:操作系统(进程管理、内存管理、文件系统等)、嵌入式系统(启动流程、驱动开发、中断管理等)、网络通信(TCP/IP协议栈、Socket编程等)、开发工具(交叉编译、调试工具等)以及实际项目经验分享。专栏将采用理论结合实践的方式,每个知识点都会附带相关的面试真题和答案解析。

