读U-Boot源码-C语言编程大法总结篇一
导读:如本人在《U-Boot架构浅析》所说,U-Boot具有十大黄金原则:小巧、快速、简单、可移植、可配置、可调试、易用、可维护、优雅、开源。面对如此精美的作品,如不深究,从提升编程技艺角度而言实为憾事。故本文试图观其形而悟其神,并把所见所思所得记录与诸君分享。文中观点或有错误疏漏,诚请交流指正,不甚感激!
说明:
封面配图,纯为搞笑
文中绘图采用UML语言,或有不对也请一并指正。
代码分析基于u-boot-2016.09.y
代码见下面
https://gitlab.denx.de/u-boot/u-boot/-/tree/u-boot-2016.09.y
所得之一:封装思想
U-boot将几乎所有信息集总抽象至global_data,概括而言,大体有以下信息:
bd_info(bd_t*)包含CPU频率,内存大小,FLASH大小等信息
为实现可移植原则,将不同体系结构利用宏进行选编译。
通用属性数据:CPU频率、PCI频率、内存频率、环境变量地址、堆信息等杂散参数信息。
驱动设备接口:
struct udevice *dm_root;
驱动程序模型的根指针
struct udevice *dm_root_f;
重定向前驱动程序模型的根指针
struct list_headuclass_root;
uclass链表,所有被udevice匹配的uclass都会被挂载到这个双向链表上
struct udevice *timer;
定时器设备指针
struct udevice*cur_serial_dev;
当前串口设备指针
structarch_global_data arch,
各不同体系结构属性。
所得:
可移植:实现接口统一,通用部分实现,多样性兼顾。
可配置:差异属性利用宏进行使能与关闭。
所得之二:宏的妙用
jt_funcs 定义了函数跳转表:
在static int initr_jumptable(void)进行初始化。
#define EXPORT_FUNC(impl, res, func, ...) res(*func)(__VA_ARGS__);
impl,函数名这个参数展开时没有用到,只是为了增加可读性。相当于函数实现名。
res 返回值
__VA_ARGS__ 可变参数列表
如:
EXPORT_FUNC(get_version, unsigned long,get_version, void)
展开为:
unsigned long (* get_version) (void)
利用该宏定义可变参数函数指针,将一系列不同函数原型用一种统一的格式进行定义,并收集在_exports.h中,jt_funcs结构体将该头文件包含,并封装而成跳转表结构体。编译预处理时自动将定义插入到结构体的定义中了。
<include /_exports.h>包含封装在EXPOR_FUNC宏中的一系列导出函数,其中一些导出取决于“ CONFIG_XXX”选项。将根据包含此标头的文件定义宏。例如,在common / exports.c>中,将设置EXPOR_FUNC宏以使用<include /_exports.h>中的函数初始化跳转表结构。 在<include /exports.h>中,EXPOR_FUNC宏将设置为包含在跳转表结构中的函数指针的类型。
这样做的好处:
可维护性极高,如需添加或修改跳转表结构体函数,只需修改_exports.h,以及修改对应的实现源文件
定义函数指针进跳转表另一个给我启发是,可以实现动态加载,以实现统一接口不同操作。类似C 的函数重载。
所得之三:环形缓冲区
在分析控制台模块部分代码时发现,其内存缓冲区采用环形缓冲区。
所得之一:将环形缓冲区预留一个字节以判断满/空。这样做程序优雅实现简便。
环形缓冲I/O:@start @size - 1
空:如果@head ==@tail 则空
满:如果在追加一个字节使@head== @tail,则满
未完待续。。。
点击留言