Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
内核学习营
RT-Thread--内核学习thread(二)
发布于 2018-09-29 14:45:49 浏览:2695
订阅该版
先说点题外话,看了RTT的源代码,其实还挺感慨的,RTT源代码结构清晰,完整的面向对象编程方式,命名规范,实在是非常难得。希望能在之后的发展中继续把关代码风格审核,不要丢掉这个优秀的习惯。 首先还是先了解下rt_thread(基本版)类所包含的成员: 1.与object完全一样的部分,这个地方按道理是可以直接使用struct rt_object parent的,事实上除了rt_thread类以外,其他类也都是这样做的。 2.tlist用于rt_thread的一个双向链表,把每个rt_thread实例通过链表链接起来,方便通过name成员来查找对应的rt_thread. 3.sp一个数据指针,指向线程栈当前使用的地址,线程切换之后,用来保存MCU中寄存器sp中的数据。 4.entry一个函数指针,指向线程的入口函数。 5.parameter一个数据指针,指向传入线程的参数的数据地址。 6.stack_addr一个数据指针,指向为线程分配的栈所有数据的顶部,一旦分配完成在线程删除之前是一个固定值。 7.stack_size保存所分配的栈空间的大小。 8.error保存与线程相关的错误标志。 RTT申明了以下11种错误: ```#define RT_EOK 0 /**< There is no error */ #define RT_ERROR 1 /**< A generic error happens */ #define RT_ETIMEOUT 2 /**< Timed out */ #define RT_EFULL 3 /**< The resource is full */ #define RT_EEMPTY 4 /**< The resource is empty */ #define RT_ENOMEM 5 /**< No memory */ #define RT_ENOSYS 6 /**< No system */ #define RT_EBUSY 7 /**< Busy */ #define RT_EIO 8 /**< IO error */ #define RT_EINTR 9 /**< Interrupted system call */ #define RT_EINVAL 10 /**< Invalid argument */``` 9.stat 保存线程现在处在的状态。 RTT的线程有以下7(6)种状态: ```#define RT_THREAD_INIT 0x00 /**< Initialized status */ #define RT_THREAD_READY 0x01 /**< Ready status */ #define RT_THREAD_SUSPEND 0x02 /**< Suspend status */ #define RT_THREAD_RUNNING 0x03 /**< Running status */ #define RT_THREAD_BLOCK RT_THREAD_SUSPEND /**< Blocked status */ #define RT_THREAD_CLOSE 0x04 /**< Closed status */ #define RT_THREAD_STAT_MASK 0x0f``` 10.current_priority,init_priority这两个成员一个用来保存当前优先级,一个用来保存初始化的时候的优先级,当任务被启动的时候current_priority会被重新赋值为init_priority 11.number_mask与线程调度算法有关的一个参数,在调度器板块再来看这个部分。 12.init_tick,remaining_tick这两个成员在有相同优先级的线程同时运行的时候生效,init_tick记录当前线程在有相同优先级任务的时候的时间片长度,如init_tick=20则当运行20个tick后会进行线程切换。 13.thread_timer实例化了一个rt_timer类,该rt_timer主要用来定时唤醒线程,当线程获取s信号量或者互斥锁或邮箱、队列的时候会拥有一个超时时间,此时会配置这个软定时器,时间到后对线程进行唤醒。 14.cleanup一个函数指针,指向一个清理函数,当线程退出的时候,将会执行该函数。 15.user_data用户数据 ```struct rt_thread { /* rt object */ char name[RT_NAME_MAX]; /**< the name of thread */ rt_uint8_t type; /**< type of object */ rt_uint8_t flags; /**< thread's flags */ rt_list_t list; /**< the object list */ rt_list_t tlist; /**< the thread list */ /* stack point and entry */ void *sp; /**< stack point */ void *entry; /**< entry */ void *parameter; /**< parameter */ void *stack_addr; /**< stack address */ rt_uint32_t stack_size; /**< stack size */ /* error code */ rt_err_t error; /**< error code */ rt_uint8_t stat; /**< thread status */ /* priority */ rt_uint8_t current_priority; /**< current priority */ rt_uint8_t init_priority; /**< initialized priority */ #if RT_THREAD_PRIORITY_MAX > 32 rt_uint8_t number; rt_uint8_t high_mask; #endif rt_uint32_t number_mask; rt_ubase_t init_tick; /**< thread's initialized tick */ rt_ubase_t remaining_tick; /**< remaining tick */ struct rt_timer thread_timer; /**< built-in thread timer */ void (*cleanup)(struct rt_thread *tid); /**< cleanup function when thread exit */ rt_uint32_t user_data; /**< private user data beyond this thread */ };``` 看完rt_thread包含的成员,挺多的,但每个成员的作用,还算是比较清晰的。那么我们继续来聊thread相关的函数。 主要是以下几个函数, ```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); rt_err_t rt_thread_detach(rt_thread_t thread); 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); rt_thread_t rt_thread_self(void); rt_thread_t rt_thread_find(char *name); rt_err_t rt_thread_startup(rt_thread_t thread); rt_err_t rt_thread_delete(rt_thread_t thread); rt_err_t rt_thread_yield(void); rt_err_t rt_thread_delay(rt_tick_t tick); rt_err_t rt_thread_mdelay(rt_int32_t ms); rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg); rt_err_t rt_thread_suspend(rt_thread_t thread); rt_err_t rt_thread_resume(rt_thread_t thread); void rt_thread_timeout(void *parameter);``` 可以看到作为继承于object的子类,rt_thread类基本上重写了object相关的所有函数,现在来逐一分析每个函数所做的操作: 一、首先是rt_thread_init()以及rt_thread_create() rt_thread_init()与rt_thread_create()的差异同样是在一个是静态线程的初始化,一个是动态的创建线程,函数内部的差异仅仅是在rt_thread_create()会为创建的线程从堆中申请栈空间并为要实例化的rt_thread申请内存。 这两个函数可以看做C++中的构造函数。 ```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; /******************************申请内存********************************/ thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread, name); if (thread == RT_NULL) return RT_NULL; stack_start = (void *)RT_KERNEL_MALLOC(stack_size); if (stack_start == RT_NULL) { /* allocate stack failure */ rt_object_delete((rt_object_t)thread); return RT_NULL; } /*********************************************************************/ /******************************成员初始化*****************************/ _rt_thread_init(thread, name, entry, parameter, stack_start, stack_size, priority, tick); /*********************************************************************/ return thread; } RTM_EXPORT(rt_thread_create);``` 两个函数最终都在_rt_thread_init()这个私有函数中进行成员初始化。 _rt_thread_init主要执行以下操作: * 1.双向链表指针指向自身。 * 2.赋值入口函数地址。 * 3.赋值入口函数参数。 * 4.线程栈相关的初始化: * 赋值栈顶地址,赋值栈大小。 * 将栈空间所有ram设置为'#'(一般是用做检测栈的使用率)。 * 调用rt_hw_stack_init()对栈顶数据进行初始化(这个设计到线程切换时,保存当前线程的相应PC地址,临时变量,以及MCU寄存器的值等既保存线程的上下文),之后会详细介绍这个函数。 * 5.初始化优先级。 * 6.初始化init_tick,remaining_tick (作用上文提过) * 7.初始化错误标志。 * 8.初始化线程状态。 * 9.初始化clean_up函数和user_data,这里直接初始化为0,默认不使用。 * 10.初始化定时器,将定时器的time_out回调设置为rt_thread_timeout(),这个函数会将线程从阻塞队列中移出,加入就绪队列,并将error成员置为RT_ETIMEOUT * 11.调用rt_thread_inited_hook()钩子函数 * ```static rt_err_t _rt_thread_init(struct rt_thread *thread, * 说到rt_hw_stack_init()函数,首先需要介绍下MCU里的程序的运行规律,在裸机环境下,程序的临时变量,函数的参数,代码运行的地址(PC)统统放在一个叫栈的地方,栈是MCU RAM 里面一段连续的内存空间,栈的大小一般在启动文件或者链接脚本里面指定, 最后由 C 库函数_main 进行初始化。 但是, 在多线程的RTOS中,每个线程都是独立的,互不干扰的,所以要为每个线程都分配独立的栈空间,这个栈空间可以是一个预先定义好的全局数组(静态创建的线程), 也可以是动态分配的一段内存空间(动态创建的线程),但归根结底它们都存在于 MCU的RAM 中。 学过汇编指令的肯定都熟悉,当MCU执行中断或者跳转函数的时候,有一个保护现场的动作,把MCU相应的寄存器数据,SP指针,PC地址PUSH入栈,这些入栈出栈操作有些是MCU自动完成的,有些必须要通过程序要完成。入栈结束后再执行跳转,当中断结束后,要返回当前程序位置的时候再从栈中把数据取出来,重新跳转到之前程序位置,恢复临时变量的值,这就叫恢复现场。 这个两个步骤其实也就是RTOS实现线程切换的最根本的原理。 重新回到rt_hw_stack_init()函数,它其实完成的就是对线程栈的最基础的初始化。 首先在栈顶空出了4个字节,然后进行了8字节对齐操作,然后留出了空间用来存储stack_frame中的内容,stack_frame包含与CPU、FPU相关的一些寄存器(R0-R11,PC,LR等等)。 ```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); stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8); stk -= sizeof(struct 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)* = 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 */ #if USE_FPU stack_frame->flag = 0; #endif /* USE_FPU */ /* return task's current stack address */ return stk; }``` 二、rt_err_t rt_thread_detach()和rt_thread_delete() 这两个函数都用来删除线程的,区别是第一个是作用到静态创建的线程,第二个的作用是删除动态创建的线程。 1.参数检查。 2.将线程移出调度器队列(线程将不会再被调度) 3.将线程的timer移出timer队列(timer将不会进行计时) 4.设置stat成员为RT_THREAD_CLOSE 5.对于动态创建的线程,直接将线程加入rt_thread_defunct队列,对于静态的线程,只有在有clean_up函数的时候才加入rt_thread_defunct。在idle任务中,会对处在defunct队列中的线程进行释放内存等收尾工作。 ```rt_err_t rt_thread_delete(rt_thread_t thread) { rt_base_t lock; /* thread check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); RT_ASSERT(rt_object_is_systemobject((rt_object_t)thread) == RT_FALSE); if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) { /* remove from schedule */ rt_schedule_remove_thread(thread); } /* release thread timer */ rt_timer_detach(&(thread->thread_timer)); /* change stat */ thread->stat = RT_THREAD_CLOSE; /* disable interrupt */ lock = rt_hw_interrupt_disable(); /* insert to defunct thread list */ rt_list_insert_after(&rt_thread_defunct, &(thread->tlist)); /* enable interrupt */ rt_hw_interrupt_enable(lock); return RT_EOK; } rt_err_t rt_thread_detach(rt_thread_t thread) { rt_base_t lock; /* thread check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); RT_ASSERT(rt_object_is_systemobject((rt_object_t)thread)); if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) { /* remove from schedule */ rt_schedule_remove_thread(thread); } /* release thread timer */ rt_timer_detach(&(thread->thread_timer)); /* change stat */ thread->stat = RT_THREAD_CLOSE; /* detach object */ rt_object_detach((rt_object_t)thread); if (thread->cleanup != RT_NULL) { /* disable interrupt */ lock = rt_hw_interrupt_disable(); /* insert to defunct thread list */ rt_list_insert_after(&rt_thread_defunct, &(thread->tlist)); /* enable interrupt */ rt_hw_interrupt_enable(lock); } return RT_EOK; }``` 三、rt_err_t rt_thread_startup()线程启动函数 启动函数主要执行了: 1.参数检查 2.将current_priority 重新赋值为init_priority 3.为number_mask赋值,调度器会使用,之后再说 4.将stat置为RT_THREAD_SUSPEND挂起状态 5.调用rt_thread_resume启动线程。 6.判断当前是否有线程在运行(既调度器是否启动),若有则启动一次调度(此时若线程具有最高优先级,则会切换到该线程开始运行) ```rt_err_t rt_thread_startup(rt_thread_t thread) { /* thread check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); /* set current priority to init priority */ thread->current_priority = thread->init_priority; /* calculate priority attribute */ #if RT_THREAD_PRIORITY_MAX > 32 thread->number = thread->current_priority >> 3; /* 5bit */ thread->number_mask = 1L << thread->number; thread->high_mask = 1L << (thread->current_priority & 0x07); /* 3bit */ #else thread->number_mask = 1L << thread->current_priority; #endif RT_DEBUG_LOG(RT_DEBUG_THREAD, ("startup a thread:%s with priority:%d
", 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; }``` 四、rt_thread_control()函数 该函数根据参数cmd的不同有几种不同的功能, cmd可取的值为: ```RT_THREAD_CTRL_CHANGE_PRIORITY 修改线程的优先级 RT_THREAD_CTRL_STARTUP 启动线程 RT_THREAD_CTRL_CLOSE 关闭删除线程``` 只有修改优先级是独有的功能 ```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 > 32 thread->number = thread->current_priority >> 3; /* 5bit */ thread->number_mask = 1 << thread->number; thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ #else thread->number_mask = 1 << thread->current_priority; #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 > 32 thread->number = thread->current_priority >> 3; /* 5bit */ thread->number_mask = 1 << thread->number; thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ #else thread->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); #ifdef RT_USING_HEAP case RT_THREAD_CTRL_CLOSE: return rt_thread_delete(thread); #endif default: break; } return RT_EOK; }``` 五、其他函数 ```(1)rt_thread_t rt_thread_self(void); (2)rt_thread_t rt_thread_find(char *name); (3)rt_err_t rt_thread_yield(void); (4)rt_err_t rt_thread_delay(rt_tick_t tick); (5)rt_err_t rt_thread_mdelay(rt_int32_t ms); (6)rt_err_t rt_thread_suspend(rt_thread_t thread); (7)rt_err_t rt_thread_resume(rt_thread_t thread); (8)void rt_thread_timeout(void *parameter);``` (1)获取当前正在运行的线程指针。 (2)通过线程名字获取线程的句柄。 (3)yield这个单词不好翻译,简单的来说这个函数的功能就是进行一次线程切换,把当前线程加入到就绪队列的最末尾,同时执行就绪队列的第一个线程。 (4)(5)延时当前线程,具体方式是将当前线程悬挂,并启动线程的timer,当时间到了之后通过time_out回调唤醒任务。 (6)挂起线程。 挂起线程会将线程移出调度器队列,使得线程无法被调度,将线程状态置为RT_THREAD_SUSPEND,并关闭线程的timer,最后会调用rt_thread_suspend_hook()钩子函数 (7)恢复线程 恢复线程会将线程移出挂起队列(当线程被相应信号挂起的时候,就会被加入到对应信号的挂起队列中,等待信号被释放),重新加入调度器就绪队列。 (8)线程timer的time_out回调。 将线程的error设置为 -RT_ETIMEOUT,并加线程移出当前的队列,重新加入调度器就绪队列,并执行一次调度。
查看更多
4
个回答
默认排序
按发布时间排序
yqiu
2018-09-29
这家伙很懒,什么也没写!
挺好,加油!
yushigengyu
认证专家
2018-09-29
这家伙很懒,什么也没写!
>挺好,加油! --- 这论坛人太少了。。
yuexiaoban
2018-12-22
这家伙很懒,什么也没写!
不错,来回学习几次就熟悉了
yangjie
认证专家
2018-12-26
hello
欢迎在论坛添砖加瓦~
撰写答案
登录
注册新账号
关注者
0
被浏览
2.7k
关于作者
yushigengyu
这家伙很懒,什么也没写!
提问
15
回答
25
被采纳
0
关注TA
发私信
相关问题
1
【内核学习】rtthread内核移植记录-STM32F103ZET6-HAL库
2
《内核学习营》+水一方+自用STM32F103VC 板RT-Thread内核移植分享
3
《内核学习营》+水一方+项目中创建标准的 RT-Thread工程
4
内核学习营+坦然+探索者stm32f407板子RT-thread循环点亮led灯
5
<内核学习营>+坦然+探索者stm32f407板子RT-thread串口字符点灯
6
<内核学习营>+坦然+探索者stm32f407板子RT-thread的pwm点灯实验
7
<内核学习营>+坦然+探索者stm32f407板子RT-thread串口实验
8
<内核学习营>+坦然+野火stm32f103板子RT-thread读写SD卡实验
9
<内核学习营>+坦然+探索者stm32f407板子RT-thread的RTC闹钟实验
10
【内核学习营】+王秀峰+led_rgb
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
在用clangd开发RTT吗,快来试试如何简单获得清晰干净的工作区
2
GD32F450 片内 flash驱动适配
3
STM32H7R7运行CherryUSB
4
RT-Smart首次线下培训,锁定2024 RT-Thread开发者大会!
5
使用RC522软件包驱动FM1722
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
6
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部