FreeRTOS学习九(锁机制)

        在执行代码时,有的代码开始执行,是不允许被打断的。这部分的代码也叫作临界段代码。为了确保这些代码不被中断而增加了临界区的概念。所谓的临界区保护重要流程在执行的时候不会被其他事情打断。等流程运行结束后,再将程序重新恢复到原来的状态。这种临界区保护的做法也叫锁机制。在裸机系统中,只能通过开关中断来实现锁机制。在RTOS中,增加了多种锁机制。有调度锁、中断锁、任务锁和互斥锁

中断锁

        顾名思义,中断锁的意思是防止程序进行中断切换,即不允许被其他中断打断。FreeRTOS中没有专门的中断锁函数,但是有临界区保护的代码。

重要函数如下:

#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() //进临界区
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) //退出临界区

#define portENTER_CRITICAL() vPortEnterCritical()//进临界区
#define portEXIT_CRITICAL() vPortExitCritical()//退出临界区


#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()//进临界区
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x)//退出临界区

以上这几个函数都是关于中断锁的控制。portENTER_CRITICAL()函数调用vPortEnterCritical()函数,进入该函数。

void vPortEnterCritical( void )
{
/* The interrupt should not be masked when this API is 1st called.
in order to check the error usage, typical case is:
hal_nvic_save_and_set_interrupt_mask()

xQueueGenericSend() // Calling an FreeRTOS API function from within a critical section
hal_nvic_restore_interrupt_mask()
*/

configASSERT(uxCriticalNesting || !__get_BASEPRI());

portDISABLE_INTERRUPTS();

uxCriticalNesting++;

/* This is not the interrupt safe version of the enter critical function so
assert() if it is being called from an interrupt context.  Only API
functions that end in "FromISR" can be used in an interrupt.  Only assert if
the critical nesting count is 1 to protect against recursive calls if the
assert function also uses a critical section. */

if( uxCriticalNesting == 1 )
{
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
}
}

发现该函数还是调用了portDISABLE_INTERRUPTS();函数。但是不同的是,在该函数中有变量uxCriticalNesting,该变量的作用是什么呢?看vPortExitCritical()函数

void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();
}
}

函数中对uxCriticalNesting变量进行了判断,只有该变量为0时,才会执行退出临界区的操作。所以,综上所述,portENTER_CRITICAL()和portEXIT_CRITICAL()函数必须是成对调用。而portDISABLE_INTERRUPTS()和portENABLE_INTERRUPTS()是可以不成对出现的。

portSET_INTERRUPT_MASK_FROM_ISR()函数也是关闭中断

static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
mrs ulReturn, basepri
msr basepri, ulNewBASEPRI
dsb
isb
}

return ulReturn;
}

        从源码中可以看到该函数在关闭中断后,将原来的寄存器值返回。那么可以通过portCLEAR_INTERRUPT_MASK_FROM_ISR函数来恢复之前的状态。

        这里介绍一下basepri寄存器。如果向basepri寄存器写0的话,就会停止中断屏蔽如果要屏蔽优先级不高于xx的中断,则可以将该优先级传给这个参数。例如上边的代码中将configMAX_SYSCALL_INTERRUPT_PRIORITY传递给了basepri寄存器,configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY值为4.则表示屏幕优先级不高于4的中断。即屏蔽PenSV和systick中断。

#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    0x4

调度锁

        调度锁的意思是防止程序进行上下文切换。当有一些数据是不允许上下文切换的话,就可以通过调度锁来保证。

重要的函数如下:

vTaskSuspendAll( void ) 开启调度锁(不允许调度)

xTaskResumeAll(void) 关闭调度锁(允许调度)
void vTaskSuspendAll( void )
{
/* A critical section is not required as the variable is of type
BaseType_t.  Please read Richard Barry's reply in the following link to a
post in the FreeRTOS support forum before reporting this as a bug! -
http://goo.gl/wu4acr */
++uxSchedulerSuspended;
}

可以看到该函数并没有执行其他的操作,只是将uxSchedulerSuspended计数值+1。那这个变量究竟是什么呢?

在看上下文切换函数uxSchedulerSuspended

