关闭中断也叫中断锁,是禁止多任务访问临界区最简单的一种方式。
当中断关闭的时候,就意味着当前任务不会被其他事件打断,也就是当前线程不会被抢占,除非这个任务主动放弃了处理器控制权。
关闭中断/恢复中断API接口由BSP实现,根据平台的不同其实现方式也大不相同
警告: 由于关闭中断会导致整个系统不能响应外部中断,所以在使用关闭中断做为互斥访问临界区的手段时,首先必须需要保证关闭中断的时间非常短,例如数条机器指令。
使用中断锁来操作系统的方法可以应用于任何场合,且其他几类同步方式都是依赖于中断锁而实现的。
中断锁是最强大的和最高效的同步方法。
问题:使用中断锁最主要的问题在于,在中断关闭期间系统将不再响应任何中断,也就不能响应外部的事件。在使用中断锁时,需要确保关闭中断的时间非常短。例如:为了保证一行代码(例如赋值)的互斥运行。
调度器锁住也能让当前运行的任务不被换出,直到调度器解锁。
但是,对调度器上锁,系统依然能响应外部中断,中断服务例程依然能进行相应的响应。所以在使用调度器上锁的方式进行任务同步时,需要考虑好任务访问的临界资源是否会被中断服务例程所修改,如果可能会被修改,那么将不适合采用此种方式进行同步。
注意: rt_enter_critical/rt_exit_critical可以多次嵌套调用,但每调用一次rt_enter_critical就必须相对应地调用一次rt_exit_critical退出操作,嵌套的最大深度是65535。
它不能被用于中断与线程间的同步或通知。
如果执行调度器锁的时间过长,会对系统的实时性造成影响
信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。
信号量是一种非常灵活的同步方式,可以运用在多种场合中。形成锁,同步,资源计数等关系,也能方便的用于线程与线程,中断与线程的同步中。
例如:两个线程用来进行任务间的执行控制转移,信号量的值初始化成具备0个信号量资源实例,而等待线程先直接在这个信号量上进行等待。
当信号线程完成它处理的工作时,释放这个信号量,以把等待在这个信号量上的线程唤醒,让它执行下一部分工作。这类场合也可以看成把信号量用于工作完成标志:信号线程完成它自己的工作,然后通知等待线程继续下一部分工作。
单一的锁常应用于多个线程间对同一临界区的访问。
信号量在作为锁来使用时,通常应将信号量资源实例初始化成1,代表系统默认有一个资源可用。
当线程需要访问临界资源时,它需要先获得这个资源锁。当这个线程成功获得资源锁时,其他打算访问临界区的线程将被挂起在该信号量上,这是因为其他线程在试图获取这个锁时,这个锁已经被锁上(信号量值是0)。当获得信号量的线程处理完毕,退出临界区时,它将会释放信号量并把锁解开,而挂起在锁上的第一个等待线程将被唤醒从而获得临界区的访问权。因为信号量的值始终在1和0之间变动,所以这类锁也叫做二值信号量。
例如一个中断触发,中断服务例程需要通知线程进行相应的数据处理。
首先,设置信号量的初始值是0,这个时候线程在信号量上挂起等待,直到信号量被释放。
当中断触发时,先进行与硬件相关的动作,例如从硬件的I/O口中读取相应的数据,并确认中断以清除中断源,而后释放一个信号量来唤醒相应的线程以做后续的数据处理。
警告:中断与线程间的互斥不能采用信号量(锁)的方式,而应采用中断锁。
资源计数适合于线程间速度不匹配的场合。
这个时候信号量可以做为前一线程工作完成的计数,而当调度到后一线程时,它可以以一种连续的方式一次处理数个事件。
例如:生产者与消费者问题中,生产者可以对信号进行多次释放,而后消费者被调度到时能够一次处理多个资源。
注: 一般资源计数类型多是混合方式的线程间同步,因为对于单个的资源处理依然存在线程的多重访问,这就需要对一个单独的资源进行访问、处理,并进行锁方式的互斥操作。
互斥量又叫相互排斥的信号量,是一种特殊的二值性信号量。
它支持互斥量所有权、递归访问以及防止优先级翻转的特性。
互斥量的状态只有两种,开锁或闭锁(两种状态值)。当有线程持有它时,互斥量处于闭锁状态,由这个线程获得它的所有权。相反,当这个线程释放它时,将对互斥量进行开锁,失去它的所有权。
当一个线程持有互斥量时,其他线程将不能够对它进行开锁或持有它,持有该互斥量的线程也能够再次获得这个锁而不被挂起。这样的话,线程递归持有会发生主动挂起(最终形成死锁)。
使用信号量会导致的另一个潜在问题是线程优先级翻转。所谓优先级翻转问题即当一个高优先级线程试图通过信号量机制访问共享资源时,如果该信号量已被一低优先级线程持有,而这个低优先级线程在运行过程中可能又被其它一些中等优先级的线程抢占,因此造成高优先级线程被许多具有较低优先级的线程阻塞,实时性难以得到保证。
在RT-Thread操作系统中实现的是优先级继承算法。优先级继承是通过在线程A被阻塞的期间内,将线程C的优先级提升到线程A的优先级别,从而解决优先级翻转引起的问题。优先级继承协议是指,提高某个占有某种资源的低优先级线程的优先级,使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等,然后执行,而当这个低优先级线程释放该资源时,优先级重新回到初始设定。因此,继承优先级的线程避免了系统资源被任何中间优先级的线程抢占。
警告: 在获得互斥量后,请尽快释放互斥量,并且在持有互斥量的过程中,不得再行
更改持有互斥量线程的优先级。
互斥量的使用比较单一,因为它是信号量的一种,并且它是以锁的形式存在。
1. 线程多次持有互斥量的情况下。这样可以避免同一线程多次递归持有而造成死锁的问题;
2. 可能会由于多线程同步而造成优先级翻转的情况;
另外需要切记的是互斥量不能在中断服务例程中使用。
事件主要用于线程间的同步,它的特点是可以实现一对多,多对多的同步。
一对多:一个线程可等待多个事件的触发:可以是其中任意一个事件唤醒线程进行事件处理的操作;也可以是几个事件都到达后才唤醒线程进行后续的处理;
多对多:事件也可以是多个线程同步多个事件,这种多个事件的集合可以用一个32位无符号整型变量来表示,变量的每一位代表一个事件,线程通过“逻辑与”或“逻辑或”与一个或多个事件建立关联,形成一个事件集。
事件的“逻辑或”也称为是独立型同步,指的是线程与任何事件之一发生同步;事件“逻辑与”也称为是关联型同步,指的是线程与若干事件都发生同步。
RT-Thread定义的事件有以下特点:
- 事件只与线程相关,事件间相互独立:每个线程拥有32个事件标志,采用一个32 bit无符号整型数进行记录,每一个bit代表一个事件。若干个事件构成一个事件集;
- 事件仅用于同步,不提供数据传输功能;
- 事件无排队性,即多次向线程发送同一事件(如果线程还未来得及读走),其效果等同于只发送一次。
在RT-Thread实现中,每个线程都拥有一个事件信息标记,它有三个属性,分别是RT_EVENT_FLAG_AND(逻辑与),RT_EVENT_FLAG_OR(逻辑或)以及RT_EVENT_FLAG_CLEAR(清除标记)。当线程等待事件同步时,可以通过32个事件标志和这个事件信息标记来判断当前接收的事件是否满足同步条件。
它能够在一定程度上替代信号量,用于线程间同步。一个线程或中断服务例程发送一个事件给事件对象,而后等待的线程被唤醒并对相应的事件进行处理。
事件的发送操作在事件未清除前,是不可累计的,而信号量的释放动作是累计的。
接收线程可等待多种事件,即多个事件对应一个线程或多个线程。同时按照线程等待的参数,可选择是“逻辑或”触发还是“逻辑与”触发。信号量只能识别单一的释放动作,而不能同时等待多种类型的释放。
邮箱服务是实时操作系统中一种典型的任务间通信方法,特点是开销比较低,效率较高。
邮箱中的每一封邮件只能容纳固定的4字节内容(针对32位处理系统,指针的大小即为4个字节,所以一封邮件恰好能够容纳一个指针)。
典型的邮箱也称作交换消息。线程或中断服务例程把一封4字节长度的邮件发送到邮箱中。而一个或多个线程可以从
邮箱中接收这些邮件进行处理。
非阻塞方式的邮件发送过程能够安全的应用于中断服务中,是线程,中断服务,定时器向线程发送消息的有效手段。
通常来说,邮件收取过程可能是阻塞的,这取决于邮箱中是否有邮件,以及收取邮件时设置的超时时间。当邮箱中不存在邮件且超时时间不为0时,邮件收取过程将变成阻塞方式。
RT-Thread操作系统的邮箱中可存放固定条数的邮件,邮箱容量在创建/初始化邮箱时设定,每个邮件大小为4字节。当需要在线程间传递比较大的消息时,可以把指向一个缓冲区的指针作为邮件发送到邮箱中。
邮箱是一种简单的线程间消息传递方式,在RT-Thread操作系统的实现中能够一次传递4字节邮件,并且邮箱具备一定的存储功能,能够缓存一定数量的邮件数。
邮箱中一封邮件的最大长度是4字节,所以邮箱能够用于不超过4字节的消息传递,当传送的消息长度大于这个数目时就不能再采用邮箱的方式。
在32位系统上4字节的内容恰好适合放置一个指针,所以邮箱也适合那种仅传递指针的情况。比如新申请内存用来传递数据。
它能够接收来自线程或中断服务例程中不固定长度的消息,并把消息缓存在自己的内存空间中。
其他线程也能够从消息队列中读取相应的消息,而当消息队列是空的时候,可以挂起读取线程。当有新的消息到达时,挂起的线程将被唤醒以接收并处理消息。
消息队列是一种异步的通信方式。先进先出原则(FIFO)。
消息队列可以应用于发送不定长消息的场合,包括线程与线程间的消息交换,以及中断服务例程中发送给线程的消息(中断服务例程不可能接收消息)。
一个线程或中断 发送消息,一个线程处理。
两个线程间可以采用[消息队列+信号量或邮箱]的形式实现。 发送线程通过消息发送的形式发送相应的消息给消息队列,发送完毕后希望获得接收线程的收到确认。
第一种类型的消息使用了邮箱来作为确认标志,而第二种类型的消息采用了信号量来作为确认标志。邮箱做为确认标志,代表着接收线程能够通知一些状态值给发送线程;而信号量作为确认标志只能够单一的通知发送线程,消息已经确认接收。