RT-Thread内核源码分析-线程栈结构分析

2024-06-16 14:18

本文主要是介绍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内核源码分析-线程栈结构分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1066671

相关文章

【Linux进阶】UNIX体系结构分解——操作系统,内核,shell

1.什么是操作系统? 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kerel),因为它相对较小,而且位于环境的核心。  从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特生。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等

[职场] 公务员的利弊分析 #知识分享#经验分享#其他

公务员的利弊分析     公务员作为一种稳定的职业选择,一直备受人们的关注。然而,就像任何其他职业一样,公务员职位也有其利与弊。本文将对公务员的利弊进行分析,帮助读者更好地了解这一职业的特点。 利: 1. 稳定的职业:公务员职位通常具有较高的稳定性,一旦进入公务员队伍,往往可以享受到稳定的工作环境和薪资待遇。这对于那些追求稳定的人来说,是一个很大的优势。 2. 薪资福利优厚:公务员的薪资和

springboot家政服务管理平台 LW +PPT+源码+讲解

3系统的可行性研究及需求分析 3.1可行性研究 3.1.1技术可行性分析 经过大学四年的学习,已经掌握了JAVA、Mysql数据库等方面的编程技巧和方法,对于这些技术该有的软硬件配置也是齐全的,能够满足开发的需要。 本家政服务管理平台采用的是Mysql作为数据库,可以绝对地保证用户数据的安全;可以与Mysql数据库进行无缝连接。 所以,家政服务管理平台在技术上是可以实施的。 3.1

高仿精仿愤怒的小鸟android版游戏源码

这是一款很完美的高仿精仿愤怒的小鸟android版游戏源码,大家可以研究一下吧、 为了报复偷走鸟蛋的肥猪们,鸟儿以自己的身体为武器,仿佛炮弹一样去攻击肥猪们的堡垒。游戏是十分卡通的2D画面,看着愤怒的红色小鸟,奋不顾身的往绿色的肥猪的堡垒砸去,那种奇妙的感觉还真是令人感到很欢乐。而游戏的配乐同样充满了欢乐的感觉,轻松的节奏,欢快的风格。 源码下载

高度内卷下,企业如何通过VOC(客户之声)做好竞争分析?

VOC,即客户之声,是一种通过收集和分析客户反馈、需求和期望,来洞察市场趋势和竞争对手动态的方法。在高度内卷的市场环境下,VOC不仅能够帮助企业了解客户的真实需求,还能为企业提供宝贵的竞争情报,助力企业在竞争中占据有利地位。 那么,企业该如何通过VOC(客户之声)做好竞争分析呢?深圳天行健企业管理咨询公司解析如下: 首先,要建立完善的VOC收集机制。这包括通过线上渠道(如社交媒体、官网留言

百度OCR识别结构结构化处理视频

https://edu.csdn.net/course/detail/10506

微信小程序开发必知必会:文件结构和基本配置

一、微信小程序基本文件结构 1.  project.config.json:项目的基本配置文件,包括项目名称、appid、项目目录、页面文件夹等。     {"setting": {"urlCheck": false,"es6": true,"postcss": true,"nodeModulesPath": "D:\\\\node_modules"},"appid": "wxd678e

基于Java医院药品交易系统详细设计和实现(源码+LW+调试文档+讲解等)

💗博主介绍:✌全网粉丝10W+,CSDN作者、博客专家、全栈领域优质创作者,博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 🌟文末获取源码+数据库🌟 感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人  Java精品实战案例《600套》 2023-2025年最值得选择的Java毕业设计选题大全:1000个热

利用结构体作为函数参数时结构体指针的定义

在利用结构体作为函数的参数进行传递时,容易犯的一个错误是将一个野指针传给函数导致错误。 #include <stdio.h>#include <math.h>#include <malloc.h>#define MAXSIZE 10typedef struct {int r[MAXSIZE]; //用于存储要排序的数组,r[0]作为哨兵或者临时变量int length;

美容美发店营销版微信小程序源码

打造线上生意新篇章 一、引言:微信小程序,开启美容美发行业新纪元 在数字化时代,微信小程序以其便捷、高效的特点,成为了美容美发行业营销的新宠。本文将带您深入了解美容美发营销微信小程序,探讨其独特优势及如何助力商家实现业务增长。 二、微信小程序:美容美发行业的得力助手 拓宽客源渠道:微信小程序基于微信社交平台,轻松实现线上线下融合,帮助商家快速吸引潜在客户,拓宽客源渠道。 提升用户体验: