C 嵌入式系统设计模式 13:中断模式

本书的原著为:《Design Patterns for Embedded Systems in C ——An Embedded Software Engineering Toolkit 》,讲解的是嵌入式系统设计模式,是一本不可多得的好书。

本系列描述我对书中内容的理解。本文章描述访问硬件的设计模式之六:中断模式。

物理世界本质上是并发和异步的。

  • 并发 是指在同一时刻有多个活动(通常是任务或进程)正在发生或进行。在单线程环境中,并发通常是通过时间片轮转等机制模拟出来的,而在多线程或多核处理器环境中,多个任务可以同时进行。
    并发编程的目标是最大限度地利用计算资源,提高程序的响应性和吞吐量。为了实现并发,开发者通常会使用多线程、多进程、异步I/O等技术。
  • 异步操作 是指程序的某个部分可以在不等待其他部分完成的情况下继续执行。在异步编程中,任务的执行不会阻塞当前线程或进程,而是会立即返回并在后台继续执行。当任务完成时,通常会通过回调函数、事件、Promise等方式通知程序的其他部分。
    异步编程的主要好处是提高程序的响应性和性能,因为它允许程序在等待资源(如I/O操作)时继续执行其他任务。

在嵌入式系统和低级系统编程中,中断 是一种强大的机制,用于响应和处理外部事件或内部状态变化。中断处理程序是与特定中断类型相关联的代码段,当该类型的中断发生时,处理器会自动跳转到该代码段执行。由于中断可以打断正在执行的程序,因此它们提供了一种并发和异步处理事件的方式。这对于实时系统和嵌入式系统来说尤为重要,因为这些系统需要能够及时响应和处理外部事件,以确保系统的正确性和可靠性。此外,中断处理程序还可以用于实现诸如定时器、串行通信、输入/输出等操作,从而提高了系统的功能和性能。

摘要

中断模式在嵌入式系统和实时系统中非常常见,因为这些系统需要能够及时响应和处理外部事件。为了实现中断模式,开发人员需要利用处理器和编译器提供的中断服务例程(ISR)机制。ISR是与特定中断源相关联的代码段,当中断发生时,处理器会自动跳转到该代码段执行。在ISR中,开发人员可以编写用于处理中断事件的逻辑代码。处理完中断事件后,处理器会返回到被中断的程序继续执行。

问题

中断模式是一种强大且有效的机制,用于处理紧急和高频事件。与轮询模式相比,中断模式能够确保及时响应和处理这些事件,从而提高系统的实时性和可靠性。在中断模式中,当紧急事件发生时,处理器会立即暂停当前任务,跳转到与该事件相关联的中断服务例程(ISR)进行处理。处理完成后,处理器会返回到被中断的任务继续执行。这种机制确保了紧急事件能够得到优先处理,而不会被其他低优先级的任务所阻塞。

模式结构

模式结构图如下所示:
在这里插入图片描述

模式详情

中断处理

中断向量是一个指向中断服务例程(ISR)的指针或地址,当中断发生时,系统会查找对应的中断向量,并跳转到该向量指向的中断服务例程去执行。因此,通过修改中断向量表,我们可以控制哪个中断服务例程响应特定的中断。

中断处理install()deinstall() 函数提供了一种机制来动态地修改中断向量表。这使得我们可以在运行时安装或卸载中断服务例程,从而灵活地处理不同的中断事件。

每个 handleInterrupt_x() 函数都处理一个特定的中断,并以“从中断返回”(Return From Interrupt,RTI)语句结束。这条语句依赖于编译器和处理器。如前所述,至关重要的是中断服务例程不能有参数,这就是原因。否则当它们尝试返回时,将会从CPU堆栈中弹出错误的值。

中断向量表

InterruptVectorTable(中断向量表)实际上就是一个包含中断服务例程地址的数组。它位于处理器特定的内存位置。当中断号x发生时,CPU会暂停当前的处理,并间接调用该表中与第x个索引对应的地址。在RTI(从中断返回)指令执行后,CPU会恢复被暂停的任务。

效果

中断处理模式在嵌入式系统、操作系统和实时系统中非常常见,因为这些系统需要快速响应外部事件或内部状态变化。通过中断,系统可以在不等待当前任务完成的情况下,立即处理紧急事件,从而提高系统的响应性和实时性。

然而,由于中断会打断正常的程序流程,因此在使用中断时需要特别小心。如果中断服务例程执行时间过长,或者频繁触发中断,可能会导致系统性能下降或任务延迟。通常,当中断服务例程正在执行时,会禁用中断(不允许中断嵌套);这意味着中断服务例程必须执行得非常快,以确保不会错过其他中断。

