Try   HackMD

RT-Thread IPC 互斥鎖(mutex)

  • 即一種值為 1 的特殊 semaphore,特別的是具有防止優先級翻轉的特性

結構

File: rtdef.h

#ifdef RT_USING_MUTEX /** * Mutual exclusion (mutex) structure */ struct rt_mutex { struct rt_ipc_object parent; /**< inherit from ipc_object */ rt_uint16_t value; /**< value of mutex */ rt_uint8_t original_priority; /**< priority of last thread hold the mutex */ rt_uint8_t hold; /**< numbers of thread hold the mutex */ struct rt_thread *owner; /**< current owner of mutex */ }; typedef struct rt_mutex *rt_mutex_t; #endif
  • 為了防止優先權翻轉,在持有鎖的過程中可能會被提升優先權,在結構中就需要紀錄原本的優先級。

File: ipc.c

建立 mutex

動態記憶體管理

rt_mutex_create

功能 回傳值
建立 mutex mutex
*name flag
名字 FIFO / PRIO
/** * This function will create a mutex from system resource * * @param name the name of mutex * @param flag the flag of mutex * * @return the created mutex, RT_NULL on error happen * * @see rt_mutex_init */ rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag) { struct rt_mutex *mutex; RT_DEBUG_NOT_IN_INTERRUPT; /* allocate object */ mutex = (rt_mutex_t)rt_object_allocate(RT_Object_Class_Mutex, name); if (mutex == RT_NULL) return mutex;
  • 首先 allocate 一個物件,初始化
/* init ipc object */ rt_ipc_object_init(&(mutex->parent)); mutex->value = 1; mutex->owner = RT_NULL; mutex->original_priority = 0xFF; mutex->hold = 0;
  • value 設為 1,擁有者為 NULL,原始權限最低(255),持有次數為 0
/* set flag */ mutex->parent.parent.flag = flag; return mutex; } RTM_EXPORT(rt_mutex_create);
  • 同時填入 flag

靜態記憶體管理

rt_mutex_init

功能 回傳值
初始化 mutex RT_EOK
mutex *name flag
mutex 本體 名字 FIFO / PRIO
/** * This function will initialize a mutex and put it under control of resource * management. * * @param mutex the mutex object * @param name the name of mutex * @param flag the flag of mutex * * @return the operation status, RT_EOK on successful */ rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag) { /* parameter check */ RT_ASSERT(mutex != RT_NULL); /* init object */ rt_object_init(&(mutex->parent.parent), RT_Object_Class_Mutex, name); /* init ipc object */ rt_ipc_object_init(&(mutex->parent)); mutex->value = 1; mutex->owner = RT_NULL; mutex->original_priority = 0xFF; mutex->hold = 0; /* set flag */ mutex->parent.parent.flag = flag; return RT_EOK; } RTM_EXPORT(rt_mutex_init);
  • 這裡不需要 allocate,只需要初始化物件

刪除 mutex

動態記憶體管理

rt_mutex_delete

功能 回傳值
刪除 mutex RT_EOK
mutex
欲刪除的 mutex
/** * This function will delete a mutex object and release the memory * * @param mutex the mutex object * * @return the error code * * @see rt_mutex_detach */ rt_err_t rt_mutex_delete(rt_mutex_t mutex) { RT_DEBUG_NOT_IN_INTERRUPT; /* parameter check */ RT_ASSERT(mutex != RT_NULL); RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent) == RT_FALSE); /* wakeup all suspend threads */ rt_ipc_list_resume_all(&(mutex->parent.suspend_thread)); /* delete semaphore object */ rt_object_delete(&(mutex->parent.parent)); return RT_EOK; } RTM_EXPORT(rt_mutex_delete);
  • 與 semaphore 類似,先將正在等待此鎖的所有 thread 叫醒,接著透過 rt_object_delete 刪除 mutex

靜態記憶體管理

rt_mutex_detach

