前向声明
编程定律
先强调一点: 在一切可能的场景, 尽可能地使用前向声明 (Forward Declaration). 这符合信息隐蔽的原则.
一个例子
regmap
那么前向声明究竟是个什么鬼? 在内核写代码和看代码的童鞋, 经常发现 Linux 内核里面充斥着这样的代码, 比如
include/VIM Linux/regulator/driver.h
文件中:
我们以 regmap 这个结构体为例, 这个地方就是一个前向声明, 告诉后面的代码 regmap 是个结构体, 至于这个结构体里面有什么鬼, 不知道!
Linux 可以说满世界都在使用这个结构体. 满世界都在使用声明在 include/Linux/regmap.h 中的 regmap_write(),regmap_read() 这样的 API, 可以说无处不在, 无处不用, 比如 drivers/rtc/rtc-at91sam9.c 中的:
这样的东西大家随便一搜索, 都可以搜索出来无数个. 这样看起来, regmap 这个结构体, 应该是一个跨模块的 API, 它的整个结构体长成怎么样, 应该是出现在一个 include/Linux / 级别的顶级跨模块头文件中了, 这样方便跨模块引用这个结构体.
但是, 真实的情况却让你大跌眼镜, regmap 结构体的具体成员长什么样子, 没有出现在任何一个外部级别的头文件里面, 而是完全 internal 的 (内部的, 内部的, 内部的, 各位童鞋!!!):
drivers/base/regmap/internal.h
既然它出现在 drivers/base/regmap/internal.h, 那么想必除了 drivers/base/regmap / 本身的内部实现外, 外部不可能引用 drivers/base/regmap/internal.h 这个头文件.
所以, 我们得出一个结论, 尽管 Linux 满世界都在使用 struct regmap, 但是除了 drivers/base/regmap / 内部以外, 其实外部没有任何一个人知道 regmap 这个结构体长成什么样子!!
这是一种极其良好的 "高内聚, 低耦合" 设计. 因为, drivers/base/regmap / 外部所有的人, 其实都只是在拥有 regmap 这个结构体的指针, 而并没有访问 regmap 结构体其中的任何一个成员, 其实也只有 drivers/base/regmap / 的内部实现在访问而已.
比如, regmap_write 实现于: drivers/base/regmap/regmap.c 文件, 它的代码如下:
这样做带来的一个极大好处是, drivers/base/regmap / 外部的世界根本不需要知道 regmap 结构体长成什么样子, 因为没人需要知道, 它们都只是在访问 regmap 的指针!
而 drivers/base/regmap / 内部无论怎么修改 regmap 结构体的实现和成员本身, 对外部的世界根本不可见, 修改 regmap 结构体后, drivers/base/regmap / 以外的模块都不需要重新编译!
相反, 如果我们直接把 regmap 结构体的内部细节暴露在 include/Linux/regmap.h 这个头文件中, 那么由于这个头文件满世界都被引用, 你只要修改 regmap 结构体本身, 就会导致内核无数模块的增量编译!
include/Linux/regmap.h 中暴露了 regmap_config 结构体, 这说明这个结构体的内容需要被 regmap 以外的模块知道:
...
为什么, 它涉及到具体的寄存器是如何读写的 callback 以及具体的寄存器 pattern, 这肯定是一个 API 基本的东西, 本身就应该是跨模块的东西, 所以它的长相出现在了 include/Linux/regmap.h 这个顶级头文件中.
对于一个外部模块而言, 它只需要能够通过 regmap.h 公开暴露的小部分寄存器配置接口, 来通过类似 regmap_init_mmio() 这个的 API 来填充 regmap 结构体的内部实现. 比如 drivers/rtc/rtc-at91sam9.c 中的:
上述代码中, rtc->gpbr 是一个 struct regmap 指针, regmap_init_mmio() 在内部填充了 regmap 的本身实现. 之后 drivers/rtc/rtc-at91sam9.c 再调用 regmap_write(),regmap_read() 的时候, 这些 API 从 regmap 模块内部调用我们填充进去的 reg_bits,val_bits,reg_stride 这些寄存器 pattern, 帮忙完成寄存器的最终读写.
画一幅图
理清关系
永远用高内聚和低耦合的思想设计代码. Linux 内核 2000 万行的代码, 不这么设计肯定要崩盘. 写代码不是得过且过. 尤其做单片机写裸奔程序的童鞋要特别注意, 你们往往觉得玩 Linux 的童鞋代码一层层套很傻逼, 这是完全不正确的理解.
天天要带娃, 鸡飞狗跳, 没时间写, 我随便用碎片时间胡说八道的, 不知道各位看官有什么感想? 欢迎板砖, 也欢迎打赏.
来源: https://www.cnblogs.com/linuxdev/p/11937863.html