当在主流操作系统(Windows、Linux、MacOS、Android等)上开发这类处理程序,无论使用什么开发模型一般都不会出现什么问题,但要在嵌入式环境达到更好的性能不得不使用一些开发模型,导致这个的原因是主流操作系统已在驱动层面和系统层面处理好了些问题只需要在应用层做好处理即可,然而在嵌入式环境中,为了达到更好的性能,则必须使用一些特定的开发模型,尽管芯片厂商可能提供了一些驱动程序,但这仍然不足以满足性能需求。
1. 什么是生产者消费者模型
生产者消费者模型最早由荷兰计算机科学家 Edsger W. Dijkstra 在他的经典著作《The Structure of the “THE”- Multiprogramming System》中提出。在这篇论文中,Dijkstra 讨论了操作系统中的并发控制问题,并引入了生产者消费者问题作为一个经典的示例,以展示如何使用信号量机制来协调生产者和消费者之间的数据交换。模型架构如下:

这个模型在生产者和消费者之间增加一个缓存也相当于增加了一个库存,用于降低生产者对消费者实时处理的要求,试想一下生产者每生产一条数据都需要消费者实时处理才能生产下一条数据,这会对消费者的处理能力以及实时性要求极高,消费者由于不能及时的消耗这一条数据就会导致无法再生产数据。从更长的时间周期来看消费者的消费能力都一定是大于等于生产者的生产能力,但消费者的任务繁重实时能力这类数据的能力已不太高,增加这么一个缓存机制后,虽然成本有所增加,但是从整个系统的角度上生产消费的效率可以较大的提供。
生产者消费者之间的缓存,一般有两种数据结构,分别是FIFO(First In First Out,先进先出)、FILO(First In Last Out,先进后出),在实际环境中究竟应该选择哪种数据结构需要结合实际的需求、数据特征等条件进行选择。
2. 重构串口处理程序
串口上数据本质是流数据,“什么时候有数据、有多少数据、数据内容是什么”都是未知的,需要把数据完整的收下来做进一步数据解析才能清楚,考虑数据串口流数据的特征,为了更好保证数据的完整性这里的缓存数据类型应该选择FIFO,架构如下:
FIFO的实现有多种方式,比如队列、环形缓存(环形缓存本质是一个数组,仅仅将数组的收尾链接在了一起),队列更多用在一些固定长度内容的场景有助于提高缓存的利用率,当然队列也可以用在串口这个场景里面仅仅是缓存的利用率可能不太高而已,更多的场景还是推荐使用环形缓存数据结构作为缓存,如下图所示:
串口收到数据后会产生一个中断,根据硬件平台不同可能收到一个字节就产生了中断也可能收到多个字节才能产生中断,在中断中将收到的数据推入RingBuffer中,然后串口继续接收后续数据,接下来处理程序可在空闲期间处理RingBuffer中收到的数据,这样基本上就完成了一个串口关于消费者和生成者模式的设计,这也一个经典的前后台分立设计模式。