一、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;
}
三、總結
根據不同的鎖需要,採用不同的鎖機制。