本文主要是介绍RT-Thread内核源码分析-线程栈结构分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
RT-Thread提供了一套满足POSIX标准的接口,因此基于RT-Thread编写的应用程序,可以相对轻松的移植到linux平台。 pthread_create接口用来创建一个线程, 接下来我们基于pthread_create来分析动态分配的数组时如何作为线程栈来使用的。
int pthread_create(pthread_t *tid,const pthread_attr_t *attr, void *(*start) (void *), void *parameter)
{int result;void *stack;char name[RT_NAME_MAX];static rt_uint16_t pthread_number = 0;_pthread_data_t *ptd;/* tid shall be provided */RT_ASSERT(tid != RT_NULL);/* allocate posix thread data */ptd = (_pthread_data_t*)rt_malloc(sizeof(_pthread_data_t));if (ptd == RT_NULL)return ENOMEM;/* clean posix thread data memory */rt_memset(ptd, 0, sizeof(_pthread_data_t));ptd->canceled = 0;ptd->cancelstate = PTHREAD_CANCEL_DISABLE;ptd->canceltype = PTHREAD_CANCEL_DEFERRED;ptd->magic = PTHREAD_MAGIC;if (attr != RT_NULL)ptd->attr = *attr;else {/* use default attribute */pthread_attr_init(&ptd->attr);}rt_snprintf(name, sizeof(name), "pth%02d", pthread_number ++);if (ptd->attr.stack_base == 0){stack = (void*)rt_malloc(ptd->attr.stack_size);//根据用户指定的大小动态分配栈空间}elsestack = (void*)(ptd->attr.stack_base);if (stack == RT_NULL) {rt_free(ptd);return ENOMEM;}/* pthread is a static thread object */ptd->tid = (rt_thread_t) rt_malloc(sizeof(struct rt_thread));//分配线程控制块if (ptd->tid == RT_NULL){if (ptd->attr.stack_base == 0)rt_free(stack);rt_free(ptd);return ENOMEM;}if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE){ptd->joinable_sem = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO);if (ptd->joinable_sem == RT_NULL){if (ptd->attr.stack_base != 0)rt_free(stack);rt_free(ptd);return ENOMEM;}}elseptd->joinable_sem = RT_NULL;/* set parameter */ptd->thread_entry = start;ptd->thread_parameter = parameter;/* initial this pthread to system *///调用rtthread提供的线程初始化接口if (rt_thread_init(ptd->tid, name, pthread_entry_stub, ptd, stack, ptd->attr.stack_size, 99 - ptd->attr.priority, 5) != RT_EOK){if (ptd->attr.stack_base == 0)rt_free(stack);if (ptd->joinable_sem != RT_NULL)rt_sem_delete(ptd->joinable_sem);rt_free(ptd);return EINVAL;}/* set pthread id */*tid = ptd->tid;/* set pthread cleanup function and ptd data */(*tid)->cleanup = _pthread_cleanup;(*tid)->user_data = (rt_uint32_t)ptd;/* start thread */result = rt_thread_startup(*tid);//启动该线程if (result == RT_EOK)return 0;/* start thread failed */rt_thread_detach(ptd->tid);if (ptd->attr.stack_base == 0)rt_free(stack);if (ptd->joinable_sem != RT_NULL)rt_sem_delete(ptd->joinable_sem);rt_free(ptd);return EINVAL;
}
pthread_create内部是调用rt_thread_init和rt_thread_startup来完成线程的初始化和启动工作,我们先看初始化函数:
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)
{/* thread check */RT_ASSERT(thread != RT_NULL);RT_ASSERT(stack_start != RT_NULL);/* init thread object */rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name);return _rt_thread_init(thread,name,entry,parameter,stack_start,stack_size,priority,tick);
}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));thread->entry = (void *)entry;thread->parameter = parameter;/* stack init */thread->stack_addr = stack_start;thread->stack_size = (rt_uint16_t)stack_size;/* init thread stack */rt_memset(thread->stack_addr, '#', thread->stack_size);// 传入的栈地址是: stack_addr + stack_size - 4; 之所以这么做是确保栈空间是向下增长的, 注意:这里有一个减4的操作 thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,(void *)((char *)thread->stack_addr + thread->stack_size - 4),(void *)rt_thread_exit);/* priority init */RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);thread->init_priority = priority;thread->current_priority = priority;/* 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;/* init thread timer */rt_timer_init(&(thread->thread_timer),thread->name,rt_thread_timeout,thread,0,RT_TIMER_FLAG_ONE_SHOT);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);//这里又多了一个加4的操作, 和上文中的减4的操作配合,这个是为什么呢?stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);//8字节对齐, 不用担心stk会越界;stk -= sizeof(struct stack_frame);//stack_frame是ARM寄存器存储列表,也就是我们栈中寄存器的存储顺序, 这个操作将寄存器入栈, stk即为栈顶指针,接下来是对线程栈内容的初始化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;}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 *//* return task's current stack address */return stk;//返回栈顶指针
}struct exception_stack_frame
{rt_uint32_t r0;rt_uint32_t r1;rt_uint32_t r2;rt_uint32_t r3;rt_uint32_t r12;rt_uint32_t lr;rt_uint32_t pc;rt_uint32_t psr;
};struct stack_frame
{/* r4 ~ r11 register */rt_uint32_t r4;rt_uint32_t r5;rt_uint32_t r6;rt_uint32_t r7;rt_uint32_t r8;rt_uint32_t r9;rt_uint32_t r10;rt_uint32_t r11;struct exception_stack_frame exception_stack_frame;
};到这里,我们就清楚了线程在创建时分配的数组是如何转化为线程栈的, 动态分配的数组位于堆上, 数组作为栈来使用时,需要从数组的末尾进行栈的初始化。
再来看函数rt_thread_startup, 我们知道,在linux环境下,pthread_create函数创建线程后,子线程也会立即被调度执行,那么,在RT-Thead环境下,也是这样的效果吗?
/*** This function will start a thread and put it to system ready queue** @param thread the thread to be started** @return the operation status, RT_EOK on OK, -RT_ERROR on error*/
rt_err_t rt_thread_startup(rt_thread_t thread)
{/* thread check */RT_ASSERT(thread != RT_NULL);RT_ASSERT(thread->stat == RT_THREAD_INIT);/* set current priority to init priority */thread->current_priority = thread->init_priority;/* calculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32thread->number = thread->current_priority >> 3; /* 5bit */thread->number_mask = 1L << thread->number;thread->high_mask = 1L << (thread->current_priority & 0x07); /* 3bit */
#elsethread->number_mask = 1L << thread->current_priority;
#endifRT_DEBUG_LOG(RT_DEBUG_THREAD, ("startup a thread:%s with priority:%d\n",thread->name, thread->init_priority));/* change thread stat */thread->stat = RT_THREAD_SUSPEND;/* then resume it */rt_thread_resume(thread);if (rt_thread_self() != RT_NULL)//这里是子线程是否立即被调度执行的关键{/* do a scheduling */rt_schedule();}return RT_EOK;
}/*** This function will return self thread object** @return the self thread object*/
rt_thread_t rt_thread_self(void)
{return rt_current_thread;//全局变量
}
所以, pthread_create创建的子线程是否立即调度的关键在于全局变量rt_current_thread是否为NULL,我们分析系统启动代码:
void rtthread_startup(void)
{/* init board */rt_hw_board_init();#ifdef RT_USING_HEAP
#if STM32_EXT_SRAMrt_system_heap_init((void*)STM32_EXT_SRAM_BEGIN, (void*)STM32_EXT_SRAM_END);
#else
#ifdef __CC_ARMrt_system_heap_init((void*)&Image$$RW_IRAM1$$ZI$$Limit, (void*)STM32_SRAM_END);
#elif __ICCARM__rt_system_heap_init(__segment_end("HEAP"), (void*)STM32_SRAM_END);
#else/* init memory system */rt_system_heap_init((void*)&__bss_end, (void*)STM32_SRAM_END);
#endif
#endif /* STM32_EXT_SRAM */
#endif /* RT_USING_HEAP *//* init scheduler system */rt_system_scheduler_init();/* initialize timer */rt_system_timer_init();/* init timer thread */rt_system_timer_thread_init();#ifdef HW_RTC/* init rtc */rt_hw_rtc_init();
#endif/* init application */rt_application_init();//应用程序线程初始化#ifndef FACTORY_TEST_MODEsetup_watchdog();
#endif/* init idle thread */rt_thread_idle_init();// 系统空闲线程初始化,/* start scheduler */rt_system_scheduler_start();//启动内核调度器/* never reach here */return ;
}int main(void)
{/* disable interrupt first */rt_hw_interrupt_disable();/* startup RT-Thread RTOS */rtthread_startup();return 0;
}/*** @ingroup SystemInit* This function will startup scheduler. It will select one thread* with the highest priority level, then switch to it.*/
void rt_system_scheduler_start(void)
{register struct rt_thread *to_thread;register rt_ubase_t highest_ready_priority;#if RT_THREAD_PRIORITY_MAX > 32register rt_ubase_t number;number = __rt_ffs(rt_thread_ready_priority_group) - 1;highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;
#elsehighest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
#endif/* get switch to thread */to_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,struct rt_thread,tlist);rt_current_thread = to_thread;//对全局变量进行设置, 相当于内核调度器的启动开关/* switch to new thread */rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);/* never come back */
}
所以, 在rt_system_scheduler_start调用之前, 通过pthread_create创建的线程是不会被调度的。rt_system_scheduler_start是内核调度器的启动开关,只有在内核调度器启动后,才可通过rt_schedule进行线程切换。
rt_system_scheduler_start 与 rt_schedule的区别可以参考文章https://blog.csdn.net/u011734326/article/details/90173478
这篇关于RT-Thread内核源码分析-线程栈结构分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!