Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
内核学习营
个人学习内核心得(二)——线程管理
发布于 2019-02-27 14:03:26 浏览:3592
订阅该版
* 本帖最后由 家定不举棋 于 2019-3-2 16:40 编辑 * 线程:轻量级进程,是实现任务的载体,是RT-Thread的最基本的调度单位。 众多线程的集合形成了RTThread线程,RTThread线程可分为系统线程和用户线程 区别点:系统线程为系统内核所创建的;用户线程为应用程序所创建的线程 相同点:都是由内核对象容器中分配的,且都具有线程属性 每个线程都有自己的线程控制块,描述了线程的基本信息。线程控制块为struct rt_thread: ![线程控制块rt_thread.png](/uploads/201902/27/141745thdz9snnbrhz5n5b.png) ```struct rt_thread { /*线程对象 */ char name[RT_NAME_MAX];//线程名称 rt_uint8_t type;//线程类型 rt_uint8_t flags;//线程标志位 #ifdef RT_USING_MODULE void *module_id;//应用程序模块ID #endif rt_list_t list;//对象列表 rt_list_t tlist;//线程列表 /* 栈指针和函数入口 */ void *sp;//栈指针 void *entry;//入口函数指针 void *parameter;//参数 void *stack_addr;//栈指针地址 rt_uint32_t stack_size;//栈大小 /* 错误代码 */ rt_err_t error;//线程错误标志 rt_uint8_t stat;//线程状态 /* 优先级 */ rt_uint8_t current_priority;//线程当前优先级 rt_uint8_t init_priority;//初始化的优先级 rt_uint32_t number_mask; #if defined(RT_USING_EVENT) rt_uint32_t event_set;//线程接受到的事件 rt_uint8_t event_info;//线程的事件过滤信息,用于过滤事件,只保留感兴趣的事件 #endif #if defined(RT_USING_SIGNALS) rt_sigset_t sig_pending;//等待信号 rt_sigset_t sig_mask;//信号的掩码位 void *sig_ret;//从信号处返回的堆栈指针 rt_sighandler_t *sig_vectors;//信号处理向量 void *si_list;//信号输入列表 #endif rt_ubase_t init_tick;//线程初始化计数值 rt_ubase_t remaining_tick;//线程剩余计数值 struct rt_timer thread_timer;//内置线程定时器 void (*cleanup)(struct rt_thread *tid);//线程退出清除函数 /* light weight process if present */ #ifdef RT_USING_LWP void *lwp; #endif rt_uint32_t user_data;//用户数据 };```线程属性由线程对象、栈指针与入口指针、错误代码、优先级、事件、信号等组成:线程对象描述了线程的基本信息; 1.线程的栈指针用来存储线程切换时的上下文和函数中的局部变量;2.线程的入口函数描述了线程的执行函数; 线程入口函数可分为无限循环模式和顺序执行或有限次循环模式,无限循环模式必须得有让出CPU的动作(主动挂起或延时) 3.线程的错误代码和优先级用来给出错误状态和线程状态; 其中线程的状态总共有五种:初始状态、就绪状态、运行状态、挂起状态、关闭状态;线程的错误代码有十种,具体如下图所示: ![线程状态.png](/uploads/201902/27/175559eennehnxnfenzedr.png) ![线程错误码.png](/uploads/201902/27/182408t1qzlktnxooo1ofb.png) 4.线程的优先级描述了线程的优先级,这里包括初始化的优先级和当前的优先级(当前优先级不一定和初始化的优先级相同); RT-Thread最大支持256个线程优先级(0~255),数值越小优先级越高。ARM Cortex-M系列普遍采用32个优先级,最低优先级默认分配给空闲线程。 线程调度使用高优先级抢占低优先级,优先级相同采取时间片轮转的调度。时间片仅对优先级相同的就绪态线程有效。就是如果低优先级的程序正在执行,当有高优先级的程序进来,那么低优先级线程给高优先级线程让位;如果两个优先级相同的线程,那么每个线程轮流执行一段自己的时间片。 时间片单位为一个系统节拍OS Tick。 5.线程的事件和信号表示线程所接收到的事件和信号。 线程结束:自动执行rt_thread_exit()函数->进入关闭状态挂到rt_thread_defunct队列中->空闲线程回收资源 系统线程有:空闲线程和主线程。 空闲线程是系统创建的优先级最低的线程,其状态为就绪态,一般为死循环。 空闲线程作用:回收空闲被删除线程的资源,调用用户设定的钩子函数,钩子函数由于RT_IDEL_HOOK_LIST_SIZE定义,所以钩子函数一次只能设置4个 主线程:创建main_thread_entry()入口函数,为用户应用提供接口 线程管理部分API: ![线程管理部分API.png](/uploads/201902/28/134129q4vb5nno3s48w4n4.png) ```/*回收执行完成后的线程 *无输入 *1.获取当前线程 *2.禁止系统中断,将系统中断信息存储到level中 *3.将当前线程从调度器中移除 *4.将线程的状态改成RT_THREAD_CLOSE *5.将线程从时间列表中脱离 *6.判断线程是否为静态系统线程且是否被清理 *6-1.如果是静态线程且未被清除用rt_object_detach函数将线程清理,否则将线程插入到rt_thread_defunct列表中 *7.开启系统中断,切换到下一个任务*/ void rt_thread_exit(void) /*静态初始化创建一个线程 *输入所要创建的线程句柄、线程名称、线程入口函数、线程入口函数参数、线程起始地址、线程栈大小、线程优先级、线程时间片 *1.检查thread和stack_size参数是否为空 *2.初始化一个线程对象 *3.调用系统初始化的函数_rt_thread_init,初始化创建一个静态函数*/ 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) /*系统初始化创建一个线程 *输入所要创建的线程句柄、线程名称、线程入口函数、线程入口函数参数、线程起始地址、线程栈大小、线程优先级、线程时间片 *1.初始化线程节点 *2.将输入的入口函数、函数参数、栈地址、栈大小输入到线程句柄中 *3.初始化栈中所有的字节为'#' *4.初始化设置sp的内容 *5.确保线程优先级小于RT_THREAD_PRIORITY_MAX *6.将线程中的init_priority和current_priority初始化为输入的线程优先级 *7.初始化tick和remain tick,将线程错误状态设置为RT_EOK,将线程状态设置为RT_THREAD_INIT, *8.初始化线程清扫参数为0,用户数据为0 *9.初始化线程定时器,返回RT_EOK*/ 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) /*返回当前正在运行的线程 return rt_current_thread*/ rt_thread_t rt_thread_self(void) /*启动一个线程,并把该线程放入到就绪队列中 *输入所要启动的线程句柄 *1.参数检查,判断该线程不为空,且该线程的状态为RT_THREAD_INIT,输入对象类型为线程类型RT_Object_Class_Thread *2.当线程的当前优先级改为线程的初始化优先级 *3.设置线程的位图掩码 *3.将线程的状态设置为RT_THREAD_SUSPEND *4.重新恢复还原线程 *5.如果当前线程不为空,那么执行线程调度,返回RT_EOK*/ rt_err_t rt_thread_startup(rt_thread_t thread) /*脱离一个静态线程 *输入需要脱离的静态线程句柄 *1.判断线程句柄是否为空,线程类型是否为RT_Object_Class_Thread,线程是否为静态线程 *2.如果线程状态不为RT_THREAD_INIT,那么将当前线程从调度器中移除 *3.将线程定时器脱离 *4.将线程的状态设置为RT_THREAD_CLOSE *5.将线程从内核对象中脱离 *6.如果线程的清理参数不为空,那么执行下列操作: *6-1.关闭系统中断 *6-2.将线程插入到rt_thread_defunct中 *6-3.打开系统中断 *7.返回RT_EOK*/ rt_err_t rt_thread_detach(rt_thread_t thread) /*动态创建一个线程 *输入线程名称、线程入口函数、函数参数、线程堆栈大小、线程优先级、线程时间片 *1.从对象系统中分配一个线程对象,确定创建的线程不为RT_NULL *2.动态申请内存,大小为stack_size,如果申请不到内存则删除刚创建的动态线程 *3.调用_rt_thread_init函数,初始化创建一个线程*/ 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) /*删除一个动态线程 *输入所要删除的动态线程句柄 *1.确定线程不为空,线程的类型为RT_Object_Class_Thread,输入的线程不为静态线程 *2.如果线程状态不为RT_THREAD_INIT,则从调度器上移除线程 *3.脱离线程定时器 *4.将线程状态设置为RT_THREAD_CLOSE *5.禁止系统中断 *6.将线程加入到rt_thread_defunct列表中 *7.恢复系统中断,返回RT_EOK*/ rt_err_t rt_thread_delete(rt_thread_t thread) /*将当前线程让出处理器,调度器将调用最高优先级的线程,被让出的线程将仍保持就绪状态,排到队列的最后面 *无输入 *1.关闭系统中断 *2.将当前线程赋值到thread *3.如果当前线程的状态仍然是RT_THREAD_READY并且线程仍然在就绪队列上,那么执行以下操作: *3-1.从就绪队列中移除当前线程 *3-2.将线程加入到就绪队列的最后面 *3-3.打开系统中断,重新打开调度器,返回RT_EOK *4.不然直接打开中断,返回RT_EOK*/ rt_err_t rt_thread_yield(void) /*让当前线程睡眠若干个tick *输入睡眠的tick大小 *1.关闭系统中断,获取当前线程且确保当前线程不为RT_NULL和确保当前线程的类型为RT_Object_Class_Thread *2.挂起当前线程 *3.设定线程定时器并启动线程定时器 *4.打开中断,启动调度器 *5.将线程的错误标志改为RT_EOK并且返回RT_EOK*/ rt_err_t rt_thread_sleep(rt_tick_t tick) /*让当前线程延时若干个tick *输入要延时的tick大小 *该函数的本质为返回rt_thread_sleep(tick);*/ rt_err_t rt_thread_delay(rt_tick_t tick) /*让当前线程延时若干个毫秒 *输入所要延时的时间(单位为ms) *1.将ms转换为tick *2.调用rt_thread_sleep函数*/ rt_err_t rt_thread_mdelay(rt_int32_t ms) /*用命令的方式控制线程 *输入所要控制的线程句柄、控制命令、命令参数,其中控制命令为:RT_THREAD_CTRL_CHANGE_PRIORITY,RT_THREAD_CTRL_STARTUP,RT_THREAD_CTRL_CLOSE *1.确保输入的线程不为RT_NULL且该线程类型为RT_Object_Class_Thread *2.判断命令 cmd=RT_THREAD_CTRL_CHANGE_PRIORITY:改变线程优先级 *2-1.关闭系统中断 *2-2 如果当前线程的状态为RT_THREAD_READY,执行: *2-2-1.将线程从调度队列中删除 *2-2-2.改变线程优先级 *2-2-3.设置线程掩码 *2-2-4.将线程再次插入到调度队列中 *2-3.不然执行: *2-3-1.将当前参数赋值到优先级中 *2-3-2.设置线程掩码 *2-4.打开系统中断 cmd=RT_THREAD_CTRL_STARTUP:启动线程 *启动该线程,rt_thread_startup(thread) cmd=RT_THREAD_CTRL_CLOSE:删除线程 *删除该线程,rt_thread_delete(thread) *3.返回RT-EOK*/ rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg) /*将线程挂起 *输入所要挂起的线程句柄,该线程只能是在就绪状态 *1.确保当前线程不为RT_NULL和确保当前线程的类型为RT_Object_Class_Thread *2.如果该线程不在就绪状态,那么返回-RT_ERROR *3.关闭系统中断 *4.将线程的状态设置为RT_THREAD_SUSPEND *5.将线程从调度器中移除 *6.停止线程计数器 *7.打开系统中断 *8.使用钩子函数 *9.返回RT_EOK*/ rt_err_t rt_thread_suspend(rt_thread_t thread) /*唤醒线程,重新开始一个线程并且将它放到系统就绪队列 *输入一个线程句柄 *1.确保线程不为RT_NULL,确保该线程类型为RT_Object_Class_Thread *2.如果该线程的状态不为RT_THREAD_SUSPEND那么返回-RT_ERROR *3.禁止中断使能 *4.将线程从挂起列表中移除 *5.停止线程定时器 *6.使能系统中断 *7.将线程插入到就绪列表 *8.调用钩子函数 *9.返回RT_EOK*/ rt_err_t rt_thread_resume(rt_thread_t thread) /*线程超时函数 *输入超时挂起的线程参数 *1.确保线程不为RT_NULL,确保该线程类型为RT_Object_Class_Thread,确保该线程的状态为RT_THREAD_SUSPEND *2.设置错误标志为-RT_ETIMEOUT *3.从线程从挂起列表中删除 *4.将线程插入到调度器就绪列表 *5.启动调度器*/ void rt_thread_timeout(void *parameter) /*通过线程名称查找线程 *输入线程名称 *1.如果当前线程不为空则进入临界区 *2.通过对象信息RT_Object_Class_Thread获得系统对象容器,确保系统对象容器不为RT_NULL *3.在对象列表中寻找 *4.获得每个内核对象 *5.对比内核对象的名称和输入的线程名称,如果相同且当前线程不为RT_NULL则退出临界区返回对象 *6.不然退出临界区返回RT_NULL*/ rt_thread_t rt_thread_find(char *name)``` 线程调度器部分API:![线程调度器部分API.png](/uploads/201903/02/153834f64q41q2r4g35jmg.png) ```/*系统线程调度器初始化 *无输入 *1.调度器嵌套锁计数器初始值设为0 *2.初始化线程优先级的列表 *3.将当前线程优先级设置为RT_THREAD_PRIORITY_MAX - 1,当前线程为RT_NULL,初始化就绪优先级组 *4.初始化线程僵尸列表rt_thread_defunct*/ void rt_system_scheduler_init(void) /*启动线程调度器,选择最高优先级的线程来执行 *无输入 *1.寻找发现系统中优先级最高的线程 *2.将当前线程rt_current_thread设置为寻找到的线程 *3.切换到新线程进行执行*/ void rt_system_scheduler_start(void) /*执行线程调度 *无输入 *1.关闭系统中断,保存当前中断向量 *2.检查调度器是否使能,如果rt_scheduler_lock_nest>0则直接打开系统中断,结束 *3.获取当前就绪线程的最高优先级 *4.切换到该线程 *5.如果当前线程和目标线程一致,则直接打开系统中断,结束;否则执行: *5—1.获取最高就绪线程优先级 *5-2.设置钩子函数 *5-3.切换到新线程 *5-4.检查线程栈是否溢出 *5-5.如果当前没有处于中断嵌套中,那么使用rt_hw_context_switch进行线程切换,打开系统中断,检查信号状态 *5-6.否则使用rt_hw_context_switch_interrupt进行线程切换,打开系统中断,结束 *rt_hw_context_switch和rt_hw_context_switch_interrupt线程切换区别: *rt_hw_context_switch为在非中断线程中进行线程切换,rt_hw_context_switch_interrupt为在中断线程中进行线程切换*/ void rt_schedule(void) /*将线程插入到系统就绪队列中 *输入所要插入到就绪队列中的线程句柄 *不要在用户线程中调用该API *1.确保该线程不为RT_BULL *2.将系统中断失能 *3.改变线程状态为就绪状态RT_THREAD_READY *4.将线程插入到就绪列表中 *5.设置优先级掩码,使能系统中断*/ void rt_schedule_insert_thread(struct rt_thread *thread) /*从系统就绪队列中移除一个线程 *输入需要从就绪列表中移除的线程句柄 *不要在用户线程中调用该API *1.确保所要移除的线程不为RT_NULL *2.失能系统中断 *3.从就绪列表中移除线程 *4.如果当前优先级链表不为空设置线程就绪优先级组 *5启动系统中断*/ void rt_schedule_remove_thread(struct rt_thread *thread) /*锁定线程调度程序,进入临界区 *无输入 *1.关闭系统中断 *2.调度锁计数器rt_scheduler_lock_nest加1 *3.打开系统中断*/ void rt_enter_critical(void) /*解锁系统调度器 *无输入 *1.将系统中断失能 *2.调度锁计数器rt_scheduler_lock_nest减一 *3.如果rt_scheduler_lock_nest<0时将rt_scheduler_lock_nest设置为0,使能系统中断,开启调度器, *4.否则使能系统中断,结束*/ void rt_exit_critical(void) /*获取调度器锁计数器的值 *无输入,直接返回rt_scheduler_lock_nest*/ rt_uint16_t rt_critical_level(void)``` 空闲线程部分API: ![空闲线程部分API.png](/uploads/201903/02/163454xp44ro4hr955pxb8.png) ```/*在空闲线程中设置钩子函数,当系统执行空闲线程时,所设置的钩子函数件会被执行 *输入指定的钩子函数 *1.禁止系统中断,保存系统中断前的状态 *2.将钩子函数存到idle_hook_list函数指针数组中,由于宏定义RT_IDEL_HOOK_LIST_SIZE,则一次最多只能保存四个钩子函数 *3.启动系统中断 *4.*/ rt_err_t rt_thread_idle_sethook(void (*hook)(void)) /*从钩子函数列表中删除一个钩子函数 *输入所要删除的钩子函数指针 *1.关闭系统中断 *2.从钩子函数列表中寻找所要删除的钩子函数,并将所找到的钩子函数赋值为RT_NULL *3.打开系统中断*/ rt_err_t rt_thread_idle_delhook(void (*hook)(void)) /*确定是否有僵尸线程需要被删除*/ rt_inline int _has_defunct_thread(void) /*执行系统后台回收资源工作当系统空闲时,循环执行直至没有死线程 *无输入 *1.确定该函数不在系统中断历程中 *2.失能系统中断 *3.检查僵尸列表中是否为空,如果没有,打开系统中断返回;如果有那么执行: *3-1.获取僵尸线程 *3-2.将该线程从线程列表中删除 *3-3.锁定调度程序,防止在清除时被调度 *3-4.调用线程清理函数 *3-5.如果他是系统线程,解锁调度程序,开启中断返回;不然释放该线程堆栈,删除线程对象*/ void rt_thread_idle_excute(void) /*空闲线程入口函数 *1.确认钩子函数列表idle_hook_list中是否有钩子函数 *2.调用rt_thread_idle_excute释放死线程的资源*/ static void rt_thread_idle_entry(void *parameter) /*初始化空闲线程*/ void rt_thread_idle_init(void)``` 总结: ![线程管理.png](/uploads/201903/02/163740j7xwca0xvq760g3z.png) 相关思维导图文件: ![线程管理.xmind](/uploads/201903/02/164002or50qnese9v020ae.attach) ![线程调度器部分API.xmind](/uploads/201903/02/164002dfdifzzw83mmfw8g.attach) ![空闲线程部分API.xmind](/uploads/201903/02/164002nwwg8cwffzjretst.attach)
查看更多
3
个回答
默认排序
按发布时间排序
shadowliang
2019-03-19
Hello,world!!!
学习了,顶一下
yhb1206
2019-04-08
https://blog.csdn.net/yhb1206
牛叉!!!
撰写答案
登录
注册新账号
关注者
0
被浏览
3.6k
关于作者
家定不举棋
这家伙很懒,什么也没写!
提问
10
回答
6
被采纳
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
GD32F450 片内 flash驱动适配
2
STM32H7R7运行CherryUSB
3
RT-Smart首次线下培训,锁定2024 RT-Thread开发者大会!
4
使用RC522软件包驱动FM1722
5
常量数据类型和表达式陷阱分享
热门标签
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
篇文章
5
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部