携手创作,一起成长!这是我参加「日新方案 8 月更文挑战」的第3天,点击检查活动概况

前2课讲完了RT-Thread开发环境,发动流程,发动今后当然是开端跑线程了,
那么自然咱们得学会怎么创立线程以及线程的有关操作。

前言

前段时刻写完 RT-Thread 版本,开发环境,发动流程后,停了好一段时刻,由于完结了前面2课的讲解,感觉直接用起来都问题不大了,为啥,由于RTOS的调度,线程通讯等机制,学习过FreeRTOS,看看RT-Thread官方的文档阐明,许多东西就很清楚了= =!以至于在写本文的时分,都感觉,是不是太简略了?

可是后来又想了想:

1、本系列博文的意图在于总结记载,为的是今后在项目中运用起来,我可以直接参阅自己总结的博文,而不是去翻官方的文档资料。

2、尽量使得没有学习过 RT-Thread 的同学依据系列博文可以对 RT-Thread 有个知道,然后在一些细节的点上面有必定的了解,一起在遇到 RT-Thread 与 FreeRTOS不同的当地,会加以阐明。

3、最初的FreeRTOS系列,真便是很随意的按照自己学习测验的流程来走,对小白来说并不友爱,回头看起来,尽管我是真的画了精力和作业去阐明遇到的问题以及解决办法,可是少了循序渐进的过程,全体也没有一个好的结构体系,所以如同没有帮到太多人(看的人不多哈= =!)。所以在 RT-Thread 系列上面,该体系的仍是得体系起来,即使有些东西简略基础,官方和网上文档具体,作为一个笔记,该记载的仍是得记载!

好的,题外话说到这儿,咱们回到 RT-Thread 自身,上回咱们现已把发动流程讲清楚了, 上文的最终讲到:整个体系就正常跑起来了,然后用户运转自己想要做的作业,可以在 main 中设计自己的运用代码,或许创立线程。

所以咱们接下来当然得说说怎么创立线程以及线程的一些操作。

本 RT-Thread 专栏记载的开发环境:

RT-Thread记载(一、RT-Thread 版本、RT-Thread Studio开发环境 及 合作CubeMX开发快速上手)

RT-Thread记载(二、RT-Thread内核发动流程 — 发动文件和源码分析)

一、RT-Thread线程操作函数

RT-Thread线程操作包含:创立 / 初始化线程、发动线程、运转线程、删去 / 脱离线程。

1.1 动态创立线程

函数比较简略,具体的看注释就好(本文余下的函数介绍相似,看注释):

/*
demo,用来接收动态线程回来的句柄
比方 led2_thread  = rt_thread_create(......);
*/
static rt_thread_t led2_thread = RT_NULL; 
#ifdef RT_USING_HEAP      					//界说运用了HEAP才干动态创立线程
/*
参数的意义,放在上面看起来更加便利,要不然太长了
1、线程的称号;线程称号的最大长度由 rtconfig.h 中的宏 RT_NAME_MAX 指定,剩余部分会被主动截掉
2、线程进口函数
3、线程进口函数参数,没有就用 RT_NULL
4、线程栈巨细,单位是字节
5、线程的优先级。优先级规模依据体系配置状况(rtconfig.h 中的 RT_THREAD_PRIORITY_MAX 宏界说),
假如支撑的是 256 级优先级,那么规模是从 0~255,数值越小优先级越高,0 代表最高优先级
6、线程的时刻片巨细。时刻片(tick)的单位是操作体系的时钟节拍。
当体系中存在相同优先级线程时,这个参数指定线程一次调度可以运转的最大时刻长度。
这个时刻片运转结束时,调度器主动选择下一个安排妥当态的同优先级线程进行运转
回来值:
线程创立成功,回来线程句柄
线程创立失利,回来RT_BULL 
*/
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.2 静态创立线程