由于中断服务例程(ISR)必须简短,因此如果在中断服务例程中调用其它系统服务时必须小心。例如,为了共享由中断发出的数据,中断服务例程可能需要将数据排入队列并快速返回;在将来的某个时刻,应用软件会在队列中发现这些数据。当数据的实际获取比其处理更为紧急时,这种机制非常有用。通过这种方式,可以将一个长时间的中断处理程序分解成两个部分:紧急部分,通过中断服务例程本身完成;处理部分,通过第二个函数完成,该函数定期检查数据或信号。

  • 如果中断服务例程处理时间过长,会延迟系统对其他中断的响应,可能导致实时任务错过截止时间或系统性能下降。这通常发生在中断服务例程执行了不必要的复杂操作,或者等待了某些不可用的资源。
  • 实现错误导致中断被禁用也是一个常见问题。当中断被禁用时,系统无法响应外部事件,可能导致重要任务被延迟或错过。这种错误通常是由于程序员对中断管理的不当理解或疏忽造成的。
  • 然而,共享资源上的竞争状态和死锁可能是最为棘手的问题。当多个中断服务例程或任务需要访问共享资源时,如果没有采取适当的同步措施,就可能出现竞争状态。这可能导致数据不一致、系统崩溃或其他不可预测的行为。死锁是竞争状态的一种极端情况,它发生在两个或多个中断服务例程或任务相互等待对方释放资源时,导致系统无法继续执行。

为了避免这些问题,需要采取一系列措施。首先,中断服务例程应该尽可能简短和高效,避免执行复杂操作或等待不可用资源。其次,需要确保中断管理代码的正确性,避免由于实现错误导致中断被禁用。最后,对共享资源的访问需要采取适当的同步措施,如使用锁、信号量或互斥量等机制来避免竞争状态和死锁的发生。

竞争状态(Race Condition)和死锁(Deadlock)都是并发编程中常见的问题:

  • 竞争状态 指的是当两个或多个进程在没有适当同步的情况下,访问共享的数据或资源,并且至少有一个进程在修改这些数据或资源时,最终的结果将取决于这些线程或进程的执行顺序。由于进程调度的不确定性,这可能导致不可预测和不可重复的结果。竞争状态通常是由于缺乏适当的同步机制(如互斥锁、信号量等)来确保对共享数据的互斥访问而引起的。
  • 死锁 则是指两个或多个进程在等待对方释放资源时陷入无限等待的状态,导致系统无法继续执行。这通常是由于每个进程都持有一个资源并请求另一个进程持有的资源,从而形成了一个循环等待的情况。如果没有外部干预,死锁将导致系统停滞不前,无法完成任何有用的工作。

如果中断服务例程和应用程序服务之间共享变量或数据结构(如队列),则访问这些资源构成了潜在竞争状态和死锁条件,因为你永远无法准确知道何时会执行中断服务例程。

下图展示了一种典型结构,其中中断服务例程与正常应用程序都访问同一共享资源,该共享资源是一个复杂数据结构。当应用程序正在访问共享资源时发生中断,就会出现竞争状态。想象一下,如果应用程序在读取数据的过程中(该读取操作不是原子操作)发生了中断,中断服务例程将会暂停应用程序的读取操作,转而执行中断服务例程,在此期间修改数据,然后返回到应用程序。这样,应用程序将会看到损坏的数据——部分是新的数据,部分是旧的数据。

在这里插入图片描述
对于这个问题,有多种解决方案,但它们都涉及到对共享资源的序列化访问。一种方法是在应用程序读取数据之前禁用中断,并在访问完成后重新启用中断。另一种方法是使用互斥信号量来保护数据,如下图所示。
在这里插入图片描述
在这张图中,共享资源 是受互斥信号量保护的;当调用 getData()setData() 函数时,会给资源 加锁 。当函数完成时,会给资源 解锁 。如果 中断服务例程 在尝试访问数据时等待信号量锁,就可能发生死锁。由于中断服务例程已经中断了拥有锁的 应用程序,假如中断服务程序一直等待信号量锁,则应用程序永远没有机会移除锁,因为它得不到运行,无法释放锁。当然,解决方案是中断服务例程不能等待锁。新数据可以被丢弃,或者可以创建两个共享资源,但规定中断服务例程或应用程序一次只能锁定其中一个资源。后一种解决方案有时被称为“双缓冲区”。双缓冲区方案比较复杂,如果你不希望这么做,那么唯一的选项是应用程序读取数据之前禁用中断,通常这是简单又常用的方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/496653.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

