rt-thread操作系統的IPC(Inter-Process Communication,進程間通信)包含有信號量,互斥鎖,事件,郵箱,消息隊列. 本文主要針對信號量. 1 信號量控制塊 [cpp] /** * Semaphore structure */ struct rt_semaphore { struct rt_ipc_object parent; /**< inherit from ipc_object *///派生自IPC對象 rt_uint16_t value; /**< value of semaphore. *///信號量計數器 }; typedef struct rt_semaphore *rt_sem_t; value為信號計數器,parent為一rt_ipc_object即IPC對象,其定義如下: [cpp] /** * Base structure of IPC object */ struct rt_ipc_object { struct rt_object parent; /**< inherit from rt_object *///可知其派生自內核對象 rt_list_t suspend_thread; /**< threads pended on this resource *///線程掛起鏈表 }; 從rt_ipc_object的定義結構可知其派生自rt_object結構,即內核對象的定義(參考http://blog.csdn.net/flydream0/article/details/8568463),另外,它不包含一鏈表,用來保存因此信號量而掛起的線程. 2 信號量的創建與初始化 2.1 初始化 [cpp] /** * 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));//初始化信號量的IPC對象 /* set init value */ sem->value = value;//設置信號量計數器的值 /* set parent */ sem->parent.parent.flag = flag;//設置信號量的內核對象的標志 return RT_EOK; } 其中rt_object_init已在之前介紹rt-thread的內核對象中相關文章中已有介紹(參見:http://blog.csdn.net/flydream0/article/details/8568463),rt_ipc_object_init函數見如下: [cpp] /** * @addtogroup IPC */ /*@{*/ /** * This function will initialize an IPC object * * @param ipc the IPC object * * @return the operation status, RT_EOK on successful */ rt_inline rt_err_t rt_ipc_object_init(struct rt_ipc_object *ipc) { /* init ipc object */ rt_list_init(&(ipc->suspend_thread));//初始化線程掛起鏈表 return RT_EOK; } 2.2 創建信號量 [cpp] /** * 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));//初始化信號量的IPC對象 /* set init value */ sem->value = value;//初始化信號量的計數器值 /* set parent */ sem->parent.parent.flag = flag;//設置信號量的內核對象的標志 return sem; } 3 脫離及刪除信號量 3.1 脫離信號量 [cpp] /** * 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) { RT_ASSERT(sem != RT_NULL); /* 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; } 其中rt_ipc_list_resume_all函數如下: [cpp] /** * This function will resume all suspended threads in a list, including * suspend list of IPC object and private list of mailbox etc. * * @param list of the threads to resume * * @return the operation status, RT_EOK on successful */ rt_inline rt_err_t rt_ipc_list_resume_all(rt_list_t *list) { struct rt_thread *thread; register rt_ubase_t temp; /* wakeup all suspend threads */ while (!rt_list_isempty(list))//遍歷線程掛起鏈表 { /* disable interrupt */ temp = rt_hw_interrupt_disable();//關中斷 /* get next suspend thread */ thread = rt_list_entry(list->next, struct rt_thread, tlist);//獲得線程 /* set error code to RT_ERROR */ thread->error = -RT_ERROR;//設置線程的錯誤碼為-RT_ERROR /* * resume thread * In rt_thread_resume function, it will remove current thread from * suspend list */ rt_thread_resume(thread);//喚醒此線程 /* enable interrupt */ rt_hw_interrupt_enable(temp);//開中斷 } return RT_EOK; } 3.2 刪除線程 [cpp] /** * 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;//確保此函數不是在中斷中使用 RT_ASSERT(sem != RT_NULL); /* 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; } 4 獲取信號量 4.1 等待信號量 [cpp] /** * 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; RT_ASSERT(sem != RT_NULL); RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(sem->parent.parent))); /* disable interrupt */ temp = rt_hw_interrupt_disable();//關中斷 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)//如果此信號量的計數器的值大於0,說明有信號,則應該立即返回 { /* semaphore is available */ sem->value --;//則將信號量的計數器的值減1 /* enable interrupt */ rt_hw_interrupt_enable(temp);//開中斷 } else//如果此信號量的計數器的值小於或等於0,說明此時還未有信號 { /* no waiting, return with timeout */ if (time == 0)//如果等待時間參數為0,則立即返回超時錯誤 { rt_hw_interrupt_enable(temp);//開中斷 return -RT_ETIMEOUT;//返回超時錯誤 } else//等待信號 { /* current context checking */ RT_DEBUG_NOT_IN_INTERRUPT;//確保此時不在中斷中使用 /* semaphore is unavailable, push to suspend list */ /* get current thread */ thread = rt_thread_self();//獲取當前正在運行的線程 /* reset thread error number */ thread->error = RT_EOK;//設置當前線程的錯誤代碼為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); /* has waiting time, start thread timer */ if (time > 0)//如果時間參數大於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));//啟動定時器開始計時 } /* enable interrupt */ rt_hw_interrupt_enable(temp);//開中斷 /* do schedule */ rt_schedule();//當前線程已掛起,需要重新調試線程 //rt_schedule之後再執行到這裡,只有兩種可能,一是當前線程被掛起後時間已到達,此時,定時器的超時回調處理函數會將此線程的err值設為-RT_ETIMEOU,見thread.c源文件中的rt_thread_timeout函數;另一種情況是,有信號量到來,當前線程被rt_sem_release函數喚醒,此時,此線程的err值將一直保持原樣不變,因此可以下面可能通過判斷線程的err值來判斷當前線程是否已被接收到信號量 if (thread->error != RT_EOK)//如果當前線程的錯誤代碼不為RT_EOK,則返回,否則一直阻塞到等待到有信號到達或超時 { return thread->error; } } } RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(sem->parent.parent))); return RT_EOK; } 4.2 獲取無等待信號量 [cpp] /** * 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); } 由此可見,rt_sem_trytake只是rt_sem_take函數的一種特例,時間參數為0而已. 5 釋放信號量 [cpp] /** * 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; RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(sem->parent.parent))); need_schedule = RT_FALSE;//默認情況下設置不需要重新調度標記 /* disable interrupt */ temp = rt_hw_interrupt_disable();//關中斷 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;//需要重新調度 } else sem->value ++; /* increase value *///信號量計數器加1 /* enable interrupt */ rt_hw_interrupt_enable(temp);//開中斷 /* resume a thread, re-schedule */ if (need_schedule == RT_TRUE)//如果需要重新調度線程,則重新調度 rt_schedule(); return RT_EOK; } 其中函數rt_ipc_list_resume只會喚醒信號量中第一個掛起的線程,其源碼如下: [cpp] /** * This function will resume the first thread in the list of a IPC object: * - remove the thread from suspend queue of IPC object * - put the thread into system ready queue * * @param list the thread list * * @return the operation status, RT_EOK on successful */ rt_inline rt_err_t rt_ipc_list_resume(rt_list_t *list) { struct rt_thread *thread; /* get thread entry */ thread = rt_list_entry(list->next, struct rt_thread, tlist);//獲取線程 RT_DEBUG_LOG(RT_DEBUG_IPC, ("resume thread:%s\n", thread->name)); /* resume it */ rt_thread_resume(thread);//喚醒此線程 return RT_EOK; } 6 信號量控制 [cpp] /** * This function can get or set some extra attributions of a semaphore object. * * @param sem the semaphore object * @param cmd the execution command * @param arg the execution argument * * @return the error code */ rt_err_t rt_sem_control(rt_sem_t sem, rt_uint8_t cmd, void *arg) { rt_ubase_t level; RT_ASSERT(sem != RT_NULL); if (cmd == RT_IPC_CMD_RESET)//重置信號量的計數器值 { rt_uint32_t value; /* get value */ value = (rt_uint32_t)arg; /* disable interrupt */ level = rt_hw_interrupt_disable();//關中斷 /* resume all waiting thread */ www.2cto.com rt_ipc_list_resume_all(&sem->parent.suspend_thread);//喚醒信號量上所有掛起的線程 /* set new value */ sem->value = (rt_uint16_t)value;//設置信號時的計數器值 /* enable interrupt */ rt_hw_interrupt_enable(level);//開中斷 rt_schedule();//立即重新調試 return RT_EOK; } return -RT_ERROR; }