static struct rt_thread led1_thread;   //demo,用户界说的线程句柄
static char led1_thread_stack[256];    //demo,用户界说的静态线程巨细
/*
参数的意义
1、线程句柄。线程句柄由用户供给出来,并指向对应的线程操控块内存地址,上面的led1_thread。
2、线程的称号;线程称号的最大长度由 rtconfig.h 中界说的 RT_NAME_MAX 宏指定,剩余部分会被主动截掉
3、线程进口函数
4、线程进口函数参数,没有就用 RT_NULL
5、线程栈开端地址,依据上面界说便是 &led1_thread_stack[0],
6、线程栈巨细,单位是字节。依据上面界说便是 sizeof(led1_thread_stack),
在大多数体系中需求做栈空间地址对齐(例如 ARM 体系结构中需求向 4 字节地址对齐)
7、线程的优先级。优先级规模依据体系配置状况(rtconfig.h 中的 RT_THREAD_PRIORITY_MAX 宏界说),
假如支撑的是 256 级优先级,那么规模是从 0~255,数值越小优先级越高,0 代表最高优先级
8、线程的时刻片巨细。时刻片(tick)的单位是操作体系的时钟节拍。
当体系中存在相同优先级线程时,这个参数指定线程一次调度可以运转的最大时刻长度。
这个时刻片运转结束时,调度器主动选择下一个安排妥当态的同优先级线程进行运转
回来值:
线程创立成功,回来RT_EOK
线程创立失利,回来RT_ERROR 	
*/
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);

这儿需求阐明一下,为什么用户界说一个 char 类型的数组可以作为线程栈空间呢?

由于请求一个全局变量的数组,本质便是拓荒了一段接连的内存空间!这是用户请求的,所以在编译的时分就被确定分配好了,这段内存空间请求出来,通过rt_thread_init函数,就分配给了这个线程运用。

假如知道了上面的话,可是还不能了解内存空间和线程有什么关系的时分,这个就得慢慢来……简略来说便是,线程运转需求占用一段内存空间,这段内存空间每个线程的都不相同,他们是用来线程运转的时分,函数调用线程切换保存现场用的。

横竖先记住有必要给每个线程单独的一片内存空间,RTOS才干正常运转,一切的RTOS都是。

动态创立相同的意思,只不过你看不到,由内核函数主动处理了就没那么直观。

在上面示例代码中,256个char类型的数组,便是占用256个字节(char类型占用1个字节),所以最终分配给线程的空间便是256个字节。

1.3 发动线程

创立完线程并不代表线程就运转了,在RT-Thread称为初始状况,要跑起来需求人为的给他“开”一下,这儿与FreeRTOS创立使命后是不同的,FreeRTOS是直接创立完结就开端运转参加调度了。

创立的线程状况处于初始状况,并未进入安排妥当线程的调度行列,咱们可以在线程创立成功后调用rt_thread_startup函数接口让该线程进入安排妥当态:

/*
static rt_thread_t led2_thread = RT_NULL;
static struct rt_thread led1_thread;
上面的两个demo便是:
rt_thread_startup(&led1_thread);
rt_thread_startup(led2_thread);
*/
rt_err_t rt_thread_startup(rt_thread_t thread);

这儿又有一个小细节需求阐明一下,动态和静态创立线程的rt_thread_startup运用的小区别!

上面代码的注释中,两个Demo:

一个是rt_thread_startup(&led1_thread);(静态)

一个是rt_thread_startup(led2_thread);(动态)

静态线程为什么需求取地址,动态可以直接用,不细心看的话还不必定发现这个问题, 其实从他们的界说就现已不同了,只不过rt_thread_t rt_thread 一眼看去还真可能傻傻分不清楚 = =!以前我刚用的时分也在这儿模糊了一会:

static struct rt_thread led1_thread 静态类型为struct rt_thread 类型便是线程操控块结构体 static rt_thread_t led2_thread 动态类型为rt_thread_t 类型是一个指针,如下解释:

rt_thread_t这个类型他是通过 typedef 重名命的:

RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)

所以回到开端的问题,搞清楚了rt_thread_startup 函数的参数是线程操控块结构体指针, 再结合动态静态创立线程的线程句柄界说,这么问题就清楚了!明白了这个,那么这儿又可以阐明一个细节问题!如下

线程创立的一个细节—创立和初始化?

在文中,我介绍API运用的标题是“动态创立线程” 和“静态创立线程”,个人认为看上去好了解,也没问题,可是这儿注意官方的用语:

动态是 — 创立和删去线程

静态是 — 初始化和脱离线程

说白了都是新建线程,可是用词却不相同,为什么动态用创立,而静态用初始化呢?带着疑问咱们回头再去看看两种方式的不同。

在运用rt_thread_init之前,咱们需求界说两个东西,一个结构体,一个数组:

