嵌入式程序设计方法——解耦

当我们讨论“解耦”的时候,我们谈论的是什么?

 

我个人理解,嵌入式应用设计中,应用(App)和驱动程序(Driver)的解耦可以分为三个层次:

 

1. 构建解耦:提高代码可读性

合理设计头文件和函数接口,将驱动和应用层的代码(*.c)分离编译。代码中的调用关系保持不变。

将App和Driver在文件的层面上分离,有助于提高代码可读性。但是经过编译-链接环节后,显式的调用关系会让App和Driver在二进制层面是混合在一起的。

 

2. 二进制解耦:接口抽象(隐藏不必要信息,提高可移植性)

通过函数指针和注册机制,将驱动程序和应用层在二进制层面上分离。应用层通过函数指针,以查找并跳转的方式执行驱动程序。

注册机制实质上就是查找表(Look Up Table)。App和Driver可以单独被链接到不同的地址,但是通过指定的函数指针查找并跳转,二者依然可以建立调用关系。

但是由于App和Driver间的调用关系从未真正解除,二者的代码会运行在同一个程序上下文中。我们很难高效地在同一个程序上下文中,分离App和Driver的代码,有针对性地部署安全策略。在App/Driver的API中插桩虽然可以实现权限控制的需求,但无论是开发效率,还是执行效率,都非常低下。当系统规模逐渐膨胀、逻辑逐渐复杂的时候,这种实现方式对效率的影响是灾难性的。

 

3. 逻辑解耦:程序安全(分离执行上下文,提高安全性)

应用层和驱动层仅通过结构化信息,传递驱动请求和响应。由于二者间不存在显式/隐式的调用关系,所以驱动层和应用层可以运行在不同的地址空间,使得可以适应相对复杂安全设计。

举个例子,App线程和Driver线程,以某种IPC方法传递一类自描述结构体。在这类自描述结构体中,应当能够描述App线程需要的一系列驱动动作请求。当Driver线程拿到自描述结构体时,就会根据其中所描述的动作,调用指定的驱动代码完成指定的动作,并将结果以类似的方式传回App线程。这种设计使得App和Driver间不存在任何调用关系,并且由于App和Driver运行在不同的程序上下文,易于实现更有效的安全机制。我们可以使用MMU或者MPU这类硬件,非常轻松地划定App和Driver代码的运行权限。通过在Driver线程中实现更完备的安全检查,程序可以更有效地拦截并处理空指针、非法数据格式这类异常,提高系统健壮性。

 

当然,使用独立的Driver线程并不是逻辑解耦的唯一实现方法。现代处理器一般都会提供一类特殊的SVC中断,作为System Service的入口。通过指定的SVC请求号,即可在SVC中断上下文中调用指定的驱动程序。

但这种设计方法也不是没有代价的。由于消除了调用关系,这种方法中的驱动请求和驱动执行是异步关系。对于高频执行,或者高实时性要求的驱动需求来说,异步机制引入的不确定性是需要谨慎斟酌的。

 

需要说明的是,上述三类解耦的设计方法是逐渐递进,而不是并列或者互斥的关系。

构建解耦的基础上,实现二进制解耦有助于提高程序的抽象程度,从而提高程序的可移植性

而在构建解耦二进制解耦的基础上,实现**逻辑解耦**则有利于进一步地提高程序的健壮性

posted @ 2020-02-13 22:48  AlexYzhov  阅读(1083)  评论(0编辑  收藏  举报