RT_Thread 临界区访问控制

一、WHY

   什么是临界区,为什么我们需要临界区的访问控制?首先来看下wiki对临界区的定义:


在同步的程序设计中,临界区段(Critical section)指的是一个访问共享资源(例如:共享设备或是共享存储器)的程序片段,而这些共享资源无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待,以确保这些共享资源是被异或的使用。比如打印机。


二、RT_Thread 临界区访问控制机制

2.1 全局中断:禁止全局中断,禁止抢占

全局中断开关也称为中断锁,是禁止多线程访问临界区最简单的一种方式,即通过关闭中断的方式,来保证当前线程不会被其他事件打断(因为整个系统已经不再响应那些可以触发线程重新调度的外部事件),也就是当前线程不会被抢占,除非这个线程主动放弃了处理器控制权,其函数接口如下:

rt_base_t rt_hw_interrupt_disable(void);
void rt_hw_interrupt_enable(rt_base_t level);

优点:使用中断锁来操作临界区的方法可以应用于任何场合,且其他几类同步方式都是依赖于中断锁而实现的,可以说中断锁是最强大的和最高效的同步方法。

缺点:在中断关闭期间系统将不再响应任何中断,也就不能响应外部的事件。所以中断锁对系统的实时性影响非常巨大,当使用不当的时候会导致系统完全无实时性可言。它属于粒度最大的访问控制机制

实现举例:

/*FILE: libcpu/arm/contex-m4/context_gcc.S*/
/*
 * rt_base_t rt_hw_interrupt_disable();
 */
.global rt_hw_interrupt_disable
.type rt_hw_interrupt_disable, %function
rt_hw_interrupt_disable:
    MRS     r0, PRIMASK
    CPSID   I
    BX      LR

/*
 * void rt_hw_interrupt_enable(rt_base_t level);
 */
.global rt_hw_interrupt_enable
.type rt_hw_interrupt_enable, %function
rt_hw_interrupt_enable:
    MSR     PRIMASK, r0
    BX      LR

2.2 使能全局中断,禁止抢占

在这种情况下,打开全局中断,关闭调度功能,因为禁止了线程的调度器,所以哪怕是ISR中使一些thread恢复为ready 状态,其他线程也仍然不能抢占当前进程的运行。因此,该机制可以用来禁止多thread间的临界区同步。

优点:相比2.1,不会禁止全局中断, ISR仍然可以得到处理。

缺点:因为禁止了抢占,因为ISR资源(锁,信号量等)释放而重新ready的高优先级thread并不能抢占当前的thread,所以仍然会影响到实时性。

代码实现:

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 ++;

    /* enable interrupt */
    rt_hw_interrupt_enable(level);
}


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);
    }
}

2.3 使能全局中断,使能抢占,使用互斥锁同步

该机制会在临界区访问之前去检查锁是否有效,如果有效,则上锁并开始访问临界区,访问结束后释放锁;如果无效,则可以阻塞等待该锁有效。通过该机制,可以保证只有一个thread可以访问临界区。

优点:对系统的ISR处理和其他thread影响最小。

缺点:因为存在着锁的获取,释放,容易出现编程错误,产生死锁。

实现代码:

/**
 * This function will take a mutex, if the mutex is unavailable, the
 * thread shall wait for a specified time.
 *
 * @param mutex the mutex object
 * @param time the waiting time
 *
 * @return the error code
 */
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time)
{
    register rt_base_t temp;
    struct rt_thread *thread;

    /* this function must not be used in interrupt even if time = 0 */
    RT_DEBUG_IN_THREAD_CONTEXT; 【1】

    /* get current thread */
    thread = rt_thread_self();

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

    /* reset thread error */
    thread->error = RT_EOK;

    if (mutex->owner == thread)
    {
        /* it's the same thread */
        mutex->hold ++;   【2】
    }
    else
    {
__again:
        /* The value of mutex is 1 in initial status. Therefore, if the
         * value is great than 0, it indicates the mutex is avaible.
         */
        if (mutex->value > 0)
        {
            /* mutex is available */
            mutex->value --;

            /* set mutex owner and original priority */
            mutex->owner             = thread;
            mutex->original_priority = thread->current_priority; 【3】
            mutex->hold ++;
        }
        else
        {
            /* no waiting, return with timeout */
            if (time == 0)
            {
                /* set error as timeout */
                thread->error = -RT_ETIMEOUT;

                /* enable interrupt */
                rt_hw_interrupt_enable(temp);

                return -RT_ETIMEOUT;
            }
            else
            {
                /* mutex is unavailable, push to suspend list */

                /* change the owner thread priority of mutex */
                if (thread->current_priority < mutex->owner->current_priority)
                {
                    /* change the owner thread priority */
                    rt_thread_control(mutex->owner,
                                      RT_THREAD_CTRL_CHANGE_PRIORITY,
                                      &thread->current_priority); 【4】
                }

                /* suspend current thread */
                rt_ipc_list_suspend(&(mutex->parent.suspend_thread),
                                    thread,
                                    mutex->parent.parent.flag);

                /* has waiting time, start thread timer */
                if (time > 0)
                {
                    /* reset the timeout of thread timer and start it */
                    rt_timer_control(&(thread->thread_timer),
                                     RT_TIMER_CTRL_SET_TIME,
                                     &time);
                    rt_timer_start(&(thread->thread_timer)); 【5】
                }

                /* enable interrupt */
                rt_hw_interrupt_enable(temp);

                /* do schedule */
                rt_schedule();     【6】

                if (thread->error != RT_EOK)
                {
                    /* interrupt by signal, try it again */
                    if (thread->error == -RT_EINTR) goto __again; 【7】

                    /* return error */
                    return thread->error;    【8】
                }
                else
                {
                    /* the mutex is taken successfully. */
                    /* disable interrupt */
                    temp = rt_hw_interrupt_disable();
                }
            }
        }
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);

    return RT_EOK;
}

【1】:需要保证调用该function的时候处于非中断上下文状态;why?

【2】:使用hold 计数来记录同一个thread占用锁的次数,方便同一线程的不同子函数的编程;

【3】【4】:为了消除优先级反转的问题,当等待互斥锁的thread A的优先级比占用锁的thread B优先级高,则提高thread B的优先级。

【5】设定等待锁time out时间并开启timer

【6】调度调度器

【7】如果当前thread等待锁被signal 打断,则再次等待锁

【8】time out 时间到,退出等待

对应着锁释放代码实现:

/**
 * This function will release a mutex, if there are threads suspended on mutex,
 * it will be waked up.
 *
 * @param mutex the mutex object
 *
 * @return the error code
 */
rt_err_t rt_mutex_release(rt_mutex_t mutex)
{
    register rt_base_t temp;
    struct rt_thread *thread;
    rt_bool_t need_schedule;

    need_schedule = RT_FALSE;

    /* only thread could release mutex because we need test the ownership */
    RT_DEBUG_IN_THREAD_CONTEXT;

    /* get current thread */
    thread = rt_thread_self();

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

    /* mutex only can be released by owner */
    if (thread != mutex->owner)
    {
        thread->error = -RT_ERROR;

        /* enable interrupt */
        rt_hw_interrupt_enable(temp);

        return -RT_ERROR;
    }

    /* decrease hold */
    mutex->hold --;
    /* if no hold */
    if (mutex->hold == 0)
    {
        /* change the owner thread to original priority */
        if (mutex->original_priority != mutex->owner->current_priority)
        {
            rt_thread_control(mutex->owner,
                              RT_THREAD_CTRL_CHANGE_PRIORITY,
                              &(mutex->original_priority));
        }

        /* wakeup suspended thread */
        if (!rt_list_isempty(&mutex->parent.suspend_thread))
        {
            /* get suspended thread */
            thread = rt_list_entry(mutex->parent.suspend_thread.next,
                                   struct rt_thread,
                                   tlist);

            /* set new owner and priority */
            mutex->owner             = thread;
            mutex->original_priority = thread->current_priority;
            mutex->hold ++;

            /* resume thread */
            rt_ipc_list_resume(&(mutex->parent.suspend_thread));

            need_schedule = RT_TRUE;
        }
        else
        {
            /* increase value */
            mutex->value ++;

            /* clear owner */
            mutex->owner             = RT_NULL;
            mutex->original_priority = 0xff;
        }
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);

    /* perform a schedule */
    if (need_schedule == RT_TRUE)
        rt_schedule();

    return RT_EOK;
}

三、总结

     根据不同的锁需要,采用不同的锁机制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章