static struct rt_thread led1_thread;   //demo,用户界说的线程句柄
static char led1_thread_stack[256];    //demo,用户界说的静态线程巨细

在编译的时分,这个结构体和数组,就被分配了必定的内存空间,这段空间默认一般是初始化为0,便是空间给你留着了,可是等着你去放数据。不论在程序后边使不运用rt_thread_init,这段空间都现已存在了的! 这样来说,调用rt_thread_init只是对现已存在的一段内存空间的赋值,对一个存在的东西的设置,不便是叫做 初始化吗。所以运用静态的创立严厉的来说,更应该称之为初始化线程!

而在运用rt_thread_create之前,咱们只需求界说一个rt_thread_t 类型的指针,初始化是NULL就没有了,只要在调用rt_thread_create成功之后,才会拓荒出一块寄存线程操控块的内存空间,从无到有的一个过程,所以叫做 创立。

不得不佩服,官方仍是用词谨慎,其实想想也能更好的了解函数功能!

句柄是什么?

讲到这儿,为了让有些小伙伴更简单看懂,咱们再插一个细节,咱们常常听到回来句柄,函数句柄,使命句柄,那么句柄是什么?

记住一句话:句柄其实便是指针,它是指向指针的指针。

在咱们的rt_thread_create函数中,假如成功回来值是 线程句柄,类型为rt_thread_t ,咱们前面又讲过rt_thread_t 是一个结构体指针,这个结构体是线程操控块结构体,所以 在上面示例代码中回来句柄的意思 ,便是回来了一个指针,这个指针指向线程操控块。

(假如指针,指向指针的指针不明白,这是C言语基础知识,可以检查相关资料,我有一篇博文也说到过一二:C言语学习点滴笔记 中 4、指针: 一种特别的变量 和 多元指针,指向指针的指针)

1.4 删去线程和脱离线程

针对上面动态静态方法创立的线程,RT-Thread 有不同的删去函数:

关于运用rt_thread_create动态创立的线程,咱们运用rt_thread_delete函数,如下:

/*
参数:thread 	要删去的线程句柄
回来值:
RT_EOK 		删去线程成功
-RT_ERROR 	删去线程失利
*/
rt_err_t rt_thread_delete(rt_thread_t thread);

调用该函数后,线程目标将会被移出线程行列并且从内核目标办理器中删去,线程占用的仓库空间也会被开释。实践上,用 rt_thread_delete() 函数删去线程接口,仅仅是把相应的线程状况更改为 RT_THREAD_CLOSE 状况,然后放入到 rt_thread_defunct 行列中;而真实的删去动作(开释线程操控块和开释线程栈)需求到下一次履行闲暇线程时,由闲暇线程完结最终的线程删去动作。

关于运用rt_thread_init 静态创立的线程,咱们运用rt_thread_detach 函数,如下:

/*
参数:线程句柄,它应该是由 rt_thread_init 进行初始化的线程句柄。
回来值:
RT_EOK 		线程脱离成功
-RT_ERROR  	线程脱离失利
*/
rt_err_t rt_thread_detach (rt_thread_t thread);

官方在介绍rt_thread_detach有一句话,相同,线程自身不应调用这个接口脱离线程自身。这句话我了解便是不论动态删去仍是静态删去,不能在线程函数中自己把自己删去。 这儿也与FreeRTOS使命后不同,FreeRTOS可以直接在使命中调用函数删去自己。

可是需求特别阐明的是,在 RT-Thread 中履行结束的线程体系会主动将其删去!用户无需剩余操作,怎么了解呢,看下面的比方:

咱们一般线程函数都是死循环,通过延时开释CPU操控权,比方:

static void led1_thread_entry(void *par){
    while(1){   		
        //do_something
        rt_thread_mdelay(100);
    }
}

咱们需求删去的线程往往只是为了做某一件事,某一次特别的作业,比方:

static void this_is_a_need_delete_task(void *par){
  			//do_one_time_thing
}

其实这个线程是为了某一件特别作业而创立的,它是需求删去的,咱们并不需求做任何特别处理,由于履行是没有循环的,履行完结今后,RT-Thread 内核会主动把线程删去!!

1.5 挂起和康复线程

线程挂起和康复,在官方有单独的阐明:

RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)
已然官方强烈不主张在程序中运用该接口,咱们这儿就不阐明晰,由于以运用为主,咱们就不去用了。

需求阐明的一点是,这儿和FreeRTOS也是不同的,FreeRTOS用户可以随意用,最典型的便是使一段代码进入临界区挂起其他使命。

1.6 其他线程辅佐函数

其他的线程辅佐函数,除了线程睡觉函数,其他的在一般的运用中都可以不需求。所以咱们简略的过一遍,引证一下官方的介绍。假如后期运用的时分有用到,再来加以具体阐明:

1.6.1 获得当时线程

在程序的运转过程中,相同的一段代码可能会被多个线程履行,在履行的时分可以通过下面的函数接口获得当时履行的线程句柄,把下面的函数加在这段代码中的,哪个线程调用就回来哪个线程句柄:

/*
回来值
thread 	当时运转的线程句柄
RT_NULL 	失利,调度器还未发动
*/
rt_thread_t rt_thread_self(void);

1.6.2 让出处理器资源

rt_err_t rt_thread_yield(void);

调用该函数后,当时线程首先把自己从它所在的安排妥当优先级线程行列中删去,然后把自己挂到这个优先级行列链表的尾部,然后激活调度器进行线程上下文切换(假如当时优先级只要这一个线程,则这个线程持续履行,不进行上下文切换动作)。

1.6.3 线程睡觉(延时函数)

线程睡觉,直白点说,便是延时函数,只不过RTOS中的延时函数,是会开释CPU运用权的,开释CPU运用权,就等于线程睡觉了。

/*
参数:tick/ms 	
线程睡觉的时刻:sleep/delay 的传入参数 tick 以 1 个 OS Tick 为单位 ;
mdelay 的传入参数 ms 以 1ms 为单位;
回来
RT_EOK 	操作成功,一般不需求
*/
rt_err_t rt_thread_sleep(rt_tick_t tick);
rt_err_t rt_thread_delay(rt_tick_t tick);
rt_err_t rt_thread_mdelay(rt_int32_t ms);

1.6.4 线程操控函数

/*
参数阐明:
1、thread 	线程句柄
2、cmd 	指示操控命令
cmd 当时支撑的命令包含:
•RT_THREAD_CTRL_CHANGE_PRIORITY:动态更改线程的优先级;
•RT_THREAD_CTRL_STARTUP:开端运转一个线程,等同于 rt_thread_startup() 函数调用;
•RT_THREAD_CTRL_CLOSE:封闭一个线程,
等同于 rt_thread_delete() 或 rt_thread_detach() 函数调
用。
3、arg 	操控参数
回来值:
RT_EOK 		操控履行正确
-RT_ERROR 	失利
*/
rt_err_t rt_thread_control(rt_thread_t thread, rt_uint8_t cmd, void* arg);

1.6.5 设置和删去闲暇钩子

闲暇钩子函数是闲暇线程的钩子函数(不要和调度器钩子函数搞混了),假如设置了闲暇钩子函数,就可以在体系履行闲暇线程时,主动履行闲暇钩子函数来做一些其他作业,比方体系指示灯。设置 / 删去闲暇钩子的接口如下:

/*
参数:
hook 	设置的钩子函数,在函数中实现一些操作,可是不要有挂起操作
回来值:
RT_EOK 	设置成功
-RT_EFULL 	设置失利
*/
rt_err_t rt_thread_idle_sethook(void (*hook)(void));
rt_err_t rt_thread_idle_delhook(void (*hook)(void));

官方有一段注意阐明如下:

RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)

1.6.6 设置调度器钩子

在整个体系的运转时,体系都处于线程运转、中止触发 – 呼应中止、切换到其他线程,乃至是线程间的切换过程中,或许说体系的上下文切换是体系中最遍及的作业。有时用户可能会想知道在一个时刻发生了什么样的线程切换,可以通过调用下面的函数接口设置一个相应的钩子函数。在体系线程切换时,这个钩子函数将被调用:

/*
参数:
hook 	表明用户界说的钩子函数指针
*/
void rt_scheduler_sethook(void (*hook)(struct rt_thread* from, struct rt_thread* to));
/*
钩子函数 hook() 的声明
参数阐明:
1、from 	表明体系所要切换出的线程操控块指针
2、to 	表明体系所要切换到的线程操控块指针
*/
void hook(struct rt_thread* from, struct rt_thread* to);

