在中断服务程序(ISR, Interrupt Service Routine)中直接使用互斥锁(mutex)和信号量(semaphore)是有风险的,因为这些同步机制通常不是中断安全的。但是,可以通过一些方法来安全地在 ISR 中使用互斥锁和信号量。
1. 中断安全的互斥锁和信号量
在某些RTOS(实时操作系统)中,提供了中断安全的互斥锁和信号量的实现。这些实现通常在内部做了特殊的处理,使其可以在 ISR 中安全地使用。例如,在 FreeRTOS 中,可以使用互斥锁和信号量的中断安全版本。
2. 通过任务队列传递信号
一种常用的方法是不在 ISR 中直接使用互斥锁或信号量,而是将请求封装成消息,并通过任务队列(task queue)传递给任务(task)。任务在安全的上下文中处理这些消息,从而间接地使用互斥锁或信号量。
3. 禁用中断
在 ISR 中短暂禁用中断可以确保互斥锁和信号量操作的原子性。但是这种方法需要非常小心,因为长时间禁用中断会导致中断丢失或系统响应变慢。
示例代码
下面是一个简单的示例,展示了如何在 ISR 中使用互斥锁和信号量:
示例 1: 使用任务队列传递信号
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
// 定义一个任务队列
QueueHandle_t xQueue;
// 定义一个信号量
SemaphoreHandle_t xSemaphore;
void vISRHandler(void *pvParameters)
{
// 在 ISR 中发送消息给任务队列
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(xQueue, &xHigherPriorityTaskWoken, &xHigherPriorityTaskWoken);
}
void vTaskFunction(void *pvParameters)
{
for(;;)
{
// 从任务队列中接收消息
if (xQueueReceive(xQueue, NULL, portMAX_DELAY))
{
// 在任务中安全地使用互斥锁
xSemaphoreTake(xSemaphore, portMAX_DELAY);
// 执行关键操作
// ...
xSemaphoreGive(xSemaphore);
}
}
}
void main()
{
xQueue = xQueueCreate(10, sizeof(BaseType_t));
xSemaphore = xSemaphoreCreateMutex();
// 创建任务
xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
// 初始化 ISR
// ...
// 开始调度
vTaskStartScheduler();
}
示例 2: 使用中断安全的信号量
#include "FreeRTOS.h"
#include "semphr.h"
SemaphoreHandle_t xSemaphore;
void vISRHandler(void *pvParameters)
{
// 在 ISR 中使用中断安全的信号量
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken)
{
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
void vTaskFunction(void *pvParameters)
{
for(;;)
{
// 在任务中等待信号量
xSemaphoreTake(xSemaphore, portMAX_DELAY);
// 执行关键操作
// ...
}
}
void main()
{
xSemaphore = xSemaphoreCreateBinary();
// 创建任务
xTaskCreate(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
// 初始化 ISR
// ...
// 开始调度
vTaskStartScheduler();
}
注意事项
- 中断禁用:在 ISR 中禁用中断的时间要尽可能短,以避免影响系统的实时性。
- 中断安全:确保使用的互斥锁和信号量是中断安全的。
- 任务队列:使用任务队列传递信号是一种常用且安全的方法。