闲扯B-Tree和B+Tree的异同

我们今天要聊一个比较硬的话题: 一个 / 传说中的 / 惊天动地的 / 牛逼的 / 大家都听说过的 / 却又很少实际深入接触到的 / 但又基本每天都在使用的数据结构。(大家注意看上一句话的定语) 我记得我最先接触B树是在大学学数据结构的时候&#x…

能在手机上运行,仅仅0.5B大小的小语言模型MobiLlama

模型介绍 该模型基于LLaMA-7B架构设计,旨在能够在边缘设备上高效运行,无需将数据发送到远程服务器或云端处理。如智能手机、平板电脑、智能手表等。MobiLlama模型虽然体积小、对资源的需求低,但仍能提供高精度的语言理解和生成能力。项目还提…

redis是单线程,为什么这么快?

redis是纯内存操作,C语言编写,执行速度非常快。 采用单线程,避免不必要的上下文切换,不用考虑线程安全问题。 采用I/O多路复用模型,非阻塞I/O。 例如:bgsave和bgrewriteaof都是在后台执行操作&#xff0…

软考50-上午题-【数据库】-SQL访问控制

一、SQL访问控制 数据控制,控制的是用户对数据的存储权力,由DBA决定。 DBA:数据库管理员。 DBMS数据控制应该具有一下功能: 1-1、授权语句格式 说明: 示例: 1-2、收回权限语句格式 示例: PUBLI…

亿道信息新品EM-T195轻薄型工业平板,隆重登场!

EM-T195是一款轻巧但坚固的平板电脑,仅 650克重、10.5mm毫米厚,即使没有额外的便携配件进行辅助,您也可以轻松将其长时间随身携带。耐用性外壳完全密封,防尘防潮;出色的坚固性和可靠性,使T195天生适合在苛刻…

雾锁王国服务器怎么建?雾锁王国服务器搭建方法

雾锁王国Enshrouded服务器搭建怎么搭建?非常简单,阿里云计算巢雾锁王国程序,可以一键搭建雾锁王国多人联机服务器,腾讯云是基于雾锁王国镜像系统,阿里云服务网aliyunfuwuqi.com汇总雾锁王国服务器搭建,超简…

面试经典150题【31-40】

文章目录 面试经典150题【31-40】76.最小覆盖字串36.有效的数独54.螺旋矩阵48.旋转图像73.矩阵置零289.生命游戏383.赎金信205.同构字符串290.单词规律242.有效的字母异位词 面试经典150题【31-40】 76.最小覆盖字串 基本思路很简单,就是先移动右边到合适位置。再移…

在from子句中使用子查询

目录 查询每个部门的编号、名称、位置、部门人数、平均工资 多表查询分组统计 子查询分组统计 Oracle从入门到总裁:https://blog.csdn.net/weixin_67859959/article/details/135209645 为了解释这种查询的作用,下面做一个简单的查询 查询每个部门的编号、名称、…

ViewModel 原理

在现代Android应用开发中,ViewModel是架构组件库的一个关键部分,它在提高应用的稳定性和性能方面发挥着重要作用。在这篇文章中,我们将深入探讨ViewModel的工作原理和最佳实践。 ViewModel简介 ViewModel是Android Jetpack架构组件的一部分…

图论(算法竞赛、蓝桥杯)--Dijkstra算法最短路

1、B站视频链接&#xff1a;D02 最短路 Dijkstra 算法_哔哩哔哩_bilibili 题目链接&#xff1a;【模板】单源最短路径&#xff08;弱化版&#xff09; - 洛谷 #include <bits/stdc.h> using namespace std; #define INF 2147483647 int n,m,s,a,b,c; const int N100010…

c++学习记录 vector容器—赋值操作

函数原型&#xff1a; vector& operator(const vector& vec); //重载等号操作符assign(beg,end); //将[beg,end)区间中的数据拷贝赋值给本身assign(n,elem); //将n个el…

DAY12_VUE基本用法详细版

目录 0 HBuilderX酷黑主题修改注释颜色1 VUE1.1 VUE介绍1.2 Vue优点1.3 VUE入门案例1.3.1 导入JS文件1.3.2 VUE入门案例 1.4 VUE基本用法1.4.1 v-cloak属性1.4.2 v-text指令1.4.3 v-html指令1.4.4 v-pre指令1.4.5 v-once指令1.4.6 v-model指令1.4.7 MVVM思想 1.5 事件绑定1.5.1…