1. Linux内核SPI子系统研究:从总线注册到设备管理
作为一名嵌入式Linux开发者,我最近在研究Linux-4.9.88内核的SPI子系统实现。这个系列文章将记录我的学习过程,今天先深入分析总线注册的核心机制——bus_register函数。理解这个函数对掌握Linux设备模型至关重要,它构建了/sys/bus目录下的整个组织结构。
1.1 研究思路与方法论
在研究内核代码时,我总结了一套高效的方法论:
1.1.1 纵向跟踪法
看到一个新的结构体时,不要一开始就想把所有成员都看透。正确的做法是:
- 以关键函数调用(如bus_register)为线索
- 沿着代码执行流程逐步深入
- 只有当流程执行到某一步,用到了结构体的某个成员时,那个成员才有意义
这种方法避免了过早陷入细节,保持对整体流程的把握。
1.1.2 日志记录法
做内核研究时,详细的日志记录必不可少。我的日志记录方法包括:
-
坐标定位:在开头明确记录当前研究的函数名和研究动机。例如:
code复制函数:bus_register 目的:摸清总线注册的资源分配流程 -
单步追踪:只关注当前函数执行的主干路径,忽略未执行的分支。对每个实质性动作用通俗语言记录:
code复制- 申请私有账本空间(内存分配) - 在/sys/bus下创建主目录(kset_register) -
结构体标注:遇到结构体成员访问时,立即标注其作用。例如:
code复制klist_devices:存放未来所有子设备的点名册 [容器类成员] -
图形建模:在纸上或绘图工具中画出关键结构的关系图。例如:
- 画出bus_type和subsys_private两个方框
- 用双向箭头连接它们
- 根据代码动作引出分支,绘制/sys下的目录层级
1.1.3 打印调试法
结合内核打印输出,将实际运行时的数据填入日志:
code复制platform总线的priv实际地址是0xXXXX
用真实数据验证理论推导,这是内核开发的黄金法则。
1.2 SPI初始化流程分析
让我们从SPI子系统的初始化函数开始:
c复制static int __init spi_init(void)
{
int status;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!buf) {
status = -ENOMEM;
goto err0;
}
status = bus_register(&spi_bus_type);
if (status < 0)
goto err1;
status = class_register(&spi_master_class);
if (status < 0)
go
