专栏首页LINUX阅码场宋宝华: Linux内核编程广泛使用的前向声明(Forward Declaration)

宋宝华: Linux内核编程广泛使用的前向声明(Forward Declaration)

前向声明

编程定律

先强调一点:在一切可能的场景,尽可能地使用前向声明(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的童鞋代码一层层套很傻逼,这是完全不正确的理解。

天天要带娃,鸡飞狗跳,没时间写,我随便用碎片时间胡说八道的,不知道各位看官有什么感想?欢迎板砖,也欢迎打赏。

本文分享自微信公众号 - Linux阅码场(LinuxDev),作者:宋宝华

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-10-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一文带你彻底理解Linux的各种终端类型及概念

    每天使用Linux每天都要接触到Bash,使用Bash时似乎永远都让人摸不着头脑的概念就是终端,坐在这台运行着Linux的机器的显示器前面,这个显示器就是终端...

    Linux阅码场
  • copy_{to, from}_user()的思考

    我们对copy_{to,from}_user()接口的使用应该是再熟悉不过吧。基本Linux书籍都会介绍它的作用。毕竟它是kernel space和user s...

    Linux阅码场
  • 非常详尽,多图慎入:Wayland与Weston简介

    简单地说,Wayland是一套display server(Wayland compositor)与client间的通信协议,而Weston是Wayland c...

    Linux阅码场
  • 【投稿】刀哥:Rust学习笔记 4

    2019年底Rust正式支持 async/await语法,完成了Rust协程的最后一块拼图,从而异步代码可以用一种类似于Go的简洁方式来书写。然而对于程序员来讲...

    MikeLoveRust
  • 产业互联网受资本青睐 全球化至关重要

    乌镇,世界互联网大会第四年。 ? 从今年大会可以明显观察到,在国家坚持以供给侧结构性改革为主线的政策指引下,嗅觉灵敏的资本已经盯上了“产业互联网”。 风口已来,...

    企鹅号小编
  • 新的合规要求之下,如何快速通过云操作系统等保测评

    突如其来的疫情,让大量中小企业措手不及,被迫加速将办公和业务场景从线下转往线上,同时5G、AI、云计算等新一代信息技术的应用也在加速各行业数字化和产业升级的进程...

    腾讯安全
  • 人脸识别完整项目实战(2):完整项目运行演示

    本文是《人脸识别完整项目实战》系列博文第1部分,第一节《完整项目运行演示》,本章内容系统介绍:人脸系统核心功能的运行演示。

    数据饕餮
  • 数据结构 | 每日一练(104)

    ——老子

    C语言入门到精通
  • 高性能:3-为何性能分析工具需要BPF 【bpf performance tools读书笔记】

    性能工具之所以使用扩展的BPF,部分原因在于它的可编程性。BPF程序可以执行自定义等待时间计算和统计摘要。仅这些功能就可以构成一个有趣的工具,并且还有许多其他具...

    二狗不要跑
  • SaaS领域HR巨头Zenefits衰落始末

    本文作者:杨丽 美国人力资源软件公司 Zenefits 是 2015 年成长最快的美国独角兽之一,其神话鼓舞了资本圈。这款 HR 办公软件覆盖全美 50 个州...

    人称T客

扫码关注云+社区

领取腾讯云代金券