学习计划
寒假两个月学习规划(Linux驱动方向)
目标:研二秋季投递大厂实习前,达到面经中Linux驱动/BSP方向除手撕代码外的面试水平
周期:8周(约2个月)
一、现状与差距分析
已有基础
| 模块 | 掌握程度 | 说明 |
|---|---|---|
| STM32 | 扎实 | GPIO/UART/I2C/DMA/定时器/中断/时钟,寄存器级理解 |
| FreeRTOS | 扎实 | 任务切换/PendSV/双栈/内存管理/调度机制 |
| xv6 | 扎实 | 5个lab完成,页表/trap/COW fork,OS内功好 |
| IMX6ULL | 中等 | U-Boot/LCD/I2C/SPI/RTC驱动,还没进入Linux驱动框架 |
| RK3399项目 | 实战级 | TypeC OTG切换/MIPI转LVDS屏幕适配,两年工业经验 |
面经考点差距表
| 模块 | 面经出现频率 | 当前状态 | 需要补的内容 |
|---|---|---|---|
| C语言基础 | 几乎每场 | 有实战基础 | static/volatile/const细节、内存对齐、指针高级用法 |
| C++11 | ~60% | 薄弱 | move/lambda/智能指针/STL容器 |
| Linux驱动框架 | 核心必考 | 刚到U-Boot | 字符设备→platform→设备树→子系统驱动 |
| 内核内存管理 | ~70% | xv6有底子 | kmalloc/vmalloc/slab/buddy/mmap |
| 进程与调度 | ~60% | xv6有底子 | CFS、task_struct、IPC |
| 同步机制 | ~50% | FreeRTOS有概念 | mutex/spinlock/信号量/RCU |
| U-Boot | ~40% | 已学过 | 补bootcmd/bootargs/启动流程细节 |
| ARM体系结构 | ~40% | 有基础 | 异常等级、MMU、Cache一致性 |
| 网络TCP/IP | ~35% | 薄弱 | 三次握手/四次挥手、socket、select/poll/epoll |
| 调试工具 | ~30% | 一般 | GDB、ftrace、coredump分析 |
| Makefile/Shell | ~25% | 一般 | Makefile语法、Shell脚本基础 |
二、8周详细规划
第1-2周:Linux驱动入门 + C语言查漏
Linux驱动(每天主力时间)
- 字符设备驱动开发
file_operations结构体cdev、register_chrdev、alloc_chrdev_region- 设备号(主设备号/次设备号)
class_create/device_create自动创建设备节点
- 动手写一个LED字符设备驱动,从用户空间
open/write/ioctl控制 - 设备树基础
- 设备树语法(DTS/DTB/DTC)
- 节点、属性、
compatible字段 - 设备树和驱动的匹配机制(
of_match_table) of_系列API:of_find_node_by_path、of_property_read_u32等
C语言查漏(每天1小时)
-
static的三种用法- 修饰局部变量:生命周期延长到程序结束
- 修饰全局变量:限制作用域到本文件
- 修饰函数:限制函数可见性到本文件
-
volatile的作用和使用场景- 防止编译器优化,每次从内存读取
- 场景:硬件寄存器、中断服务程序中的共享变量、多线程共享变量
-
const与#define的区别- 类型检查、作用域、调试可见性
- 指针专题
- 指针与数组的区别
- 函数指针、回调函数
- 指针的指针(二级指针)
void*的使用
- 结构体内存对齐
- 对齐规则、
#pragma pack sizeof计算
- 对齐规则、
- 大小端(Big-endian / Little-endian)
- 判断方法、转换函数
-
malloc的实现原理brk/mmap系统调用- 内存碎片问题
第3-4周:platform驱动 + 设备树深入 + 内核内存管理
Linux驱动
- platform总线驱动模型(面经必考)
platform_device和platform_driverprobe和remove函数- 资源获取:
platform_get_resource
- 设备树与驱动的完整流程
- pinctrl子系统:引脚复用配置
- gpio子系统:
of_get_named_gpio、gpio_request、gpio_direction_output
- I2C子系统驱动(重点学框架,裸机协议你已经会了)
- I2C核心层 / 适配器层 / 设备驱动层
i2c_driver、i2c_clienti2c_transfer/i2c_smbus_read_byte_data
- SPI子系统驱动
spi_driver、spi_devicespi_sync/spi_async
内核内存管理
- 物理内存管理
- buddy system(伙伴系统):页的分配与合并
alloc_pages/free_pages
- slab分配器
- 解决buddy system小内存分配效率问题
kmem_cache_create/kmem_cache_alloc
-
kmallocvsvmallocvskmapkmalloc:物理连续,基于slab,用于小内存vmalloc:虚拟连续物理不一定连续,用于大内存kmap:映射高端内存
-
mmap原理- 用户空间与内核空间的内存映射
remap_pfn_range
-
copy_from_user/copy_to_user- 为什么不能直接用
memcpy - 页错误处理
- 为什么不能直接用
- 用户空间虚拟内存布局
- 代码段 / 数据段 / BSS / 堆 / 栈 / mmap区域
- 4GB虚拟地址空间划分(3G用户 + 1G内核)
第5-6周:进程调度 + 同步机制 + C++11
内核进程与调度
-
task_struct结构体关键字段- 进程状态(TASK_RUNNING / TASK_INTERRUPTIBLE / TASK_UNINTERRUPTIBLE)
- PID、优先级、调度策略
- mm_struct(内存描述符)
- 进程状态转换
- 创建→就绪→运行→阻塞→终止
- 僵尸进程、孤儿进程
- CFS调度器原理
- 虚拟运行时间
vruntime - 红黑树组织就绪队列
- 权重与nice值的关系
- SCHED_NORMAL / SCHED_FIFO / SCHED_RR 的区别
- 虚拟运行时间
- 进程间通信(IPC)
- 管道(pipe)和命名管道(FIFO)
- 共享内存(效率最高)
- 信号量(semaphore)
- 消息队列
- 信号(signal)
- socket(可跨主机)
- 中断上下半部
- 上半部:硬中断,快速执行
- 下半部:softirq / tasklet / workqueue
- threaded irq
- 各自适用场景
同步机制
- 自旋锁(spinlock)
- 忙等待,适用于中断上下文和短临界区
spin_lock/spin_lock_irqsave
- 互斥锁(mutex)
- 可睡眠,只能用于进程上下文
- 适用于长临界区
- 自旋锁 vs 互斥锁的选择(面经高频题)
- 中断上下文 → 必须用自旋锁
- 临界区很短 → 自旋锁
- 临界区可能睡眠 → 互斥锁
- 信号量 vs completion
- 原子操作:
atomic_t、atomic_read、atomic_set、atomic_inc - RCU基本概念
- 读不加锁,写端等待所有读完成后再释放旧数据
- 适用于读多写少场景
- 死锁
- 四个必要条件
- 避免方法:固定加锁顺序、超时机制
C++11(每天1-1.5小时)
不需要精通,但面经中出现率约60%,需要能答上来
- 基础特性
auto类型推导nullptr替代NULL- 范围for循环
enum class
- 右值引用和移动语义
- 左值 vs 右值
std::move的作用:将左值转为右值引用,触发移动构造- 移动构造函数 vs 拷贝构造函数
std::forward完美转发
- 智能指针
std::shared_ptr:引用计数,共享所有权std::unique_ptr:独占所有权,不可拷贝std::weak_ptr:解决shared_ptr循环引用
-
lambda表达式- 语法:
[capture](params) -> return_type { body } - 捕获方式:
[=]值捕获、[&]引用捕获
- 语法:
- STL容器
vector:reservevsresize、push_backvsemplace_backmapvsunordered_map(红黑树 vs 哈希表)vectorvsarray
第7周:网络 + ARM体系结构 + 调试工具
网络基础
- TCP三次握手(面经高频)
- SYN → SYN+ACK → ACK
- 为什么是三次不是两次
- TCP四次挥手
- FIN → ACK → FIN → ACK
- TIME_WAIT状态的意义
- TCP vs UDP
- 可靠性、连接、顺序、效率
- socket编程基本流程
- 服务端:socket → bind → listen → accept → read/write → close
- 客户端:socket → connect → read/write → close
- I/O多路复用(面经高频)
select:fd数量有限(1024),每次需要拷贝fd集合poll:无fd数量限制,但仍需遍历epoll:事件驱动,效率最高,epoll_create/epoll_ctl/epoll_wait
- 其他协议
- ARP协议:IP地址→MAC地址
- DNS解析过程
- ICMP:ping的原理
ARM体系结构补充
- ARM异常等级
- EL0(用户态)→ EL1(内核态)→ EL2(Hypervisor)→ EL3(Secure Monitor)
- MMU工作原理
- 页表(多级页表)、TLB(Translation Lookaside Buffer)
- TLB miss的处理流程
- 与xv6的页表机制对照理解
- Cache
- L1/L2/L3层级
- Cache一致性问题(多核场景)
- DMA操作时的Cache刷新
- ARM启动流程
- BootROM → BL1 → BL2 → BL31(TF-A) → U-Boot → Kernel
- 对照你学过的U-Boot部分衔接理解
调试工具
- GDB
- 常用命令:
break/next/step/print/backtrace/watch - 远程调试:
gdbserver
- 常用命令:
- 内核调试
printk日志等级- 动态调试(dynamic debug)
ftrace:函数跟踪、事件跟踪
- coredump分析
- 生成coredump文件
- 用GDB加载分析
-
dmesg查看内核日志
第8周:Makefile/Shell + 面经实战 + 项目整理
Makefile/Shell/工具
- Makefile
- 基本语法:目标、依赖、命令
- 自动变量:
$@、$<、$^ - 模式规则、伪目标
- 内核模块的Makefile写法
- Shell脚本基础
- 变量、条件判断、循环
- 常用命令:
grep/sed/awk/find/xargs
- Git
- 分支管理、merge vs rebase
- 解决冲突
面经实战
- 逐条过面经PDF,针对Linux驱动/BSP相关的面试记录:
- 重点场次:03、04、07、13、14、15、31、32、33、34、38号
- 能答的打勾,不能答的记录下来回头补
- 模拟面试练习
- 找同学互相模拟,或者对着镜子讲
- 每个知识点要能用自己的话讲2-3分钟
项目整理(面试竞争力核心)
- RK3399 TypeC OTG项目 — STAR描述
- S:什么产品、什么需求
- T:你负责什么
- A:具体怎么做的(改了哪些代码、遇到什么坑)
- R:结果如何
- RK3399 MIPI转LVDS屏幕适配项目 — STAR描述
- 同上,重点准备:时序参数调试、DTS配置、调试过程
- 准备好能讲15分钟的项目深度版本
- 准备好3分钟的项目精简版本
三、每日时间分配
| 时间段 | 内容 | 说明 |
|---|---|---|
| 上午(3-4h) | Linux驱动学习 + 实操 | IMX6ULL跟着做,代码必须跑起来 |
| 下午前半(2h) | 当周理论模块 | 内核原理 / C++ / 网络,按周计划推进 |
| 下午后半(1.5h) | 写笔记 + 对照面经验证 | 学完一个点就去面经里找相关题 |
| 晚上(1-1.5h) | C语言 / 回顾当天内容 | 查漏补缺 |
四、优先级说明
如果时间不够用,按以下顺序砍:
- 绝不能砍:Linux驱动框架(字符设备/platform/设备树/I2C子系统)、内核内存管理、同步机制
- 尽量保住:C语言细节、进程调度CFS、中断上下半部、C++11基础
- 可以精简:网络只需掌握TCP三握四挥 + epoll、ARM只需MMU + 启动流程
- 实在没时间可以考前突击:Makefile/Shell、调试工具细节
五、关于心态
- 面经PDF里47场面试覆盖了BSP/RTOS/AI芯片/蓝牙/汽车电子等完全不同的方向,你只需要关注Linux驱动/BSP相关的15-20场
- 深度比广度重要:能把Linux I2C子系统从用户空间到硬件寄存器完整讲清楚,比什么都知道一点但讲不深强得多
- 你的RK3399项目经验是真正的差异化竞争力,很多应届生没有这个
- 每周末回顾一次本周学了什么,对照面经检验,保持节奏感