注:请细心编写你的钩子函数,稍有不小心将很可能导致整个体系运转不正常(在这个钩子函数中,基本上不允许调用体系 API,更不应该导致当时运转的上下文挂起)。

二、RT-Thread线程创立示例

尽管上面介绍了有一部分的线程操作函数,可是正常需求也就前面几个,记住线程创立,发动,一般的运用就足够了,其他的一些辅佐函数在实践中有许多状况是出了问题今后找 bug 的时分才会想起来。

所以咱们演示起来也很简略,还记得在 RT-Thread记载 第一篇博文中:

RT-Thread记载(一、RT-Thread 版本、RT-Thread Studio开发环境 及 合作CubeMX开发快速上手)

在上面博文的最终一节:3.3 创立一个跑马灯使命 我上传了一段源码,这儿我就不再重复上一边了,咱们直接通过截图阐明的方式讲解下示例:

2.1 静态创立线程示例

RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)

2.1 动态创立线程示例

RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)

三、RT-Thread线程办理简析

通过上面的阐明,咱们其实可以运用 RT-Thread 关于的函数创立线程进行一般的设计了,可是为了加深对RT-Thread的了解,咱们还得聊聊 RT-Thread线程办理。

这一块在官网其实有具体的阐明,官方的链接如下:

RT-Thread官方文档 RT-Thread内核线程办理

RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)

3.1 线程调度的基本特色

我这边按照自己的了解认知记载几个重要的点:

1、RT-Thread 的线程调度器是抢占式的,首要的作业便是从安排妥当线程列表中查找最高优先级线程,确保最高优先级的线程可以被运转,最高优先级的使命一旦安排妥当,总能得到 CPU 的运用权。

调度器开启今后,就不停的在查询列表,一切的线程依据优先级,状况,在列表中排序,调度器总是找到排序“第一位”的线程履行。RTOS的中心便是链表,这个有时刻会单独的介绍。

2、当一个运转着的线程使一个比它优先级高的线程满意运转条件,当时线程的 CPU 运用权就被剥夺了,或许说被让出了,高优先级的线程立刻得到了 CPU 的运用权。

假如是中止服务程序使一个高优先级的线程满意运转条件,中止完结时,被中止的线程挂起,优先级高的线程开端运转。

仍是上面说到的调度器的效果,使得高优先级的可以及时履行。

3、当调度器调度线程切换时,先将当时线程上下文保存起来,当再切回到这个线程时,线程调度器将该线程的上下文信息康复。 RT-Thread 线程具有独立的栈,当进行线程切换时,会将当时线程的上下文存在栈中,当线程要康复运转时,再从栈中读取上下文信息,进行康复。

要了解上面的话,推荐一篇博文: FreeRTOS记载(三、FreeRTOS使命调度原了解析_Systick、PendSV、SVC) 尽管说的是FreeRTOS的,可是都是基于Cortex-M内核的,原理机制相似。

4、每个线程都有时刻片这个参数,但时刻片仅对优先级相同的安排妥当态线程有效。

时刻片只要在优先级相同的线程间会依据用户的设置进行对应的分配。

5、线程中不能堕入死循环操作,有必要要有让出 CPU 运用权的动作,如循环中调用延时函数或许主动挂起。

运用rtos延时函数,是实践运用最常见的一种方式,牢记,delay是需求在while(1){}大括号里边的:

RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)

3.2 线程操控块

在咱们上面介绍线程操作函数的时分,常常说到一个词语,线程操控块,线控操控块结构体,RT-Thread 内核关于线程的办理,都是基于这个结构体进行的。这儿咱们先有个基本的知道,假如真的深入探讨,仍是要说到RTOS的链表,需求单独的开篇博文阐明。

咱们现在要了解的是,内核关于线程的办理是通过这个线程操控块结构体,里边包含 RT-Thread线程一切的“特点”,对这些特点的检查,修改就可以对实现对这个线程的办理操控。

咱们来看看操控块结构体(不是直接仿制官网的哦!):

/**
 * Thread structure
 */
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 标志位*/
#ifdef RT_USING_MODULE
    void       *module_id;                /**< id of application module */
#endif
    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 线程状况 */
