Bruce Powel Douglass大师介绍-CSDN博客https://blog.csdn.net/ChatCoding/article/details/134665868
嵌入式软件开发从小工到专家-CSDN博客https://blog.csdn.net/ChatCoding/article/details/135297955
C嵌入式编程设计模式源码-CSDN博客https://blog.csdn.net/ChatCoding/article/details/134819019
静态分配模式只适用于负载(内存量)高度可预测且稳定的简单系统。在适用的情况下,使用这种模式可以使系统易于设计和维护。
6.2.1 摘要
动态内存分配在实时嵌入式系统中存在两个主要问题:
- 内存分配和释放的时间不确定。
- 内存碎片化。
这种模式采用了一种非常简单的方法来解决这两个问题:禁止动态内存分配。这意味着所有对象都在系统初始化期间分配。如果设计时能够知道内存负载,并且能够为最坏情况分配足够的内存,那么系统虽然初始化时间会长一点,但运行时将更加稳定高效。
6.2.2 问题
动态内存分配在结构化和面向对象设计中非常普遍。例如,C++ 语言使用 new
和 delete
来分配和释放内存,C 语言则使用 malloc
和 free
。在这些语言中,程序员都需要显式执行这些操作,但也很难想象任何大型程序不使用分配内存的指针。而 Java 语言更加彻底,所有对象都存放在动态内存中,这意味着所有对象创建都隐式地使用了动态内存分配。此外,Java 还会在内存不再使用时自动释放它,但具体何时何地发生则不在程序员的控制范围内。
尽管很常见,动态内存分配始终是让实时系统开发疼痛的问题,主要有以下三个难题:
- 非确定性:动态内存分配和释放的时机通常是不可预测的,因为这通常需要搜索数据结构以找到可分配的空闲内存。这会导致系统的行为难以预测,甚至出现不可预料的延迟或中断。
- 内存泄漏:如果程序员没有正确释放不再使用的内存,就会发生内存泄漏。这会导致系统内存的逐渐减少,最终导致系统崩溃。
- 内存碎片化:由于内存是以不同大小的块分配的,释放顺序通常与分配顺序无关。这会导致空闲内存出现碎片化,即无法找到足够大的连续空闲内存块来满足新的内存请求。
上述三个难题都可能导致实时系统出现严重的性能问题或甚至崩溃。因此,在实时系统中,应尽量避免使用动态内存分配。
6.2.3 模式结构
图 6-1 展示了该模式的基本结构。它结构非常简单,但可以通过嵌套抽象层来处理任意大小的系统。系统对象启动初始化过程,并创建最高级别的复合对象。这些复合对象通过组合关系连接到其他复合对象或原始对象。原始对象被定义为不会动态创建其他对象的类。使用组合关系是因为它们清晰地标识了创建/删除的责任。
6.2.4 协作角色
- Allocation Plan(分配计划):如果存在,标识最大系统复合对象应该按照哪个顺序分配。
- Composite Object(复合对象):复合对象通过组合关系负责创建它所拥有的所有对象。
分配计划
分配计划(Allocation Plan)是可选的,用于指定最大系统复合对象的分配顺序。如果没有分配计划,则系统可以按任何所需的顺序分配对象。
复合对象
复合对象(Composite Object)是指与其他对象之间存在组合关系的对象,其他对象可以是复合对象或原始对象。复合对象负责通过组合创建它所拥有的所有对象。复合对象和系统对象都不能释放内存。
复合对象可以由其他复合对象组成,但每个通过组合关系拥有的对象只能属于一个复合对象。这意味着模式中清楚地标识了系统中每个对象的创建责任。
部件对象
部件对象(Part Object)是复合对象和原始对象的父类。它允许系统对象和复合对象通过组合包含复合对象和原始对象。
原始对象
原始对象(Primitive Object)是指不负责分配任何其他对象的类。所有原始对象由复合对象创建。
系统对象
系统对象(System Object)是系统中最高抽象层级的对象。它的职责是通过创建和初始化系统的主要部分(最高级别的复合对象)来“启动”系统。这些复合对象又会创建自己的部分,依此类推。一旦所有对象都创建完毕,系统对象就会通过运行 begin
操作启动系统执行。
6.2.5 结果
静态分配模式将所有对象都在系统启动时进行内存分配,适用于以下情况:
- 最坏情况可预测且内存充裕: 系统的峰值内存需求是已知且可控的,并且拥有足够的内存满足这种情况。
- 内存负载稳定: 系统在不同运行状况下的内存需求差异不大,保持相对稳定的内存占用。
- 系统规模较小: 需要预先分配所有对象内存的特点限制了系统规模,适合用于较小的系统。
- 对内存成本不敏感: 系统对内存成本相对不敏感,预先分配内存造成的潜在浪费可以承受。
使用静态分配模式的系统拥有以下优势:
- 更快的运行速度: 启动后执行代码无须进行动态内存分配,运行速度通常比动态分配更快,有时显著更快。
- 更可预测的执行: 消除了动态分配带来的非确定性因素,使系统运行更加可预测。
- 无内存碎片化: 不进行内存释放,就不会出现内存碎片化的问题。
然而,静态分配模式也存在一定的缺点:
- 启动时间较长: 需分配所有对象内存,导致启动时间可能明显延长,对启动时间要求苛刻的系统可能不适用。
- 潜在内存浪费: 预先分配所有对象内存可能造成一定程度的浪费,如果实际内存需求低于最坏情况,则会占用额外的内存空间。
总结: 静态分配模式适用于最坏情况可预测、内存负载稳定、规模较小且对内存成本不敏感的系统。它能带来更快的运行速度、更可预测的执行和无碎片化等优势,但需要付出启动时间较长和潜在内存浪费的代价。
6.2.6 实施策略
这个模式非常容易实现。在许多情况下,甚至不需要单独的初始化方法,可以直接使用每个复合对象的构造函数来完成对象的创建和初始化。
6.2.7 相关模式
本章中介绍的其他模式也解决了类似的问题,但会带来略有不同的利弊。例如,池分配模式、固定大小缓冲区模式、垃圾回收器模式和垃圾压缩器模式等等。
6.2.8 示例模型
图6-2展示了一个使用完全构建系统实例的简单例子。图6-2a展示了对象图的实例结构,图6-2b展示了静态分配模式在启动时的工作方式。
总结来说,静态分配模式通过在系统启动时分配所有对象,避免了动态内存分配带来的时间不确定性和内存碎片化问题。这种模式适用于内存负载可预测、系统设计简单且内存成本不是主要考虑因素的情况。在实施时,需要在设计阶段对内存需求有准确的预测,以确保在最坏情况下有足够的内存。
图形中各个箭头的含义
- 实线箭头:代表对象之间的组合关系
图形中各个对象的含义
- 起搏器对象(Pacemaker Object):这是整个起搏器设备的总称。
- 电池系统(Battery Subsystem):为起搏器提供动力。
- 电池传感器(Battery Sensor):监测电池的电压,并在电池电量不足时向起搏器发出警报。
- 通信子系统(Comm Subsystem):允许起搏器与外部设备(如程序器和监控系统)进行通信。它使用无线遥测系统来传输和接收数据。
- 磁簧开关(Reed Switch):检测起搏器是否置于磁场中。这可用于激活起搏器的某些功能,例如遥测。
- 消息队列(Message Queue):用于存储由通信子系统传输或接收的数据。
- 收发器线圈(Transceiver Coil):用于无线传输和接收数据。
- 起搏子系统(Pacing Subsystem):起搏器的核心
- 心房(Atrial Chamber):心脏的上腔。
- A 心脏传感器(A Heart Sensor):监测心房的电活动。
- A 起搏电容器(A Pacing Canacitor):为起搏电路存储能量。
- 心室(Ventricular Chamber):心脏的下腔。
- V 心脏传感器(V Heart Sensor):监测心室的电活动。
- V 起搏电容器(V Pacing Capacitor):为起搏电路存储能量。
- 心房(Atrial Chamber):心脏的上腔。
- 电池系统(Battery Subsystem):为起搏器提供动力。
起搏子系统(Pacing Subsystem):起搏器的核心,负责生成电脉冲来刺激心肌。它由起搏电路、起搏电极和起搏电容器组成。
- 起搏电路(Pacing Circuit):生成电脉冲。
- 起搏电极(Pacing Electrode):将脉冲传递给心肌。
- 起搏电容器(Pacing Capacitor):为起搏电路存储能量。