功能 回傳值
刪除 mutex RT_EOK
mutex
欲刪除的 mutex
/** * This function will detach a mutex from resource management * * @param mutex the mutex object * * @return the operation status, RT_EOK on successful * * @see rt_mutex_delete */ rt_err_t rt_mutex_detach(rt_mutex_t mutex) { /* parameter check */ RT_ASSERT(mutex != RT_NULL); RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent)); /* wakeup all suspend threads */ rt_ipc_list_resume_all(&(mutex->parent.suspend_thread)); /* detach semaphore object */ rt_object_detach(&(mutex->parent.parent)); return RT_EOK; } RTM_EXPORT(rt_mutex_detach);
  • 這裡則運用 rt_object_detach 刪除

使用 mutex

  • 呼叫 rt_mutex_take 來取得鎖

rt_mutex_take

功能 回傳值
要求 mutex RT_EOK
mutex time
欲要求的 mutex 等待時間(如果需要)
/** * 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; /* parameter check */ RT_ASSERT(mutex != RT_NULL); RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); /* get current thread */ thread = rt_thread_self(); /* disable interrupt */ temp = rt_hw_interrupt_disable();
  • 下面將會修改 mutex 的一些資料,這裡先將中斷關閉
RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mutex->parent.parent))); RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_take: current thread %s, mutex value: %d, hold: %d\n", thread->name, mutex->value, mutex->hold)); /* reset thread error */ thread->error = RT_EOK; if (mutex->owner == thread) { /* it's the same thread */ mutex->hold ++; }
  • 若此 mutex 的擁有者與要求著相同,持有數加 1
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; mutex->hold ++; }
  • 如果不同,且 mutex 可用,先將 value
  • 設定所有者,紀錄當前權限,持有數加 1
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; }
  • 如果不可用,且不等待,則啟用中斷,return -RT_ETIMEOUT
else { /* mutex is unavailable, push to suspend list */ RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_take: suspend thread: %s\n", thread->name)); /* 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); }
  • 若需要等待:
  • 為了避免優先權翻轉的情形發生,如需等待的 thread 的優先級大於持有 mutex 的優先級,提升持有者的
/* 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) { RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_take: start the timer of thread:%s\n", thread->name)); /* 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)); }
  • 插入 suspend list,並啟動一個 timeout 為 time 的 timer
/* enable interrupt */ rt_hw_interrupt_enable(temp); /* do schedule */ rt_schedule();
  • 開啟中斷,並做一次調度
if (thread->error != RT_EOK) { /* interrupt by signal, try it again */ if (thread->error == -RT_EINTR) goto __again; /* return error */ return thread->error; }
  • 如果因為中斷再次回到此 thread,重新要一次 mutex
else { /* the mutex is taken successfully. */ /* disable interrupt */ temp = rt_hw_interrupt_disable(); } } } } /* enable interrupt */ rt_hw_interrupt_enable(temp); RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mutex->parent.parent))); return RT_EOK; } RTM_EXPORT(rt_mutex_take);

  • 還鎖則使用 rt_mutex_release

rt_mutex_release

功能 回傳值
釋放 mutex RT_EOK
mutex
欲釋放的 mutex
/** * 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; /* parameter check */ RT_ASSERT(mutex != RT_NULL); RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); 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 的一些資料,這裡先將中斷關閉
RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_release:current thread %s, mutex value: %d, hold: %d\n", thread->name, mutex->value, mutex->hold)); RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mutex->parent.parent))); /* 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 --;
  • 持有數減 1
/* if no hold */ if (mutex->hold == 才會 { /* 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)); }
  • 若已不再擁有此 mutex,且優先權有被更改過,調整回來
/* 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); RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_release: resume thread: %s\n", thread->name)); /* 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; }
  • 若有人在等待此 mutex,將 mutex 傳遞給第一個正在等待的 thread
else { /* increase value */ mutex->value ++; /* clear owner */ mutex->owner = RT_NULL; mutex->original_priority = 0xff; } }
  • 如果沒有人在等,value 加 1,將資料初始化
/* enable interrupt */ rt_hw_interrupt_enable(temp); /* perform a schedule */ if (need_schedule == RT_TRUE) rt_schedule(); return RT_EOK; } RTM_EXPORT(rt_mutex_release);
  • 開啟中斷,並視情況做一次調度
tags: RT-Thread kernel ipc mutex