FreeRTOS与RT-Thread对于中断及临界区的处理

1、中断的管理

Freertos对系统中断的管理是通过操作 BASEPRI 寄存器来实现的,头文件FreeRTOSConfig.h中对管理的优先级进行了定义

#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5

设置freertos可以管理的最大优先级,高于优先级5的不归freertos管,低于此优先级的中断服务函数可以安全的调用freertos的api,高于该优先级的不能调用api(freertos管理不了)

RT-Thread中对中断的管理则是通过操作PRIMASK寄存器来实现的,直接关闭所有中断,NMI FAULT 和硬 FAULT 除外

2、临界区的处理

Freertos的处理方法:

进入临界区时,关闭管理级别的中断,进入次数uxCriticalNesting加1。此时MCU上除了优先级大于设定阈值的中断可以被响应,其他的中断以及系统调度器被关闭(systick的中断优先级是最低的),

在port.c中的xPortStartScheduler()中设置了SYSTICK的优先级中断

/* Make PendSV and SysTick the lowest priority interrupts. */

portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;// 设置最低中断

portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;

Freertos进入临界区

void vPortEnterCritical( void )
{
	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 );
	}
}

退出时,对进入次数uxCriticalNesting减1,直到退出最外层的嵌套(uxCriticalNesting为0)时,才设置允许管理中断,允许系统调度器工作

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

RTT的处理方法:

在进入临界区时,系统关闭一次中断(关闭所有中断,除了NMI FAULT 和硬 FAULT),将进入次数rt_scheduler_lock_nest加1后就立即开启了中断。在此消耗的系统资源极小可忽略不计,对系统其它中断几乎不产生任何影响。

void rt_enter_critical(void)
{
    register rt_base_t level;

    /* disable interrupt */
    level = rt_hw_interrupt_disable(); // 仅用作防止访问冲突

    /*
     * the maximal number of nest is RT_UINT16_MAX, which is big
     * enough and does not check here
     */
    rt_scheduler_lock_nest ++;  // 不为0时,调度器不启用 rt_schedule判断了该值是否为0
    
    /* enable interrupt */
    rt_hw_interrupt_enable(level);
}

而进入临界区后,系统则关闭了调度器,使处于临界区的任务不被其它任务抢占打断。在执行系统调度的函数中判断rt_scheduler_lock_nest进入临界区的次数是否为0,若还未全部退出临界区保护,则调度器不被启用,调度器上锁。

void rt_schedule(void)
{
    rt_base_t level;
    struct rt_thread *to_thread;
    struct rt_thread *from_thread;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    /* check the scheduler is enabled or not */
    if (rt_scheduler_lock_nest == 0) // 临界段保护判断
    {
    // 执行线程调度切换
    }
}

退出临界区,与进入临界区类似,在进入时关闭总中断,对进入次数减1。且判断当前是否有任务需要执行一次调度,而后打开总中断


/**
 * This function will unlock the thread scheduler.
 */
void rt_exit_critical(void)
{
    register rt_base_t level;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    rt_scheduler_lock_nest --;
    if (rt_scheduler_lock_nest <= 0)
    {
        rt_scheduler_lock_nest = 0;
        /* enable interrupt */
        rt_hw_interrupt_enable(level);

        if (rt_current_thread)
        {
            /* if scheduler is started, do a schedule */
            rt_schedule();
        }
    }
    else
    {
        /* enable interrupt */
        rt_hw_interrupt_enable(level);
    }
}

总的来说,freertos对临界区的处理较为细致,它将归于系统管理的中断及调度器全部关闭,直至退出临界区。此时对系统高优先级的中断执行不产生任何影响。

需要明白的一点是,RTT对临界区的处理不是单纯依靠的关中断来实现的,通过不切任务达到只有一个任务在执行而进入临界区。

它对临界区的处理直接了当,仅是将系统调度器关闭(称作调度器锁),调度器锁上锁后如果产生了中断信号,系统仍然可以响应中断。不过若是其他中断的实时性要求不高,可以使用中断锁来进入临界区rt_interrupt_enter,此时所有中断及调度器都被关闭。

上一篇:单链表反转(四种方式实现)


下一篇:36. 二叉搜索树与双向链表