Try  HackMD Logo HackMD

RT-Thread IPC Semaphore

  • 為一個值,代表同時可用的個數
  • 不等於 0 時可用,取用時將值減 1
  • 當不可用時,將 thread 掛在等待的鏈上

結構

File: rtdef.h

#ifdef RT_USING_SEMAPHORE /** * Semaphore structure */ struct rt_semaphore { struct rt_ipc_object parent; /**< inherit from ipc_object */ rt_uint16_t value; /**< value of semaphore. */ }; typedef struct rt_semaphore *rt_sem_t; #endif

flags

/** * IPC flags and control command definitions */ #define RT_IPC_FLAG_FIFO 0x00 /**< FIFOed IPC. @ref IPC. */ #define RT_IPC_FLAG_PRIO 0x01 /**< PRIOed IPC. @ref IPC. */ #define RT_IPC_CMD_UNKNOWN 0x00 /**< unknown IPC command */ #define RT_IPC_CMD_RESET 0x01 /**< reset IPC object */ #define RT_WAITING_FOREVER -1 /**< Block forever until get resource. */ #define RT_WAITING_NO 0 /**< Non-block. */

File: ipc.c

建立 semaphore

動態記憶體管理

rt_sem_create

功能 回傳值
建立 semaphore semaphore
*name value flag
名字 semaphore 值,即最大可同時使用人數 FIFO / PRIO
/** * This function will create a semaphore from system resource * * @param name the name of semaphore * @param value the init value of semaphore * @param flag the flag of semaphore * * @return the created semaphore, RT_NULL on error happen * * @see rt_sem_init */ rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag) { rt_sem_t sem; RT_DEBUG_NOT_IN_INTERRUPT; /* allocate object */ sem = (rt_sem_t)rt_object_allocate(RT_Object_Class_Semaphore, name); if (sem == RT_NULL) return sem; /* init ipc object */ rt_ipc_object_init(&(sem->parent)); /* set init value */ sem->value = value; /* set parent */ sem->parent.parent.flag = flag; return sem; } RTM_EXPORT(rt_sem_create);
  • 首先需要一塊 semaphore 的大小,初始化 ipc 物件,再依序寫入初始值及 flag

靜態記憶體管理

rt_sem_init

功能 回傳值
初始化 semaphore RT_EOK
sem *name value flag
semaphore 本體 名字 semaphore 值,即最大可同時使用人數 FIFO / PRIO
/** * This function will initialize a semaphore and put it under control of * resource management. * * @param sem the semaphore object * @param name the name of semaphore * @param value the init value of semaphore * @param flag the flag of semaphore * * @return the operation status, RT_EOK on successful */ rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag) { RT_ASSERT(sem != RT_NULL); /* init object */ rt_object_init(&(sem->parent.parent), RT_Object_Class_Semaphore, name); /* init ipc object */ rt_ipc_object_init(&(sem->parent)); /* set init value */ sem->value = value; /* set parent */ sem->parent.parent.flag = flag; return RT_EOK; } RTM_EXPORT(rt_sem_init);
  • 由於使用靜態記憶體,這裡就不需要再 allocate。

刪除 semaphore

動態記憶體管理

rt_sem_delete

功能 回傳值
刪除 semaphore RT_EOK
sem
欲刪除的 semaphore
/** * This function will delete a semaphore object and release the memory * * @param sem the semaphore object * * @return the error code * * @see rt_sem_detach */ rt_err_t rt_sem_delete(rt_sem_t sem) { RT_DEBUG_NOT_IN_INTERRUPT; /* parameter check */ RT_ASSERT(sem != RT_NULL); RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore); RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent) == RT_FALSE); /* wakeup all suspend threads */ rt_ipc_list_resume_all(&(sem->parent.suspend_thread)); /* delete semaphore object */ rt_object_delete(&(sem->parent.parent)); return RT_EOK; } RTM_EXPORT(rt_sem_delete);
  • 首先需把所有正在等待此 semaphore 的 thread 叫醒
  • 接著呼叫 rt_object_delete 清除此物件(semaphore)

靜態記憶體管理

rt_sem_detach

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

使用 semaphore

  • 呼叫 rt_sem_take 來取得 semaphore,傳入的 time 是等待時間

rt_sem_take