void vTaskSwitchContext( void )
{
if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
{
/* The scheduler is currently suspended - do not allow a context
switch. */
xYieldPending = pdTRUE;
}

该函数在进行上下文切换的时候会先检查一下该变量,如果该变量为非零。则表示当前不允许进行任务切换。

BaseType_t xTaskResumeAll( void )
{
TCB_t *pxTCB = NULL;
BaseType_t xAlreadyYielded = pdFALSE;

/* If uxSchedulerSuspended is zero then this function does not match a
previous call to vTaskSuspendAll(). */
configASSERT( uxSchedulerSuspended );

/* It is possible that an ISR caused a task to be removed from an event
list while the scheduler was suspended.  If this was the case then the
removed task will have been added to the xPendingReadyList.  Once the
scheduler has been resumed it is safe to move all the pending ready
tasks from this list into their appropriate ready list. */

taskENTER_CRITICAL();
{
--uxSchedulerSuspended;

        在该函数中,会将该变量的计数值减一。值得注意的是,在进行变量减一的操作前调用了taskENTER_CRITICAL()函数。看定义如下:

#define taskENTER_CRITICAL() portENTER_CRITICAL()

        所以,该函数是调用了临界区保护。就是说在操作uxSchedulerSuspended参数的时候先进行了临界区保护,等操作结束后,在退出临界区。

任务锁

        为了防止当前任务的执行被其他高优先级的任务打断而提供的锁机制就是任务锁。FreeRTOS没有专门的任何锁函数,但是可以通过已有的功能来实现。

1.通过给调度器加锁

        利用FreeRTOS的调度锁功能给调度器加锁的话,将关闭任务切换功能,从而高优先级任务也就无法抢占低优先级任务的执行。同时高优先级任务也是无法向低优先级任务切换的。另外,调度锁只是禁止了调度器工作,并没有关闭任何中断。

2.通过关闭任务切换中断pendSV和系统时钟节拍中断systick

        利用FreeRTOS的任务代码临界段处理函数就可以关闭PenSV中断和systick中断。操作寄存器basepri关闭。

        可以说,任务锁和调度锁差别不大

互斥锁

        当程序在访问某些共享数据的时候,可能会出现同时改变共享资源的情况。这种情况可能会造成程序运行出现异常。为了防止这种情况出现,增加了互斥锁的概念。互斥锁是依靠互斥信号量来实现的,具体的互斥信号量的内容,可以参考之前的文章。附上链接。

FreeRTOS学习五(信号量)_qq_34981的博客-CSDN博客

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS没有提供读写的标准实现,但可以通过互斥量和信号量来实现读写。 读写是用于读写访问共享资源的一种同步机制,它允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。 以下是一个基于FreeRTOS的简单实现: ``` #include "FreeRTOS.h" #include "semphr.h" /* 定义读写结构体 */ typedef struct { SemaphoreHandle_t mutex; // 互斥量 SemaphoreHandle_t rw_sem; // 信号量 int readers; // 当前读者数量 } rwlock_t; /* 初始化读写 */ void rwlock_init(rwlock_t *rwlock) { rwlock->mutex = xSemaphoreCreateMutex(); rwlock->rw_sem = xSemaphoreCreateBinary(); rwlock->readers = 0; xSemaphoreGive(rwlock->rw_sem); } /* 获取读 */ void rwlock_read_lock(rwlock_t *rwlock) { xSemaphoreTake(rwlock->mutex, portMAX_DELAY); rwlock->readers++; if (rwlock->readers == 1) { xSemaphoreTake(rwlock->rw_sem, portMAX_DELAY); } xSemaphoreGive(rwlock->mutex); } /* 释放读 */ void rwlock_read_unlock(rwlock_t *rwlock) { xSemaphoreTake(rwlock->mutex, portMAX_DELAY); rwlock->readers--; if (rwlock->readers == 0) { xSemaphoreGive(rwlock->rw_sem); } xSemaphoreGive(rwlock->mutex); } /* 获取写 */ void rwlock_write_lock(rwlock_t *rwlock) { xSemaphoreTake(rwlock->rw_sem, portMAX_DELAY); } /* 释放写 */ void rwlock_write_unlock(rwlock_t *rwlock) { xSemaphoreGive(rwlock->rw_sem); } ``` 在这个实现中,我们使用了一个互斥量来保证多个线程不会同时访问`readers`计数器。`rw_sem`是一个二元信号量,用于控制读者和写者之间的访问。当有一个写者时,读者需要等待,直到写者释放信号量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值