#ifdef RT_USING_SMP           		 /*多核相关支撑,咱们这儿就一个M3内核*/
    rt_uint8_t  bind_cpu;                /**< thread is bind to cpu */
    rt_uint8_t  oncpu;                   /**< process on cpu` */
    rt_uint16_t scheduler_lock_nest;        /**< scheduler lock count */
    rt_uint16_t cpus_lock_nest;             /**< cpus lock count */
    rt_uint16_t critical_lock_nest;         /**< critical lock count */
#endif /*RT_USING_SMP*/
    /* 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;
#if defined(RT_USING_EVENT)                /*运用作业集*/
    /* thread event */
    rt_uint32_t event_set;
    rt_uint8_t  event_info;
#endif
#if defined(RT_USING_SIGNALS)							
    rt_sigset_t     sig_pending;        /**< the pending signals */
    rt_sigset_t     sig_mask;           /**< the mask bits of signal */
#ifndef RT_USING_SMP					/*多核相关支撑,咱们这儿就一个M3内核*/
    void            *sig_ret;           /**< the return stack pointer from signal */
#endif
    rt_sighandler_t *sig_vectors;       /**< vectors of signal handler */
    void            *si_list;           /**< the signal infor list */
#endif
    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 内置线程定时器*/
     /**< cleanup function when thread exit 
     线程退出清除函数
     cleanup 函数指针指向的函数,会在线程退出的时分,被idle 线程回调一次,
     履行用户的整理现场作业。
     */
    void (*cleanup)(struct rt_thread *tid);  
    /* light weight process if present */
#ifdef RT_USING_LWP
    void        *lwp;
#endif
    rt_ubase_t user_data;      /**< private user data beyond this thread  用户数据*/
};
typedef struct rt_thread *rt_thread_t;

3.3 线程状况

线程的状况咱们借用官方的几张图,加以阐明:

RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)

来看看 RT-Thread 的使命状况:

RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)
在上图中除了今天咱们介绍的线程操作函数,还有一些函数还没有介绍过,比方rt_sem_take(),rt_mutex_take(),rt_mb_recv() ,这是咱们后期会介绍到的关于线程间通信的一些信号量,互斥量相关的函数。

作为对比,再来看看FreeRTOS 的使命状况:

RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)

3.4 体系线程

在 RT-Thread 内核中的体系线程有闲暇线程和主线程。

闲暇线程 IDLE线程:

闲暇线程是体系创立的最低优先级的线程,线程状况永远为安排妥当态。当体系中无其他安排妥当线程存在时,调度器将调度到闲暇线程,它通常是一个死循环,且永远不能被挂起。这点其实一切RTOS都是相同的。

可是,闲暇线程在 RT-Thread 也有着它的特别用途:

若某线程运转结束,体系将主动删去线程:主动履行 rt_thread_exit() 函数,先将该线程从体系安排妥当行列中删去,再将该线程的状况更改为封闭状况,不再参加体系调度,然后挂入 rt_thread_defunct 僵尸行列(资源未收回、处于封闭状况的线程行列)中,最终闲暇线程会收回被删去线程的资源。

闲暇线程也供给了接口来运转用户设置的钩子函数,在闲暇线程运转时会调用该钩子函数,适合钩入功耗办理、看门狗喂狗等作业。

主线程:

在咱们上一篇博文中介绍 RT-Thread 发动流程的时分,说到了体系发动会创立main线程:

RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)
FreeRTOS只要闲暇线程,并不会创立主线程,所以在FreeRTOS中,一般在main() 之前开启调度,永远不会履行到main()。

结语

本文的首要意图是知道 RT-Thread 线程操作函数,一起简略的阐明晰一下 RT-Thread 线程办理的一些关键,阐明晰一下 RT-Thread 与 FreeRTOS 在线程操作某些当地的不同,此外还加了一些博主认为的细节的问题, 期望懂的小伙伴可以多多指教,不明白的小伙伴看完仍是不明白的可以留言。讲得不好的当地还期望可以指出,博主必定加以批改。

总的来说,本文内容仍是比较简略的,小伙伴们可以开动起来,线程创立跑起来玩玩。优先级,使命调度,线程死循环什么的状况都可以试试。更能加加深线程调度的了解。

下一篇 RT-Thread 记载,我会讲一讲 RT-Thread 时钟办理的内容,体系时钟,软件定时器相关。

谢谢!