处理中断数据的设计模式

Design Pattern for handling interrupt data

我正在开发一个用 C 实现的固件应用程序,我想知道什么是解决此类问题的好的设计模式:

  1. 在主循环中,当我从使用 UART 通信的设备接收数据时,我会得到中断
  2. 我会发送一条AT命令,等待设备回复,如果设备回复OK,我会发送下一条AT命令,直到所有命令完成
  3. 如果设备回复NOK(not ok),我会重新发送指令

我考虑过状态机,但我仍然认为实现起来并不优雅,因为我在主循环中等待响应,转换到另一个状态。

我应该实施哪种设计模式?

这听起来像是一个简单的客户端-服务器(消费者-生产者)模型问题。

简答:监视器设计模式。

长答案...

当你说"I will get interrupt"时,你的意思是会触发硬件中断,还是会发生执行上的变化?在硬件中断的情况下,这将是特定于平台的。 Even something as simple as a keyboard hardware interrupt routine takes a bit of effort to implement.

如果是另一种情况,您可以只拥有一个 main/manager 线程、一个辅助线程,以及:

  1. 让主循环只使用互斥锁和spin-lock,等待工作线程报告数据准备就绪,而工作线程阻塞等待I/O存储在受互斥锁保护的缓冲区中.
  2. 而不是自旋锁,使用 condvar (condition variable),这样你就不会耗尽 CPU 和不必要地浪费周期。

    /*
     *  Solution to Producer Consumer Problem
     *  Using Ptheads, a mutex and condition variables
     *  From Tanenbaum, Modern Operating Systems, 3rd Ed.
     */
    
    /*
        In this version the buffer is a single number.
        The producer is putting numbers into the shared buffer
        (in this case sequentially)
        And the consumer is taking them out.
        If the buffer contains zero, that indicates that the buffer is empty.
        Any other value is valid.
    */
    
    #include <stdio.h>
    #include <pthread.h>
    
    #define MAX 10000000000         /* Numbers to produce */
    pthread_mutex_t the_mutex;
    pthread_cond_t condc, condp;
    int buffer = 0;
    
    void* producer(void *ptr) {
      int i;
    
      for (i = 1; i <= MAX; i++) {
        pthread_mutex_lock(&the_mutex); /* protect buffer */
        while (buffer != 0)            /* If there is something 
                          in the buffer then wait */
          pthread_cond_wait(&condp, &the_mutex);
        buffer = i;
        pthread_cond_signal(&condc);    /* wake up consumer */
        pthread_mutex_unlock(&the_mutex);   /* release the buffer */
      }
      pthread_exit(0);
    }
    
    void* consumer(void *ptr) {
      int i;
    
      for (i = 1; i <= MAX; i++) {
        pthread_mutex_lock(&the_mutex); /* protect buffer */
        while (buffer == 0)         /* If there is nothing in 
                           the buffer then wait */
          pthread_cond_wait(&condc, &the_mutex);
        buffer = 0;
        pthread_cond_signal(&condp);    /* wake up consumer */
        pthread_mutex_unlock(&the_mutex);   /* release the buffer */
      }
      pthread_exit(0);
    }
    
    int main(int argc, char **argv) {
      pthread_t pro, con;
    
      // Initialize the mutex and condition variables
      /* What's the NULL for ??? */
      pthread_mutex_init(&the_mutex, NULL); 
      pthread_cond_init(&condc, NULL);      /* Initialize consumer condition variable */
      pthread_cond_init(&condp, NULL);      /* Initialize producer condition variable */
    
      // Create the threads
      pthread_create(&con, NULL, consumer, NULL);
      pthread_create(&pro, NULL, producer, NULL);
    
      // Wait for the threads to finish
      // Otherwise main might run to the end
      // and kill the entire process when it exits.
      pthread_join(&con, NULL);
      pthread_join(&pro, NULL);
    
      // Cleanup -- would happen automatically at end of program
      pthread_mutex_destroy(&the_mutex);    /* Free up the_mutex */
      pthread_cond_destroy(&condc);     /* Free up consumer condition variable */
      pthread_cond_destroy(&condp);     /* Free up producer condition variable */
    
    }
    

我会使用 "divide & conquer" 原则将您的问题分解为可管理的子问题。您可以在不同的抽象级别使用以下模式:

  • 您可以使用像 "Layer Pattern" 这样的结构化架构设计模式来构建具有以下结果上下文的源代码:
    • 提高可维护性(大优势)
    • 性能下降(可忽略不计,假设 "enough hardware performance")
  • 您可以使用设计模式寻址调度来管理 一般管理线程。我会推荐一种模式,它会导致 对传入异步事件的良好响应 ("incoming UART data")。 如果您使用实时操作系统 (RTOS),您可以检查是否不同 调度机制是可配置的。一个线程可能是 "receive data" (执行完"send data"后进入,全部数据完成后退出 收到),第二个线程"command handler"(进入后 "receive data" 线程的退出,在应发送的命令已退出后退出 已确定)和第三个线程 "send data"(在“comand”退出后进入 handler”,在发送完所有数据后退出)。优先级(最高,.., 最低)可以分配如下:"receive data"、"command handler"、 "send data"。使用 "Static Priority Pattern" 会导致以下结果 结果上下文:
    • 稳定性(可以预测哪个线程在过载情况下失败。 考虑在发送过程中接收一些东西...)
    • 对传入事件的良好响应
    • "unbounded priority inversion" 可能由于阻止资源共享而发生 -> 需要实施资源共享模式
  • 您可以使用 "Critical Region Pattern"(禁用任务切换)或 "Guarded Call Pattern" (~mutex semaphores) 保护资源 "UART peripheral".
  • "Observer Pattern"(等于"Monitor Pattern")可用于管理 当活动线程根据传入的 UART 数据发生变化时 是否检测到。
  • 检测传入的 UART 数据and/or检测是否所有数据都已发送 传了"Interrupt Pattern"就可以用了。 (假设串口支持相关中断功能)
  • 在最低级别的抽象上,您可以使用可能的“状态”之一 机器”模式实现,用于发送数据和接收数据 UART 驱动程序(UART 外设状态:字节 sent/received、错误等)。

你可以找到很多关于上述例子的总结和参考 eswp3.org 和 C 中的其他设计模式。 (对不起,我不能 目前超链接超过 2 个网站。我会在赚钱后这样做 足够的声望...)