功能 回傳值
要求 semaphore RT_EOK
sem time
欲要求的 semaphore 等待時間(如果需要)
/** * This function will take a semaphore, if the semaphore is unavailable, the * thread shall wait for a specified time. * * @param sem the semaphore object * @param time the waiting time * * @return the error code */ rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time) { register rt_base_t temp; struct rt_thread *thread; /* parameter check */ RT_ASSERT(sem != RT_NULL); RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore); RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(sem->parent.parent))); /* disable interrupt */ temp = rt_hw_interrupt_disable();
  • 由於待會會修改 semaphore 的值,這裡先將中斷關閉
RT_DEBUG_LOG(RT_DEBUG_IPC, ("thread %s take sem:%s, which value is: %d\n", rt_thread_self()->name, ((struct rt_object *)sem)->name, sem->value)); if (sem->value > 0) { /* semaphore is available */ sem->value --; /* enable interrupt */ rt_hw_interrupt_enable(temp); }
  • 如過 sem->value 值大於 0 代表可用,接著減一,並開啟中斷
else { /* no waiting, return with timeout */ if (time == 0) { rt_hw_interrupt_enable(temp); return -RT_ETIMEOUT; }
  • 如果 semaphore 不可用時:
  • 且 time 為 0,表示不等待,直接開啟中斷並 return
else { /* current context checking */ RT_DEBUG_IN_THREAD_CONTEXT; /* semaphore is unavailable, push to suspend list */ /* get current thread */ thread = rt_thread_self(); /* reset thread error number */ thread->error = RT_EOK; RT_DEBUG_LOG(RT_DEBUG_IPC, ("sem take: suspend thread - %s\n", thread->name)); /* suspend thread */ rt_ipc_list_suspend(&(sem->parent.suspend_thread), thread, sem->parent.parent.flag);
  • 如果要等待,則將 thread 插入 suspend list
/* has waiting time, start thread timer */ if (time > 0) { RT_DEBUG_LOG(RT_DEBUG_IPC, ("set thread:%s to timer list\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)); }
  • 且如果等待時間大於 0,則啟動一個 timeout 為 time 的 timer
/* enable interrupt */ rt_hw_interrupt_enable(temp); /* do schedule */ rt_schedule(); if (thread->error != RT_EOK) { return thread->error; } } } RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(sem->parent.parent))); return RT_EOK; } RTM_EXPORT(rt_sem_take);
  • 最後開啟中斷,做一次調度

  • 若是不想等待,可以呼叫 rt_sem_trytake
  • 即呼叫 rt_sem_take 及傳入 time 為 0

rt_sem_trytake

功能 回傳值
要求 semaphore(不等待) RT_EOK
sem
欲要求的 semaphore
/** * This function will try to take a semaphore and immediately return * * @param sem the semaphore object * * @return the error code */ rt_err_t rt_sem_trytake(rt_sem_t sem) { return rt_sem_take(sem, 0); } RTM_EXPORT(rt_sem_trytake);

  • 還 semaphore 則使用 rt_sem_release

rt_sem_release

功能 回傳值
釋放 semaphore RT_EOK
sem
欲要求的 semaphore
/** * This function will release a semaphore, if there are threads suspended on * semaphore, it will be waked up. * * @param sem the semaphore object * * @return the error code */ rt_err_t rt_sem_release(rt_sem_t sem) { register rt_base_t temp; register rt_bool_t need_schedule; /* parameter check */ RT_ASSERT(sem != RT_NULL); RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore); RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(sem->parent.parent))); need_schedule = RT_FALSE; /* disable interrupt */ temp = rt_hw_interrupt_disable();
  • 首先將待會會遇到的 flag(need_schedule)設為 false
  • 因為待會也會修改 semaphore 的值,這裡需要關閉中斷
RT_DEBUG_LOG(RT_DEBUG_IPC, ("thread %s releases sem:%s, which value is: %d\n", rt_thread_self()->name, ((struct rt_object *)sem)->name, sem->value)); if (!rt_list_isempty(&sem->parent.suspend_thread)) { /* resume the suspended thread */ rt_ipc_list_resume(&(sem->parent.suspend_thread)); need_schedule = RT_TRUE; }
  • 如果有人在等此 semaphore,先恢復他,並修改 need_schedule 為 true
else sem->value ++; /* increase value */
  • 如果沒有人在等待,則加一
/* enable interrupt */ rt_hw_interrupt_enable(temp); /* resume a thread, re-schedule */ if (need_schedule == RT_TRUE) rt_schedule(); return RT_EOK; } RTM_EXPORT(rt_sem_release);
  • 最後開啟中斷,並根據 need_schedule 來決定需不需要執行一次調度
tags: RT-Thread kernel ipc semaphore