Rtthread线程源码分析
Rtthread線程源碼分析
/*** This function will create a thread object and allocate thread object memory* and stack.** @param name the name of thread, which shall be unique* @param entry the entry function of thread* @param parameter the parameter of thread enter function* @param stack_size the size of thread stack* @param priority the priority of thread* @param tick the time slice if there are same priority thread** @return the created thread object*/ rt_thread_t rt_thread_create(const char *name,void (*entry)(void *parameter),void *parameter,rt_uint32_t stack_size,rt_uint8_t priority,rt_uint32_t tick) {struct rt_thread *thread;void *stack_start;//1. 創建一個RT_Object_Class_Thread類型的objectd對象thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread,name);if (thread == RT_NULL)return RT_NULL;//2. 申請分配線程棧空間stack_start = (void *)RT_KERNEL_MALLOC(stack_size);if (stack_start == RT_NULL){/* allocate stack failure *///失敗需要先刪除該object并釋放內存rt_object_delete((rt_object_t)thread);return RT_NULL;}//3. 調用_rt_thread_init_rt_thread_init(thread,name,entry,parameter,stack_start,stack_size,priority,tick);return thread; }static rt_err_t _rt_thread_init(struct rt_thread *thread,const char *name,void (*entry)(void *parameter),void *parameter,void *stack_start,rt_uint32_t stack_size,rt_uint8_t priority,rt_uint32_t tick) {/* init thread list */rt_list_init(&(thread->tlist));// 1. 指定線程入口函數和參數thread->entry = (void *)entry;thread->parameter = parameter;/* stack init *///2. 線程結構體初始化thread->stack_addr = stack_start;thread->stack_size = stack_size;/* init thread stack */rt_memset(thread->stack_addr, '#', thread->stack_size); #ifdef ARCH_CPU_STACK_GROWS_UPWARD //我們一般使用滿減棧,所以該宏不定義thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,(void *)((char *)thread->stack_addr),(void *)rt_thread_exit); #else //走else// 3. 調用rt_hw_stack_init 初始化棧,棧頂的值為 【棧首地址 + 棧大小 - 4】thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,(rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)),(void *)rt_thread_exit); #endif/* priority init */RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);//初始化thread優先級成員thread->init_priority = priority;thread->current_priority = priority;thread->number_mask = 0; #if RT_THREAD_PRIORITY_MAX > 32thread->number = 0;thread->high_mask = 0; #endif/* tick init */thread->init_tick = tick;thread->remaining_tick = tick;/* error and flags */thread->error = RT_EOK;thread->stat = RT_THREAD_INIT;/* initialize cleanup function and user data */thread->cleanup = 0;thread->user_data = 0;/* initialize thread timer *///初始化線程timerrt_timer_init(&(thread->thread_timer),thread->name,rt_thread_timeout,thread,0,RT_TIMER_FLAG_ONE_SHOT);RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));return RT_EOK; } rt_uint8_t *rt_hw_stack_init(void *tentry,void *parameter,rt_uint8_t *stack_addr,void *texit) {struct stack_frame *stack_frame;rt_uint8_t *stk;unsigned long i;stk = stack_addr + sizeof(rt_uint32_t);//傳進來的stack_addr 減去了4,所以這里加上4stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);//向下8對齊,考慮到64bit平臺stk -= sizeof(struct stack_frame);//偏移到stack_frame的首地址stack_frame = (struct stack_frame *)stk;/* init all register */for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++){((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;}//這些寄存器是線程調度時自動加載到寄存器中去的,剩下的r4-r11需要手動加載stack_frame->exception_stack_frame.r0 = (unsigned long)parameter; /* r0 : argument */stack_frame->exception_stack_frame.r1 = 0; /* r1 */stack_frame->exception_stack_frame.r2 = 0; /* r2 */stack_frame->exception_stack_frame.r3 = 0; /* r3 */stack_frame->exception_stack_frame.r12 = 0; /* r12 *//*線程函數退出時會執行,退出時會將該函數當作返回的地址*/stack_frame->exception_stack_frame.lr = (unsigned long)texit; /* lr */stack_frame->exception_stack_frame.pc = (unsigned long)tentry; /* entry point, pc */stack_frame->exception_stack_frame.psr = 0x01000000L; /* PSR XPSR bit24 為1,使用thumb指令集*/#if USE_FPUstack_frame->flag = 0; #endif /* USE_FPU *//* return task's current stack address */return stk; }棧初始化后的布局
rt_thread_startup
rt_thread_startup函數主要作用是將當前init完成的線程從SUSPEND狀態切換到READY狀態,
將該thread從tlist鏈表中移除,插入到就緒鏈表。
懸起一個pendsv異常后,在沒有其他中斷或者異常發生的時候,就會進入pendsv中切換上下文:
/* r0 --> switch from thread stack* r1 --> switch to thread stack* psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack*/ .global PendSV_Handler .type PendSV_Handler, %function PendSV_Handler:/* disable interrupt to protect context switch *///上下文切換之前先要禁用中斷,保證切換不被打斷MRS r2, PRIMASKCPSID I/* get rt_thread_switch_interrupt_flag *///判斷rt_thread_switch_interrupt_flag,為0則退出pendsv handlerLDR r0, =rt_thread_switch_interrupt_flagLDR r1, [r0]CBZ r1, pendsv_exit /* pendsv already handled *//* clear rt_thread_switch_interrupt_flag to 0 *///清理rt_thread_switch_interrupt_flag MOV r1, #0x00STR r1, [r0]//判斷是否需要保存上文,啟動第一個線程的時候不需要保存上文。LDR r0, =rt_interrupt_from_threadLDR r1, [r0]CBZ r1, switch_to_thread /* skip register save at the first time *///保存上文:獲取上文psp的值MRS r1, psp /* get from thread stack pointer */#if defined (__VFP_FP__) && !defined(__SOFTFP__)TST lr, #0x10 /* if(!EXC_RETURN[4]) */VSTMDBEQ r1!, {d8 - d15} /* push FPU register s16~s31 */ #endif//將R4-R11手動保存到棧中,其他寄存器都已經由硬件自動入棧保存了。//相當于: r1 -= 4*(11 - 4) for(i = 0; i < 11 - 4; i++) r4 -> r11 入棧STMFD r1!, {r4 - r11} /* push r4 - r11 register */#if defined (__VFP_FP__) && !defined(__SOFTFP__)MOV r4, #0x00 /* flag = 0 */TST lr, #0x10 /* if(!EXC_RETURN[4]) */MOVEQ r4, #0x01 /* flag = 1 */STMFD r1!, {r4} /* push flag */ #endif//更新棧變量的值為 當前棧指針指向的棧地址LDR r0, [r0]STR r1, [r0] /* update from thread stack pointer *///下文切換: switch_to_thread://獲取棧指針的值LDR r1, =rt_interrupt_to_threadLDR r1, [r1]LDR r1, [r1] /* load thread stack pointer */#if defined (__VFP_FP__) && !defined(__SOFTFP__)LDMFD r1!, {r3} /* pop flag */ #endif//手動將r4-r11出棧,寫入對應的寄存器中。LDMFD r1!, {r4 - r11} /* pop r4 - r11 register */#if defined (__VFP_FP__) && !defined(__SOFTFP__)CMP r3, #0 /* if(flag_r3 != 0) */VLDMIANE r1!, {d8 - d15} /* pop FPU register s16~s31 */ #endif//將r1(當前棧地址) 寫入psp, 在異常返回時硬件會主動Load r0 r1...這些寄存器MSR psp, r1 /* update stack pointer */#if defined (__VFP_FP__) && !defined(__SOFTFP__)ORR lr, lr, #0x10 /* lr |= (1 << 4), clean FPCA. */CMP r3, #0 /* if(flag_r3 != 0) */BICNE lr, lr, #0x10 /* lr &= ~(1 << 4), set FPCA. */ #endifpendsv_exit:/* restore interrupt */MSR PRIMASK, r2//在進入異常服務程序后,LR的值被自動更新為特殊的EXC_RETURN,這//是一個高28位全為1的值,只有[3:0]的值有特殊含義//bit2 == 1 從進程堆棧中做出棧操作,返回后使用PSPORR lr, lr, #0x04BX lr到這里線程main線程就會開始啟動了,啟動之后,systick定時器會在各個線程時間片耗盡之后觸發pendsv切換
線程。
線程退出
當線程函數退出時會將lr的值load到pc中,因為在初始化線程棧時,LR的值我們設置的是rt_thread_exit,所以
線程函數退出時會call這個函數。
線程分離/刪除
線程分離和線程刪除函數差別如下:
線程分離函數可以將靜態線程從object中移除,線程刪除函數則只能將動態分配的線程移除到僵尸線程表中
線程定時
/*** This function will let current thread sleep for some ticks.** @param tick the sleep ticks** @return RT_EOK*/ rt_err_t rt_thread_sleep(rt_tick_t tick) {register rt_base_t temp;struct rt_thread *thread;/* disable interrupt */temp = rt_hw_interrupt_disable();/* set to current thread */thread = rt_current_thread;RT_ASSERT(thread != RT_NULL);RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);/* suspend thread */rt_thread_suspend(thread);//掛起該線程/* reset the timeout of thread timer and start it */rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick);//設置掛起時間rt_timer_start(&(thread->thread_timer));//開始計時/* enable interrupt */rt_hw_interrupt_enable(temp);rt_schedule();//線程調度/* clear error number of this thread to RT_EOK */if (thread->error == -RT_ETIMEOUT)thread->error = RT_EOK;return RT_EOK; } /*** This function will suspend the specified thread.** @param thread the thread to be suspended** @return the operation status, RT_EOK on OK, -RT_ERROR on error** @note if suspend self thread, after this function call, the* rt_schedule() must be invoked.*/ rt_err_t rt_thread_suspend(rt_thread_t thread) {register rt_base_t temp;/* thread check */RT_ASSERT(thread != RT_NULL);RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: %s\n", thread->name));if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_READY){RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, 0x%2x\n",thread->stat));return -RT_ERROR;}/* disable interrupt */temp = rt_hw_interrupt_disable();/* change thread stat */rt_schedule_remove_thread(thread);//將該線程從就緒表中移除thread->stat = RT_THREAD_SUSPEND | (thread->stat & ~RT_THREAD_STAT_MASK);//將狀態設置為RT_THREAD_SUSPEND /* stop thread timer anyway */rt_timer_stop(&(thread->thread_timer));//停止計時/* enable interrupt */rt_hw_interrupt_enable(temp);RT_OBJECT_HOOK_CALL(rt_thread_suspend_hook, (thread));return RT_EOK; } /*** This function will control thread behaviors according to control command.** @param thread the specified thread to be controlled* @param cmd the control command, which includes* RT_THREAD_CTRL_CHANGE_PRIORITY for changing priority level of thread;* RT_THREAD_CTRL_STARTUP for starting a thread;* RT_THREAD_CTRL_CLOSE for delete a thread;* RT_THREAD_CTRL_BIND_CPU for bind the thread to a CPU.* @param arg the argument of control command** @return RT_EOK*/ rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg) {register rt_base_t temp;/* thread check */RT_ASSERT(thread != RT_NULL);RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);switch (cmd){case RT_THREAD_CTRL_CHANGE_PRIORITY: //改變線程優先級/* disable interrupt */temp = rt_hw_interrupt_disable();/* for ready thread, change queue */if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY) //線程在就緒表中{/* remove thread from schedule queue first */rt_schedule_remove_thread(thread); //先將該線程移除/* change thread priority */thread->current_priority = *(rt_uint8_t *)arg; //修改線程優先級/* recalculate priority attribute */ #if RT_THREAD_PRIORITY_MAX > 32thread->number = thread->current_priority >> 3; /* 5bit */thread->number_mask = 1 << thread->number;thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ #elsethread->number_mask = 1 << thread->current_priority;//設置優先級mask #endif/* insert thread to schedule queue again */rt_schedule_insert_thread(thread);//修改好優先級后重新插入線程表}else //線程未在就緒表中{thread->current_priority = *(rt_uint8_t *)arg;/* recalculate priority attribute */ #if RT_THREAD_PRIORITY_MAX > 32thread->number = thread->current_priority >> 3; /* 5bit */thread->number_mask = 1 << thread->number;thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ #elsethread->number_mask = 1 << thread->current_priority; #endif}/* enable interrupt */rt_hw_interrupt_enable(temp);break;case RT_THREAD_CTRL_STARTUP://啟動線程return rt_thread_startup(thread);case RT_THREAD_CTRL_CLOSE://關閉線程/分離線程if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE)//靜態線程使用detech{return rt_thread_detach(thread);} #ifdef RT_USING_HEAPelse //動態線程使用delete{return rt_thread_delete(thread);} #endifdefault:break;}return RT_EOK; } /*** This function will lock the thread scheduler.*/ //該函數會使rt_scheduler_lock_nest ++,從而鎖住scheduler,確保在解鎖之前當前代碼段不會被scheduler中斷執行 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); }/*** This function will unlock the thread scheduler.*/ 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 *///這里因為如果線程systic timer超時了,本來需要做rt_schedule,但是因為rt_schedule被鎖住,而沒有做//所以會在解鎖后做一次,如果該線程確實超時,那么就會執行shedule,線程時間片還沒耗盡的話,因為它的優先級是排在//最前的,所以rt_schedule中會有判斷當前from和to的線程是一致的,就不會切換。rt_schedule();}}else{/* enable interrupt */rt_hw_interrupt_enable(level);} } /*** This function is the timeout function for thread, normally which is invoked* when thread is timeout to wait some resource.** @param parameter the parameter of thread timeout function*/ void rt_thread_timeout(void *parameter) {struct rt_thread *thread;thread = (struct rt_thread *)parameter;/* thread check */RT_ASSERT(thread != RT_NULL);RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND);RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);/* set error number */thread->error = -RT_ETIMEOUT;/* remove from suspend list */rt_list_remove(&(thread->tlist));//do nothing/* insert to schedule ready list */rt_schedule_insert_thread(thread);//將線程插入到就緒表/* do schedule */rt_schedule(); }/*** This function will find the specified thread.** @param name the name of thread finding** @return the found thread** @note please don't invoke this function in interrupt status.*/ rt_thread_t rt_thread_find(char *name) {return (rt_thread_t)rt_object_find(name, RT_Object_Class_Thread); }Rtthread thread 內部timer作用:
thread結構體中包含一個timer成員:
struct rt_timer thread_timer; /**< built-in thread timer */在init_thread函數里面會對其進行初始化:
/* initialize thread timer */rt_timer_init(&(thread->thread_timer),thread->name,rt_thread_timeout,thread,0,RT_TIMER_FLAG_ONE_SHOT);這里設置的超時回調函數為rt_thread_timeout,初始超時時間為0,RT_TIMER_FLAG_ONE_SHOT
為單次定時事件。
再看看rt_thread_timeout實現:
定時時間超時的時候會調用該函數,函數參數為該線程的信息。
主要做了如下操作:
1.設置thread->error 為 -RT_ETIMEOUT
2.移除當前線程節點
3.將該線程再次插入到就緒列表
4.執行線程調度
那么什么時候會執行上述超時函數呢?
答案是在調用rt_thread_delay(rt_tick_t tick)的時候,rt_thread_delay會調用rt_thread_sleep
調用rt_thread_sleep的時候,函數執行下面的動作:
1.先調用rt_thread_suspend(thread)將當前線程休眠,移出就緒表。
2.調用rt_timer_control設置定時時間(休眠時間)。
3.調用rt_timer_start開啟定時。
4.因為當前線程已經休眠,所以需要調用rt_schedule()調度到新的線程執行。
5.將thread->error = RT_EOK
這樣當線程delay或者休眠時就會讓出cpu,并在休眠時間到達目標值時通過rt_thread_timeout執行新的
調度。
rt_tick_increase函數會在systick中斷中被調用,每次調用都會調用timer模塊的rt_timer_check函數檢查
是否有新的超時事件.
當線程被調用detech或者delete的時候,線程的detech/delete函數也會分別調用線程timer的detech/delete,
將該timer從object中移除。
總結
以上是生活随笔為你收集整理的Rtthread线程源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ei capitan mysql_OSX
- 下一篇: 如何用多因子模型预测资险